DeepLearning | 图卷积神经网络(GCN)解析(论文、算法、代码)

本篇博客主要讲述三种图卷积网络(Graph Convolutional Network, GCN)的算法原理及python实现,全文阅读时间约10分钟。

目录

  • 一、图卷积网络原理
    • 1.0 预备知识
    • 1.1 第一代图卷积网络
    • 1.2 第二代图卷积网络
    • 1.3 第三代图卷积网络
    • 1.4 图卷积网络的优缺点
  • 二、图卷积网络python实现
  • 三、图卷积网络论文、代码、数据集资源下载!

一、图卷积网络原理

图模型早期的研究被称为图信号处理(Graph Signal Processing, GSP),在12年深度学习爆发以后,学术圈开始研究图模型与神经网络的结合。主要经过三代模型的发展后,相对成熟的图卷积网络开始发展起来,本文主要介绍的也是这三代图卷积模型的原理和变化。

1.0 预备知识

在介绍图卷积之前,需要先普及一些基本的理论知识:

  1. 图结构数据DeepLearning | 图卷积神经网络(GCN)解析(论文、算法、代码)_第1张图片
    一张图由节点和边组成,节点代表成员或者对象,而边表示节点间的关系,我们往往使用 A ∈ R n × n A \in \mathbb{R}^{n \times n} ARn×n表示一张图的结构特性, A A A称为图的邻接矩阵,其中 n n n表示节点数量, a i , j a_{i,j} ai,j的数值则表示节点 i i i和节点 j j j之间的关系。
  2. 拉普拉斯图矩阵
    基于图的邻接矩阵,我们可以方便的定义拉普拉斯图矩阵
    L = D − A L=D-A L=DA
    上式中 A A A是邻接矩阵, D D D称为度矩阵,是一个对角矩阵, d i i = ∑ j = 1 n a i j d_{ii}=\sum^{n}_{j=1}a_{ij} dii=j=1naij描述了每一个样本近邻的总和信息。
  3. 卷积定理:函数卷积的傅利叶变换是函数傅利叶变换的乘积。
  4. 傅立叶变换在图上的推广
    拉普拉斯图矩阵 L L L的特征向量 U U U可以作为傅立叶变换的基,
    L = U d i a g ( λ 1 , . . . , λ n ) U − 1 L=Udiag(\lambda_{1},...,\lambda_{n})U^{-1} L=Udiag(λ1,...,λn)U1
    这里的特征值则可以理解为不同频率分量的系数,越小的特征值表明对应基的分量信息越少。
  5. 图卷积的分类
    图卷积主要可以分为基于谱域(谱分解/特征根分解)的卷积以及基于空域的卷积,今天介绍的三代卷积网络都是基于谱域的卷积,但其在空域上也有很好的解释性。关于空域图卷积会在以后的文章中专门介绍

1.1 第一代图卷积网络

第一代图卷积定义为

y o u t p u t = σ { U d i a g ( θ 1 , . . . , θ n ) U T x } y_{output} = \sigma\{Udiag(\theta_{1},...,\theta_{n})U^{T}x\} youtput=σ{Udiag(θ1,...,θn)UTx}

这里的 σ \sigma σ表示激活函数, θ \theta θ表示可训练的网络参数,使用反向传播进行训练, x x x是输入的特征矩阵。
根据预备知识的第4点, 上式可以解释为, U T U^{T} UT x x x变换到频域当中,在频域中与训练得到的 θ \theta θ加权结合进行特征提取,继而又通过 U U U反变换到原来的空域当中。
第一代图卷积模型由LeCun组在14年提出,论文可见 Spectral networks and locally connected networks on graphs. In International Conference on Learning Representations。该模型作为早期的图卷积网络,缺点在于

  1. 每一次前向传播都需要计算 U U U, θ \theta θ, U T UT UT 的乘积,计算代价高
  2. 在空域内的解释性不好,没法很好利用图的近邻性质
  3. 卷积核需要 n n n个参数

1.2 第二代图卷积网络

第二代图卷积模型定义为

y o u t p u t = σ { U d i a g ( ∑ j = 0 K α j λ 1 j , . . . , ∑ j = 0 K α j λ n j ) U T x } y_{output} = \sigma\{Udiag(\sum^{K}_{j=0}\alpha_{j}\lambda^{j}_{1},...,\sum^{K}_{j=0}\alpha_{j}\lambda^{j}_{n})U^{T}x\} youtput=σ{Udiag(j=0Kαjλ1j,...,j=0Kαjλnj)UTx}
上式中, λ i \lambda_{i} λi L L L的第 i i i个特征值, α \alpha α是可训练的网络参数,对角矩阵又可以记为
d i a g ( ∑ j = 0 K α j λ 1 j , . . . , ∑ j = 0 K α j λ n j ) = ∑ j = 0 K α j Λ j diag(\sum^{K}_{j=0}\alpha_{j}\lambda^{j}_{1},...,\sum^{K}_{j=0}\alpha_{j}\lambda^{j}_{n})=\sum^{K}_{j=0}\alpha_{j}\Lambda^{j} diag(j=0Kαjλ1j,...,j=0Kαjλnj)=j=0KαjΛj
进而可以导出
U ∑ j = 0 K α j Λ j U T = ∑ j = 0 K α j U Λ j U T = ∑ j = 0 K α j L j U\sum^{K}_{j=0}\alpha_{j}\Lambda^{j}U^{T}=\sum^{K}_{j=0}\alpha_{j}U\Lambda^{j}U^{T}=\sum^{K}_{j=0}\alpha_{j}L^{j} Uj=0KαjΛjUT=j=0KαjUΛjUT=j=0KαjLj
于是有
y o u t p u t = σ { ∑ j = 0 K α j L j x } y_{output} = \sigma\{\sum^{K}_{j=0}\alpha_{j}L^{j}x\} youtput=σ{j=0KαjLjx}
可以看出第二代图卷积网络使用的卷积核其实是 K K K个拉普拉斯图矩阵的加和。那么这里的 K K K是什么含义呢?如何定义的 K K K个拉普拉斯矩阵。其实很简单,第 k k k个图矩阵记录的是经过 k k k跳以后可以找到的近邻,如下图所示
DeepLearning | 图卷积神经网络(GCN)解析(论文、算法、代码)_第2张图片
通过定义不同的近邻,来提取更多的信息。
第二代图卷积网络在论文 Convolutional Neural Networks on Graphs with Fast Localized Spectral Filtering中被提出,相比与第一代图卷积模型,其优点在于

  1. 卷积核只有 K K K个参数,一般 K K K远小于 N N N
  2. 不需要做特征根分解,直接用 L L L
  3. 空间域内的解释性较好

1.3 第三代图卷积网络

第三代图卷积模型,也是当前最流行的版本,由第一代卷积简化推导而来。第一代卷积模型可以表示为

g θ ⋆ x = U g θ U T x g_{\theta} \star x = Ug_{\theta}U^{T}x gθx=UgθUTx
,其中卷积核 g θ = d i a g ( θ ) g_{\theta} = diag(\theta) gθ=diag(θ) 是待训练的参数,特征向量 U U U由标准化的拉普拉斯矩阵特征根分解得到
L = I − D − 0.5 A D − 0.5 = U Λ U T L=I-D^{-0.5}AD^{-0.5}=U \Lambda U^{T} L=ID0.5AD0.5=UΛUT
,这里的卷积核 g θ g_{\theta} gθ可以理解为特征值 Λ \Lambda Λ的函数,记为 g θ ( Λ ) g_{\theta}(\Lambda) gθ(Λ).
使用切比雪夫多项式 K K K阶近似 g θ ( Λ ) g_{\theta}(\Lambda) gθ(Λ),
g θ ′ ( Λ ) ≈ ∑ k = 0 K θ k ′ T k ( Λ ~ ) g_{{\theta}'}(\Lambda) \approx \sum^{K}_{k=0}{\theta}'_{k}T_{k}(\tilde{\Lambda}) gθ(Λ)k=0KθkTk(Λ~)
,其中 Λ ~ = 2 λ m a x Λ − I \tilde{\Lambda}=\frac{2}{\lambda_{max}}\Lambda-I Λ~=λmax2ΛI θ ′ {\theta}' θ是需要学习的系数, T k T_{k} Tk的定义是递归的 T k ( x ) = a x T k − 1 ( x ) − T k − 2 ( x ) T_{k}(x)=axT_{k-1}(x)-T_{k-2}(x) Tk(x)=axTk1(x)Tk2(x), T 0 ( x ) = 1 T_{0}(x)=1 T0(x)=1 以及 T 1 ( x ) = x T_{1}(x)=x T1(x)=x 。将估计式带入一代卷积模型中,可以得到
g θ ′ ⋆ x ≈ ∑ k = 0 K θ k ′ T k ( L ~ ) x g_{{\theta}}' \star x \approx \sum^{K}_{k=0}{\theta}'_{k}T_{k}(\tilde{L})x gθxk=0KθkTk(L~)x
,其中 L ~ = 2 λ m a x L − I \tilde{L}=\frac{2}{\lambda_{max}}L-I L~=λmax2LI
进一步近似,约束 λ m a x = 2 \lambda_{max}=2 λmax=2, 取 K = 1 K=1 K=1, 则有
g θ ′ ⋆ x ≈ θ 0 ′ x − θ 1 ′ D − 0.5 A D − 0.5 x g_{{\theta}}' \star x \approx {\theta}'_{0}x- {\theta}'_{1}D^{-0.5}AD^{-0.5}x gθxθ0xθ1D0.5AD0.5x
,因为 θ ′ {\theta}' θ是需要学习的系数,可以将其约束为 θ = θ 0 ′ = − θ 1 ′ \theta = {\theta}'_{0} = -{\theta}'_{1} θ=θ0=θ1, 则有
g θ ′ ⋆ x ≈ θ ( I + D − 0.5 A D − 0.5 ) x g_{{\theta}}' \star x \approx {\theta}(I+D^{-0.5}AD^{-0.5})x gθxθ(I+D0.5AD0.5)x
,再使用重参数化技巧
I + D − 0.5 A D − 0.5 → D ~ − 0.5 A ~ D ~ − 0.5 I+D^{-0.5}AD^{-0.5} \rightarrow \tilde{D}^{-0.5}\tilde{A}\tilde{D}^{-0.5} I+D0.5AD0.5D~0.5A~D~0.5
,其中 A ~ = A + I \tilde{A}=A+I A~=A+I d i i ~ = ∑ j = 1 n a ~ i j \tilde{d_{ii}}=\sum^{n}_{j=1}\tilde{a}_{ij} dii~=j=1na~ij
据上,第三代图卷积模型为定义为
y o u t p u t = D ~ − 0.5 A ~ D ~ − 0.5 X θ y_{output}=\tilde{D}^{-0.5}\tilde{A}\tilde{D}^{-0.5}X\theta youtput=D~0.5A~D~0.5Xθ
。其模型可以被理解为是对一代模型做了一阶的切比雪夫近似,为每一个节点提取一阶近邻的信息。
第三代图卷积模型在论文 Semi-Supervised Classification with Graph Convolutional Networks中被提出,由于其较好空域解释性和精简的模型结构,受到广泛应用。

1.4 图卷积网络的优缺点

特点:

  1. 训练的输入为 X X X A A A,以整张数据图为输入,而不是以batch训练
  2. 训练阶段会用到无标签的测试数据,因而是直推式半监督学习

优点:

  1. 显式的利用节点间的关系进行特征的提取
  2. 实现相对简单、计算并不复杂

缺点:

  1. 图卷积网络一般只有1-4层,无法深层堆叠,会出现梯度消失的问题
  2. . 一般的图卷积网络以整张图作为输入,所以在普通机器上无法训练大规模的图数据

二、图卷积网络python实现

下面是图卷积网络的核心代码

#卷积层代码
import  torch
from    torch import nn
from    torch.nn import functional as F
from    utils import sparse_dropout, dot

class GraphConvolution(nn.Module):
    def __init__(self, input_dim, output_dim, num_features_nonzero,
                 dropout=0.,
                 is_sparse_inputs=False,
                 bias=False,
                 activation = F.relu,
                 featureless=False):
        super(GraphConvolution, self).__init__()


        self.dropout = dropout
        self.bias = bias
        self.activation = activation
        self.is_sparse_inputs = is_sparse_inputs
        self.featureless = featureless
        self.num_features_nonzero = num_features_nonzero

        self.weight = nn.Parameter(torch.randn(input_dim, output_dim))
        self.bias = None
        if bias:
            self.bias = nn.Parameter(torch.zeros(output_dim))


    def forward(self, inputs):
        # print('inputs:', inputs)
        x, support = inputs

        if self.training and self.is_sparse_inputs:
            x = sparse_dropout(x, self.dropout, self.num_features_nonzero)
        elif self.training:
            x = F.dropout(x, self.dropout)

        # convolve
        if not self.featureless: # if it has features x
            if self.is_sparse_inputs:
                xw = torch.sparse.mm(x, self.weight)
            else:
                xw = torch.mm(x, self.weight)
        else:
            xw = self.weight

        out = torch.sparse.mm(support, xw)

        if self.bias is not None:
            out += self.bias

        return self.activation(out), support
#模型代码
import  torch
from    torch import nn
from    torch.nn import functional as F
from    layer import GraphConvolution

from    config import args

class GCN(nn.Module):
    def __init__(self, input_dim, output_dim, num_features_nonzero):
        super(GCN, self).__init__()

        self.input_dim = input_dim # 1433
        self.output_dim = output_dim

        print('input dim:', input_dim)
        print('output dim:', output_dim)
        print('num_features_nonzero:', num_features_nonzero)


        self.layers = nn.Sequential(GraphConvolution(self.input_dim, args.hidden, num_features_nonzero,
                                                     activation=F.relu,
                                                     dropout=args.dropout,
                                                     is_sparse_inputs=True),

                                    GraphConvolution(args.hidden, output_dim, num_features_nonzero,
                                                     activation=F.relu,
                                                     dropout=args.dropout,
                                                     is_sparse_inputs=False),

                                    )

    def forward(self, inputs):
        x, support = inputs

        x = self.layers((x, support))

        return x

    def l2_loss(self):

        layer = self.layers.children()
        layer = next(iter(layer))

        loss = None

        for p in layer.parameters():
            if loss is None:
                loss = p.pow(2).sum()
            else:
                loss += p.pow(2).sum()

        return loss

三、图卷积网络论文、代码、数据集资源下载!

DeepLearning | 图卷积神经网络(GCN)解析(论文、算法、代码)_第3张图片

有问题可以私信博主,点赞关注的一般都会回复,一起努力,谢谢支持。
微信搜索“老和山算法指南”获取下载链接与技术交流群

你可能感兴趣的:(Deep,Learning,卷积网络及编程框架)