图卷积操作的原理
图卷积操作(Graph Convolution Operation)是一种针对图结构数据的特殊卷积操作。
在卷积神经网络(CNN)中,卷积操作常在网格数据(如图片)上进行。相比之下,图卷积操作是为了处理不规则的图结构数据而设计的。
在图卷积操作中,节点的特征表示是根据邻居节点的特征和自身的特征来更新的。这个过程可以视为在图上的信息传播和聚合操作。
具体来说,一个节点的新特征是其邻居节点特征的加权平均,权重通常由节点之间的连接和它们的度(即连接数)决定。
给定一个节点 ii,其嵌入
h
i
h_i
hi 表达为:
h
i
=
1
deg
(
i
)
∑
j
∈
N
(
i
)
x
j
W
T
h_i=\frac{1}{\deg(i)}\sum_{j\in N(i)}x_jW^T
hi=deg(i)1j∈N(i)∑xjWT
其中
x
j
x_j
xj 是邻居节点的输入特征,
W
T
W^T
WT 是转置的GCN权重矩阵,
deg
(
⋅
)
\deg(\cdot)
deg(⋅)是某点的度数,
N
(
⋅
)
N(\cdot)
N(⋅)返回该点的邻居节点。
代码
调用PyTorch Geometric API: GCNConv
为了在Python中实现GCN,我们可以使用PyTorch和PyTorch Geometric:
import torch
from torch_geometric.nn import GCNConv
# 定义图卷积层
class GraphConvolutionLayer(torch.nn.Module):
def __init__(self, in_features, out_features):
super(GraphConvolutionLayer, self).__init__()
self.conv = GCNConv(in_features, out_features)
def forward(self, x, edge_index):
x = self.conv(x, edge_index)
return x
# 示例:输入特征和边索引
in_features = 2
out_features = 64
edge_index = torch.tensor([[0, 1], [1, 2], [2, 0]], dtype=torch.long).t().contiguous()
x = torch.rand((3, in_features)) # 3个节点,每个节点2个特征
# 创建图卷积层实例
gcn_layer = GraphConvolutionLayer(in_features, out_features)
output = gcn_layer(x, edge_index)
print(output)
通过矩阵乘法实现GCN
不带归一化的矩阵乘法形式可以表示为:
H
=
A
~
X
W
T
H=\tilde A XW^T
H=A~XWT
在这里,
H
H
H 代表输出,即这张图的节点特征新表示,
A
~
=
A
+
I
\tilde A= A + I
A~=A+I 是邻接矩阵加上自环,
X
X
X是节点特征矩阵,
W
T
W^T
WT是转置的权重矩阵。
接下来是GCN的另一种实践,比PyTorch Geometric更灵活,特别是在处理基于骨架的动作识别这种灵活的图结构建模问题:
import torch
import torch.nn as nn
class SpatialGraphConvolution(nn.Module):
def __init__(self, in_channels, out_channels, s_kernel_size=1):
super().__init__()
self.s_kernel_size = s_kernel_size # 空间核大小
# 由于下面的卷积层是一个1x1卷积,因此它实际上是对特征进行线性变换(即对应上文式子里的W)
self.conv = nn.Conv2d(in_channels=in_channels,
out_channels=out_channels * s_kernel_size,
kernel_size=1)
def forward(self, x, A):
"""
A的维度维度:空间核大小, 图的节点数, 图的节点数
A应当之前就被正则化完毕
"""
# 应用1x1卷积进行特征变换
x = self.conv(x)
n, kc, t, v = x.size() # 获取输出特征的维度:小批次数据样本数、节点特征通道、时间步、顶点数
# 重塑特征以匹配空间核的维度
x = x.view(n, self.s_kernel_size, kc // self.s_kernel_size, t, v)
# 使用爱因斯坦求和约定来进行张量运算。这里执行的是图卷积操作。
# 'nkctv,kvw->nctw'表示对n(批大小)、k(空间核)、c(通道)、t(时间步)、v(顶点数)、w(邻接矩阵维度)的张量进行操作
x = torch.einsum('nkctv,kvw->nctw', (x, A))
# 返回连续的内存布局的张量,有助于后续操作的性能优化
return x.contiguous()
参考资料:
https://arxiv.org/pdf/1801.07455.pdf
https://github.com/ugrkilc/ST-GCN/tree/main