深入浅出了解GCN原理(公式+代码)

  由于课题研究需要,这星期看了几篇GCN相关的文章和书籍,并对其进行了代码复现,现将最近学习的内容做一个梳理与总结,用于日后复习巩固。由于能力有限,文章中有错误或者不当之处,还望各位读者多多指出。之后对GCN应用方面相关的论文阅读笔记,也会及时文末跟新。(本文作为笔者的学习笔记,如有错误,希望各位读者批评指正)- - 更新时间:2020年11月1日

[学习笔记(1)]深入浅出了解GCN原理(公式+代码)
[学习笔记(2)]深入浅出了解GNN的几种变体

目录

  • 引言
    • GCN发展
      • 1、基于损失函数的考虑
      • 2、从谱图卷积开始
      • 3、切比雪夫卷积核
      • 4、GCN图卷积网络
    • 代码设计与实现
    • 总结
        • 1、谱域卷积
        • 2、空域卷积
    • 参考文献

引言

   相信大多数读者在了解GCN(Graph Convolutional Networks)之前,对CNN(Convolutional Neural Network)都是非常熟悉的,我们知道,在连续信号中的卷积是表征函数f与g经过翻转和平移的重叠部分函数值乘积对重叠长度的积分,如下公式(1)。
∫ − ∞ + ∞ f ( τ ) g ( x − τ ) d τ ( 1 ) \quad\qquad\int_{-\infty}^{+\infty} f(\tau)g(x-\tau)d\tau \qquad\qquad\qquad (1) +f(τ)g(xτ)dτ(1)

深入浅出了解GCN原理(公式+代码)_第1张图片
对于离散信号而言,离散卷积的本质也是平移叠加之后的加权和,所以在CNN中图像上的卷积,本质上是利用参数共享的过滤器(kernel),通过计算中心像素点以及相邻像素点的加权和来构建成Feature Map,实现空间特征的提取,而加权的系数就是卷积核的权重,这其实在很多传统图像的算法中也有体现,就比如高斯平滑算子,拉普拉斯算子,边缘检测算子(提取边缘特征),包括SIFI(Scale-invariant feature transform)特征点提取,也是一系列的卷积和后处理操作。这些在图像上通过卷积核特征提取的操作,之所以有效很大一部分原因是因为图像本身是结构化数据,它是多个像素点排列整齐的矩阵,而CNN具有平移不变性(即图像如果经过平移,得到的特征图也会相应平移)。然而在真实世界中,还含有很多非欧几里得距离的数据,比如社交网络关系、交通连通图、脑网络连接等等,对于这类具有抽象意义的拓扑图关系的数据,它们是无法保证平移不变性的,这就需要我们有类似cnn的方法,来提取和挖掘有效的空间关系进行建模学习。广义来说,对于任何数据,都可以建立一定的拓扑关联,来进一步挖掘数据内部之间的关联性,所以GCN有很大的应用空间。

GCN发展

  GCN真正开始运用和发展是从这篇于2016年提出,2017年发表的文章开始:SEMI-SUPERVISED CLASSIFICATION WITHGRAPH CONVOLUTIONAL NETWORKS ,详细文章内容,读者可以自行阅读原文。

1、基于损失函数的考虑

  在2016年以前,就有前人尝试利用正则化约束的方法,通过在损失函数中引入图的拉普拉斯正则项,来对节点分类任务进行半监督学习,如下公式(2).
L = L 0 + λ L r e g w i t h L r e g = ∑ i , j A i j ∣ ∣ f ( X i ) − f ( X j ) ∣ ∣ 2 = f ( X ) T △ f ( X ) . ( 2 ) \mathcal{L}=\mathcal{L}_0+\lambda\mathcal{L_{reg}}\qquad with\quad \mathcal{L_{reg}}=\sum_{i,j}A_{ij}||f(X_i)-f(X_j)||^{2}=f(X)^T\vartriangle f(X). \qquad(2) L=L0+λLregwithLreg=i,jAijf(Xi)f(Xj)2=f(X)Tf(X).(2)
其中 L 0 \mathcal{L}_0 L0为监督损失(与label定义的损失), L r e g \mathcal{L}_{reg} Lreg为约束项, f ( . ) f(.) f(.)可以是神经网络可微函数, X X X是节点特征向量 X i X_i Xi的矩阵, △ = D − A \vartriangle=D-A =DA是无向图 G = ( V , E ) G=(V,E) G=(V,E)的非标准化拉普拉斯矩阵。这里说明下,拉普拉斯矩阵是用来研究图结构性质的核心对象,其定义为 L = D − A L=D-A L=DA,其中D是一个对角矩阵, A A A是邻接矩阵, D i i = ∑ j A i j D_{ii}=\sum_jA_{ij} Dii=jAij 表示 i i i节点的度。拉普拉斯矩阵还有一种正则化的形式(symmetric normalized laplacian) L s y m = D − 1 2 L D − 1 2 = I N − D − 1 2 A D − 1 2 ( 3 ) \qquad L_{sym}=D^{-\frac{1}{2}}LD^{-\frac{1}{2}}=I_N-D^{-\frac{1}{2}}AD^{-\frac{1}{2}} \qquad\qquad\qquad(3) Lsym=D21LD21=IND21AD21(3),其元素级别的定义如下:
L s y m [ j , j ] { 1 i f   i = j − 1 d e g ( v i ) d e g ( v j ) i f   e i j ∈ E 0 o t h e r w i s e ( 4 ) \qquad L_{sym}[j,j]\left\{ \begin{aligned} 1\qquad\quad& &{ if\ i=j\quad}\\ \frac{-1}{\sqrt{deg(v_i)deg(v_j)}} & &{if\ e_{ij} \in E} \\ 0 \qquad\quad& & {otherwise } \end{aligned} \right.{\quad \qquad \qquad \qquad (4)} Lsym[j,j]1deg(vi)deg(vj) 10if i=jif eijEotherwise(4)
拉普拉斯矩阵的定义源自于拉普拉斯算子,拉普拉斯算子它是 n n n维欧式空间种的二阶微分算子,在二维空间的图像中表示就是我们熟悉的边缘检测算子(两者之间的关系这里不多展开,感兴趣的读者可以看看这篇文章 拉普拉斯算子与拉普拉斯矩阵的关系
深入浅出了解GCN原理(公式+代码)_第2张图片

图1 拉普拉斯矩阵计算(图片来源维基百科)

回到公式(2),从表达式上我们可以清晰的看到,正则部分相当于衡量相邻节点之间的差异性,因为 A i , j A_{i,j} Ai,j是邻接矩阵上的元素,两个节点处于邻接关系时为1,其他为0.这样的正则项,使得拓扑图上相邻节点尽可能相似,物以类聚,这是很自然的想法。从信号的角度上来看,减少该正则项,就是期望经过模型之后的图信号更加平滑。从频域上来看,是对图信号做了低通滤波的处理,但是这样的假设可能会限制模型的表达能力,因为图上边不只包含相似度的信息,还可能包含其他相关信息。

2、从谱图卷积开始

  最初标准的第一代GCN出自Spectral Networks and Locally Connected Networks on Graphs,其对于图上的卷积有如下定义:
g θ ∗ x = U g θ U T x ( 5 ) \qquad \qquad g_{\theta}*x=Ug_{\theta}U^Tx \qquad\qquad\qquad\quad(5) gθx=UgθUTx(5)
这里 U U U是图的标准化拉普拉斯(Laplacian)矩阵 L s y m L_{sym} Lsym(公式3)的特征向量矩阵,因此有 L s y m = U Λ U T L_{sym}=U\Lambda U^T Lsym=UΛUT, x x x是数据中提取出来的特征,也就是输入,定义 g θ = d i a g ( h ^ ( λ l ) ) g_{\theta}=diag(\hat{h}(\lambda_l)) gθ=diag(h^(λl))是特征值的响应函数,进一步为了类比CNN中的共享卷积核的设计,在神经网络中将其设置为可训练参数的卷积核,如 d i a g ( θ l ) diag(\theta_l) diag(θl)。而 U T x U^Tx UTx的本质是将 x x x映射到傅里叶空间, U U U是傅里叶空间的一组正交基。对于如何将傅里叶分解推广到图卷积,可以参考这篇The Emerging Field of Signal Processing on Graphs: Extending High-Dimensional Data Analysis to Networks and Other Irregular Domains ,太过复杂的推导和证明这里不过多解释。
对于第一代版本的GCN主要有以下几点弊端:

  1. 每一层的计算涉及 U d i a g ( h ^ ( θ l ) ) U T Udiag(\hat{h}(\theta_l))U^T Udiag(h^(θl))UT三个矩阵乘法,复杂度为 O ( N 2 ) O(N^2) O(N2),代价较高
  2. 卷积核具有n个参数,n是L的特征值个数。
  3. 计算Hermitian Matrix特征值的代价非常昂贵

过高的计算复杂度和过大的计算参数,在实际运用中都是不允许的,于是基于以上两点的考虑,就产生了第二代版本的GCN,详细内容和公式推导可参考论文:Convolutional Neural Networks on Graphs with Fast Localized Spectral Filtering
,文中作者将卷积核巧妙地设计成了如下公式:
g θ ( Λ ) = ∑ k = 0 K − 1 θ k Λ k = ( ∑ j = 0 k − 1 θ j λ 1 j ⋱ ∑ j = 0 k − 1 θ j λ n j ) ( 6 ) g_{\theta}(\Lambda)=\sum_{k=0}^{K-1}\theta_k\Lambda^k= \begin{pmatrix} \sum_{j=0}^{k-1}\theta_j\lambda_1^j &&\\ &\ddots&\\ &&\sum_{j=0}^{k-1}\theta_j\lambda_n^j \end{pmatrix}\qquad\qquad(6) gθ(Λ)=k=0K1θkΛk=j=0k1θjλ1jj=0k1θjλnj(6)
经过这样的设计,原本需要训练n个参数,缩小到了K个,同时将公式(6)带入公式(5)得到公式(7),从公式(7)中不难发现,此时我们不在需要在求矩阵的特征向量了,可直接使用拉普拉斯矩阵进行运算。第二代版本的GCN解决了第一代的2、3两个问题的弊端,但是矩阵乘法的复杂度依旧是 O ( N 2 ) O(N^2) O(N2)
g θ ∗ x = ∑ j = 0 k − 1 θ j L j x ( 7 ) \qquad\qquad g_{\theta}*x=\sum_{j=0}^{k-1}\theta_jL^jx \qquad \qquad \qquad\quad(7) gθx=j=0k1θjLjx(7)

3、切比雪夫卷积核

  计算多个大型稠密矩阵的乘法复杂度是很高的,这运用中在实际场景是不被允许的,尤其是计算像社交关系网络这种超大拓扑图的数据,显然是无法实现的,为解决这个问题,我们采用切比雪夫多项式(Chebyshev polynomials)来近似 g θ ( Λ ) g_{\theta}(\Lambda) gθ(Λ),具体内容的讨论读者可阅读知乎:chevyshev多项式作为卷积核 或Hammond等人对这一问题深入的讨论Wavelets on graphs via spectral graph theory,我们直接给出近似公式:
g θ ’ ( Λ ) ≈ ∑ k = 0 K θ k ′ T k ( Λ ~ ) ( 8 ) \qquad \qquad g_{\theta^’}(\Lambda)\approx\sum_{k=0}^{K}\theta_k^{'}T_k(\tilde{\Lambda})\qquad\qquad\qquad(8) gθ(Λ)k=0KθkTk(Λ~)(8)
这里 Λ ~ = 2 λ m a x Λ − I N \tilde{\Lambda}=\frac{2}{\lambda_{max}}\Lambda-I_N Λ~=λmax2ΛIN是对 Λ \Lambda Λ的一种缩放, λ m a x \lambda_{max} λmax L L L的最大特征值, θ ′ ∈ R k \theta^{'}\in\mathbb{R}^k θRk是切比雪夫系数向量,切比雪夫的递归式定义为 T k ( x ) = 2 x T k − 1 ( x ) − T k − 2 ( x ) T_k(x)=2xT_{k-1}(x)-T_{k-2}(x) Tk(x)=2xTk1(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,由此就可以的得到一个计算近似特征值的方法,将公式(8)带入之前的卷积定义公式(5),我们有: g θ ∗ x = ∑ k = 0 K θ k ′ T k ( L ~ ) x ( 9 ) \qquad\qquad g_{\theta}*x=\sum_{k=0}^{K}\theta_k^{'}T_k(\tilde{L})x \qquad\qquad\qquad(9) gθx=k=0KθkTk(L~)x(9)这里 L ~ = 2 λ m a x L − I N \tilde{L}=\frac{2}{\lambda_{max}}L-I_N L~=λmax2LIN,这个表达式是局部化的,它是拉普拉斯式的一个K阶多项式,也就是说,它只依赖于距离中心节点(K阶邻域),公式(9)的复杂度是 O ( ∣ ε ∣ ) O(|\varepsilon|) O(ε),即计算复杂度取决于图的边的数量。

4、GCN图卷积网络

  为了简化问题,同时减轻由于局部节点度过大而导致过拟合的问题,我们设置K=1,即只考虑中心节点的一阶邻近,将 T 0 ( x ) = 1 T_0(x)=1 T0(x)=1, T 1 ( L ~ ) = L ~ T_1(\tilde{L})=\tilde{L} T1(L~)=L~带入公式(9),进一步,我们将尺度变换的 λ m a x \lambda_{max} λmax固定为2。于是我们就得到卷积新的近似表达:

g θ ∗ x ≈ θ 0 ′ x + θ 1 ′ ( L − I N ) x = θ ( I N + D − 1 2 A D − 1 2 ) x . ( 10 ) g_{\theta}*x\approx\theta_0^{'}x+\theta_1^{'}(L-I_N)x=\theta(I_N+D^{-\frac{1}{2}}AD^{-\frac{1}{2}})x.\qquad\qquad(10) gθxθ0x+θ1(LIN)x=θ(IN+D21AD21)x.(10)
盖尔圆盘定理我们知道,公式(10)中 I N + D − 1 / 2 A D − 1 / 2 I_N+D^{-1/2}AD^{-1/2} IN+D1/2AD1/2的特征值范围为[0,2],重复的进行卷积操作,可能会导致数值不稳定,在神经网络中多层的图卷积之后,容易导致梯度消失或者梯度爆炸。为了解决这个问题,我们将其进一步标准化:
I N + D − 1 2 A D − 1 2 → D ~ − 1 2 A ~ D ~ − 1 2 ( 11 ) \qquad \qquad \quad I_N+D^{-\frac{1}{2}}AD^{-\frac{1}{2}}\to \tilde{D}^{-\frac{1}{2}}\tilde{A}\tilde{D}^{-\frac{1}{2}}\qquad\qquad\qquad\qquad(11) IN+D21AD21D~21A~D~21(11)
其中 A ~ = A + I N \tilde{A}=A+I_N A~=A+IN, D ~ = ∑ j A ~ i j \tilde{D}=\sum_j\tilde{A}_{ij} D~=jA~ij,此时我们就将其特征值规范到了[0,1]之间。可以看到, A ~ \tilde{A} A~相当于每个节点增加了自连接关系,直观上来看,就是每个节点的跟新与邻近节点的表征以及节点上一层的表征有关。
将公式(11)带入公式(10)我们的图卷积表达式最终可写成如下形式:
Z = D ~ − 1 2 A ~ D ~ − 1 2 X Θ ( 12 ) \qquad\qquad\quad\qquad Z=\tilde{D}^{-\frac{1}{2}}\tilde{A}\tilde{D}^{-\frac{1}{2}}X\Theta \qquad \qquad\qquad\qquad\qquad\quad(12) Z=D~21A~D~21XΘ(12)
这里 X ∈ R N × C X\in\mathbb{R}^{N\times C} XRN×C,N表示节点数,C表示节点的特征向量维度如C-dimensional, Θ ∈ R C × F \Theta\in\mathbb{R}^{C\times F} ΘRC×F表示可训练的参数矩阵,F表示输出维度, Z ∈ R N × F Z\in \mathbb{R}^{N\times F} ZRN×F就是卷积后的信号,这个公式的计算复杂度为 O ( ∣ ε ∣ F C ) O(|\varepsilon|FC) O(εFC),由于 A ~ X \tilde{A}X A~X可以执行稀疏矩阵乘法运算,所以实际计算速度会很快。
深入浅出了解GCN原理(公式+代码)_第3张图片

图2 GCN模型对节点分类任务半监督的训练迭代过程,颜色表示类别

代码设计与实现

在GCN中,将公式(12)写成如下多层传播的形式:
H ( l + 1 ) = σ ( D ~ − 1 2 A ~ D ~ − 1 2 H ( l ) W ( l ) ) ( 13 ) \qquad\qquad \qquad H^{(l+1)}=\sigma(\tilde{D}^{-\frac{1}{2}}\tilde{A}\tilde{D}^{-\frac{1}{2}}H^{(l)}W^{(l)})\qquad\qquad\qquad \quad(13) H(l+1)=σ(D~21A~D~21H(l)W(l))(13)
代码实现方面非常简单,主要是前期对邻接矩阵的处理,以及图卷积层的构建,这里是一个pytorch实现的代码:

import torch
import torch.nn as nn
import torch.nn.init as init
import torch.nn.functional as F
# 图卷积层
class GraphConvolution(nn.Module):
    def __init__(self, input_dim, output_dim, use_bias=True):
        """图卷积:L*X*\theta

        Args:
        ----------
            input_dim: int
                节点输入特征的维度
            output_dim: int
                输出特征维度
            use_bias : bool, optional
                是否使用偏置
        """
        super(GraphConvolution, self).__init__()
        self.input_dim = input_dim
        self.output_dim = output_dim
        self.use_bias = use_bias
        self.weight = nn.Parameter(torch.Tensor(input_dim, output_dim))#权重
        if self.use_bias:
            self.bias = nn.Parameter(torch.Tensor(output_dim))
        else:
            self.register_parameter('bias', None)
        self.reset_parameters()

    def reset_parameters(self):
        init.kaiming_uniform_(self.weight)
        if self.use_bias:
            init.zeros_(self.bias)

    def forward(self, adjacency, input_feature):
        """邻接矩阵是稀疏矩阵,因此在计算时使用稀疏矩阵乘法"""
        support = torch.mm(input_feature, self.weight)
        output = torch.sparse.mm(adjacency, support)
        if self.use_bias: 
            output += self.bias
        return output

    def __repr__(self):
        return self.__class__.__name__ + ' (' \
            + str(self.input_dim) + ' -> ' \
            + str(self.output_dim) + ')'
# 将邻接矩阵标准化
def normalization(adjacency):
    """计算 L=D^-0.5 * (A+I) * D^-0.5,

    Args:
        adjacency: sp.csr_matrix.

    Returns:
        归一化后的邻接矩阵,类型为 torch.sparse.FloatTensor
    """
    adjacency += sp.eye(adjacency.shape[0])    # 增加自连接
    degree = np.array(adjacency.sum(1))
    d_hat = sp.diags(np.power(degree, -0.5).flatten())
    L = d_hat.dot(adjacency).dot(d_hat).tocoo()
    # 转换为 torch.sparse.FloatTensor
    indices = torch.from_numpy(np.asarray([L.row, L.col])).long()
    values = torch.from_numpy(L.data.astype(np.float32))
    tensor_adjacency = torch.sparse.FloatTensor(indices, values, L.shape)
    return tensor_adjacency

更多相关代码,可以从GitHub上下载:
《深入浅出图神经网络:GNN原理解析》配套代码

图神经网络相关算法详述及实现

关于GCN的论文集合:GCN相关论文汇总

总结

  从第一个版本到最终图卷积网络,每一次都在前一次的基础上大大减少了计算复杂度,最终才使得GCN的端到端训练成为可能,然而科学是不断进步和发展的,GCN还存在很多问题。

1、谱域卷积

在谱域图卷积中,我们对图的拉普拉斯矩阵进行谱分解,并通过在傅里叶空间特征分解帮助我们理解潜在的子图结构,GCN和ChebNet就是典型的应用。但是对于有向图的邻接矩阵是非对称矩阵,此时不能对拉普拉斯矩阵进行谱分解,需要我们重新定义邻接关系,或者通过其他形式的GCN来挖掘拓扑图上的关系。

2、空域卷积

空域卷积作用在节点的邻域上,我们通过聚合距离中心节点k-hop邻居来得到节点的特征表示。空域卷积相比谱域卷积更加简单和高效,GraphSAGE(Graph SAmple and aggreGatE)和GAT(Graph Attention Network) 是空域卷积的典型代表(GCN变体)。

未来围绕GCN的工作可以从以下几点围绕展开:
1、过度平滑问题
2、下游任务的处理应用
3、可解释性
4、处理有向图
5、inductive任务


参考文献

[1] T. N. Kipf and M. Welling, “Semi-supervised classification with graph convolutional networks,” 5th Int. Conf. Learn. Represent. ICLR 2017 - Conf. Tra

[2] M. Defferrard, X. Bresson, and P. Vandergheynst, “Convolutional neural networks on graphs with fast localized spectral filtering,” Adv. Neural Inf. Process. Syst., no. 59, pp. 3844–3852, 2016.

[3] Y. Jin, N. Duffield, P. Haffner, S. Sen, and Z. L. Zhang, “Learning Convolutional Neural Networks for Graphs Mathias,” 2010 22nd Int. Teletraffic Congr. - Proceedings, ITC 22, vol. 1, 2010.

[4] J. Bruna, W. Zaremba, A. Szlam, and Y. LeCun, “Spectral networks and deep locally connected networks on graphs,” 2nd Int. Conf. Learn. Represent. ICLR 2014 - Conf. Track Proc., pp. 1–14, 2014.

[5] 《深入浅出图神经网络:GNN原理解析》刘忠雨,李彦霖,周洋

[6] 知乎:如何理解 Graph Convolutional Network(GCN)

你可能感兴趣的:(GCN,神经网络,数据挖掘,机器学习)