当前位置:   article > 正文

简单讲解图卷积网络(Graph Convolutional Layer)附代码

图卷积

卷积操作的原理

图卷积操作(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)1jN(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)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

通过矩阵乘法实现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()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

参考资料:

https://arxiv.org/pdf/1801.07455.pdf

https://github.com/ugrkilc/ST-GCN/tree/main

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号