# 图网络略述(intro&GCN) > author:廖总 其实开始冲图神经网络也已经有一段时间了,从半年前阅览《Relational inductive biases, deep learning, and graph networks》一文,对图网络有了一定的了解,到现在已经过去半年了。然而这半年中我一直在摸鱼,就算偶尔复习图神经网络的方法,也一直在复习相同的工作。如今有幸受实验室隔座的兄弟邀请参与一个科研项目。比较尴尬的是,我除了自诩知晓图网络以外,对实际的操作与应用一无所知。我遂写下这篇文章,来昭示自己正式开始图网络的学习,并且尝试记录和融汇已知的知识点,不在浪费时间于重复的工作上。 首先,图网络是为什么产生的呢,通常,我们简略的称之为,方便表达结构化的知识,处理结构化的数据。这里的结构其实跟数据库及数据结构中描述的结构是相似的,即一种信息之间关联的结构,以及知识之间关联的结构。图拥有这样的特点,能通过节点来对实体进行描述,并用边描述实体之间的关系,与此同时还有着隐含信息的储存和全局信息的存在,于是构成了一个新颖且复杂的神经网络结构。 在周志华的机器学习一书半监督学习章节中,有对基本的图学习策略进行基本的描述,详见我为了应付课程考试整理的[图半监督学习](http://blog.cyasylum.top/index.php/2020/07/05/%E5%9B%BE%E5%8D%8A%E7%9B%91%E7%9D%A3%E5%AD%A6%E4%B9%A0/)。其基本思路是这样的,通过”样本距离度量“刻画获取样本之间的联系,将样本嵌入到“图”,即样本即其关系的集合中。后通过图将半监督学习中有标记样本的标签对未标记样本进行传递,从而获取未标记样本的属性,进行学习。 如此,便定下来图网络的基本思路,即通过*信息在图上的传递*,迭代学习知识。有了这样的基础,我们便可以开始对图网络进行讨论了。 接下来,我们从最基础的部分来讲讲,信息是如何在图上进行传播的。 ## 消息传递 那么,消息是什么呢?在大多数时候,我们将消息理解为节点(但其实在特定场合,边或者全局信息都是可以或者需要考虑的),即“实体”包含的,要传递的信息。对于一个结构相对复杂的节点而言,假设其拥有 n 个属性,我们便用一个 n 维的向量(或是其他什么)$\mathbf{x}$表示节点中储存的信息。然后,节点上的信息要怎么传递呢? 答案必然是通过节点之间的连接。 在离散数学中,我们使用邻接矩阵来刻画图上所有节点之间的联系,即 Adjacency Matrix,记作$\mathbf{A}$。在不考虑边权重的情况下,我们将存在节点$x_{i},x_{j}$之间的联系表示为$A_{ij}=1$,在存在权重的情况下,我们将$A_{ij}$的值记作两节点之间边的权重。值得注意的是,$\mathbf{A}$对角线上的值,即节点之间自连接的系数,在不做考虑自连接时都被记作 $0$ 。 另外,我们特殊定义节点的度为该点所有连接权重之和,即$D_i=\sum_{j=0}^n A_{ij} $,使用对角矩阵$\mathbf{D}=diag(D_1,D_2,\cdots,D_n)$进行统一描述。 如此,我们便通过了两个矩阵刻画了一张图上所有节点之间的传递关系。为了方便计算,以及因为种种特性,一张图最终的传递特性,被描述成了拉普拉斯矩阵$\mathbf{L}=\mathbf{D}-\mathbf{A}$。 我们通过拉普拉斯矩阵 $L$ 来考虑图上的消息传递特性。 同时,我们可以理解为,拉普拉斯矩阵描述了图的结构。 ## 归一化拉普拉斯矩阵 为了方便拉普拉斯矩阵在机器学习等众多需要迭代求解问题中的实际使用,我们要求对拉普拉斯矩阵进行归一化操作,从而避免在多次传递后导致的梯度爆炸和梯度消失。我们需要令其对角线上元素统一等于 1。 我们已知的是,主对角线上的元素只会同 $D$矩阵有关,因此,我们引入了 $\mathbf{D}^{-\tfrac{1}{2}}$ 作为归一化算子,令归一化拉普拉斯矩阵为 $$ \mathbf{L}^{sym} =\mathbf{D}^{-\frac{1}{2}} \mathbf{L}\mathbf{D}^{-\frac{1}{2}} =\mathbf{I}-\mathbf{D}^{-\frac{1}{2}} \mathbf{A}\mathbf{D}^{-\frac{1}{2}} $$ $$ L_{ij}^{sym}= \begin{cases} 1 \quad\quad\quad\quad\quad\quad\quad\enspace\thinspace \left ( i=j\right ) \cup \left ( deg(v_i)\ne0 \right ) \\ -\frac{1}{\sqrt{deg(v_i)deg(v_j)}} \qquad \left ( i\ne j \right ) \cup \left ( v_i adj v_j \right ) \\ 0 \quad\quad\quad\quad\quad\quad\quad\;\;\; else. \end{cases} $$ 现在,我们可以尝试用$\mathbf{L}$对图进行表示了。 另外还有个*随机游走归一化拉普拉斯矩阵* $$ \mathbf{L}^{sym}=\mathbf{D}^{-1}\mathbf{L}=\mathbf{I}-\mathbf{D}^{-1}\mathbf{A} $$ ,不过我还不熟,暂时不管了。 ## 图的频域表示 其实挺意外的,早在去年的这个时候,我也考虑过这个问题。对图像的矩阵进行奇异值分解,通过切除部分奇异值,我们可以对图像中的低频和高频信息进行定量的剪切,使得减少储存信息的同时不丢失大部分画质。这样的特性是不是同信号的频域分析,即傅里叶变换有着相似之处。当时的我寻遍了全网,并没有得到什么结果。而如今,我在做图的谱分析。 为了直观地对描述信号传播的图进行直观的“卷积滤波(需要注意的是,这里的卷积就不是图像意义上的卷积了,而是信号意义的卷积。但是在实际运用中图的卷积表示的也是信号在相邻图节点上的传播,这又与图像的卷积有着异曲同工之妙,那么新的问题来了,信号卷积和图像卷积是否也存在着什么物理层面上的联系?)”我们通过特征分解,获取图的“频谱”,从这边开始,便是 Spectral Graph Convolution 的思想了。 我们将 L 矩阵进行特征分解,有$\mathbf{L}=\mathbf{U}\boldsymbol{\Lambda}\mathbf{U}^\mathsf{T}$,其中特征值描述的是图像的频谱强度,而特征向量描述了分解的基底,即频率,对应频谱分析中的$e^{-j\omega t}$。 于是,我们考虑滤波和滤波器,我们设计$g\theta=diag(\theta)$,有滤波器改变了基底上信号的强度,即有$g\theta(\Lambda)$为特征值的函数。我们有$g\theta$在图$\mathbf{L}$上对输入信号$x$的卷积等于$g\theta$、$x$在频域相乘: $g\theta\star x=\mathbf{U}g\theta\mathbf{U}^\mathsf{T}x $ 如此,我们完成了在图神经网络上进行分析的基础。 但是在实际问题下,这样的图是极难计算的,当我们的节点规模较大时,对 N^2^ 规模的图进行矩阵分解,并且进行多次矩阵乘法需要消耗极大的资源,这使得图网络很难运行。因此纵使图网络有着其特殊的性质,其热度一直不是很高。 ## ChebNet 及其思考 ChebNet 的引入是当今神经网络大热门的开端,也是图卷积网络的基础。其思路为,使用切比雪夫多项式对卷积过程 K 阶拟合 ([参考](https://zhuanlan.zhihu.com/p/138420723)) ChebNet 假设$g\theta$对$\Lambda$的滤波结果是原始特征值多项式函数,而网络的目的是抛弃原本通过矩阵相乘来对卷积结果进行求解,而通过参数学习来对结果进行表示,给出下式 $$ g\theta(\Lambda)=\sum_{k=0}^K \beta_kT_k(\hat{\Lambda})=\begin{pmatrix} \begin{matrix}\sum_{k=1}^K \beta_kT_k(\hat{\lambda_1})\end{matrix}\\ &\cdots\\ &&\begin{matrix} \sum_{k=1}^K \beta_kT_k(\hat{\lambda_n})\end{matrix} \end{pmatrix} $$ 其中有切比雪夫多项式在矩阵上的表示,具体数学背景可以详细查看 $$ T_0(L) = I\ T_1(L)=L\ T_{n+1}(L)=2LT_n(L) - T_{n-1}(L) $$ 有$\beta_k$为网络的待学习参数 我们将原式 $$ g\theta\star x=\mathbf{U}g\theta\mathbf{U}^\mathsf{T}x $$ 表示为拟合形式 $$ \mathbf{U} \begin{matrix} \sum_{k=0}^K \beta_kT_k(\hat{\Lambda}) \mathbf{U}^\mathsf{T}x \end{matrix} $$ 并对其中无关输入信号 $x$ 的部分进行改写 $$ \mathbf{U}\begin{matrix}\sum_{k=0}^K \beta_kT_k(\hat{\Lambda}) \mathbf{U}^\mathsf{T} \end{matrix} $$ $$ =\begin{matrix} \sum_{k=0}^K \mathbf{U}\beta_k(\begin{matrix}\sum_{c=0}^k\alpha_{kc}\hat{\Lambda^k} \end{matrix}\mathbf{U}^\mathsf{T}) \end{matrix} $$ $$ =\begin{matrix} \sum_{k=0}^K\beta_k(\begin{matrix}\sum_{c=0}^k\alpha_{kc}(\mathbf{U}\hat{\Lambda} \mathbf{U}^\mathsf{T})^k \end{matrix}) \end{matrix} $$ $$ =\begin{matrix} \sum_{k=0}^K\beta_kT_k(\mathbf{U}\hat{\Lambda} \mathbf{U}^\mathsf{T}) \end{matrix} $$ $$ =\begin{matrix} \sum_{k=0}^K\beta_kT_k(\hat{\mathbf{L}}) \end{matrix} $$ 其中 $$ \hat{\mathbf{L}}=\frac{2}{\lambda_{max}}\mathbf{L}-\mathbf{I}_N $$ 于是我们获得了 $$ g\theta\star x =\mathbf{U}g\theta\mathbf{U}^\mathsf{T}x =\begin{matrix} \sum_{k=0}^K\beta_kT_k (\hat{\mathbf{L}})x \end{matrix} $$ 作为 ChebNet 的卷积结构 其中值得注意的一点是,ChebNet 的 K 值限制了卷积核的多项式次数,但是这里的多项式次数描述了什么呢?其实就是卷积的“范围”,即单次卷积内最高可获得的 K 阶相邻节点信息。在 K=n 的时候,我们从理论上可以通过单次卷积,获取一张连通图上所有结点的信息,而这也是原方法难以计算的根本原因。 到这里为止,我介绍了 2018 年 GCN 出现之前图网络的基本使用,并给出了对图网络的基本认知,于是,我们拥有了相对充分的工具去认识图网络近期的发展,以及更深层次的使用。 ## Graph Convolutional Networks 前面谈论的都是基于图传播的方法以及理论基础,之后才是图神经网络(GNN)相关的内容,即我们需要将一个抽象的图的概念存入实际的数据结构,并考虑其在实际数据上的应用,好吧换句话来说就是开始抄论文。 ## 设计思考 GCN 是典型的依靠消息传递进行迭代的网络,并通过对图卷积谱的线性逼近,其实就是只考虑相邻元素的线性组合,从而达到单层参数的最小化,使得 GCN 的深度构建成为可能。同时,一阶的拟合表示图卷积神经网络在单次卷积迭代中,仅考虑邻域的信息,这使得 GCN 在物理直觉上产生了与 CNN 的相似之处。我们可以发现,GCN 中的 Convolutional 是图的频谱卷积和图的邻域卷积的融合。即表现出了多种层面的滤波特性。也许正是如此,使得 GCN 有着如此好的表现。 ## 数学表示 ### 卷积层 ### 原典 之前我们引入的是图传播特性的建模,而现在我们需要考虑一次实际的迭代如何在图上进行。 我们通常考虑输入的参数与节点的隐含状态,作为网络的隐含层,即 ℎ,我们需要在每一轮迭代中对每个节点的状态进行更新,从而达到端对端训练的可能性。因此我们要确定隐含状态的更新模式,如下展示: $$ \mathbf{h}_i^{l+1}=\sigma(\mathbf{W}_0^{(l)\mathsf{T}}\mathbf{h}_i^{(l)}+ \begin{matrix} \sum_{j\in\mathcal{N}_i}c_{ij}\mathbf{W}_1^{(l)\mathsf{T}}\mathbf{h}_j^{l}+\mathbf{b}_0^{(l)} \end{matrix} ) $$ ### 归一化单参数 在实际场景中,减少了网络的参数,并对传播进行了重新的归一化 $$ \mathbf{H}^{(l+1)}=\sigma(\tilde{\mathbf{D}}^{-\frac{1}{2}}\tilde{\mathbf{A}}\tilde{\mathbf{D}}^{-\frac{1}{2}}\mathbf{H}^{(l)}\mathbf{W}^{(l)}) $$ 其中 $$ \tilde{\mathbf{A}}=\mathbf{A}+\mathbf{I}_N,\tilde{D}_i =\begin{matrix} \sum_j\tilde{A}_{ij} \end{matrix} $$ 作为传播的预处理部分,由此得到了 GCN 的完整数学表示。 ### 损失函数 文中针对半监督节点分类问题设计了一下目标函数,仅对有监督部分设计交叉熵损失,有: $$ \mathcal{L}_{sup} =-\begin{matrix} \sum_{l\in\mathcal{y}_L} \end{matrix} \begin{matrix} \sum_{\mathcal{f}=1}^{d_{out}}\gamma_{l,f}\ln Z_{l,f} \end{matrix} $$ 其中 γ 为指示变量,表示蒸馏分类正确类的交叉熵损失。 $$ \mathbf{Z}=f(\mathbf{X},\mathbf{A}) $$ 为预测结果。即我们希望正确分类下类可能性的最大化。 ## 代码实现 归一化步骤 ```python def norm(A): A = A + torch.eye(A.size(0)) d = torch.sum(A, dim=1) D = torch.diag(d) return torch.pow(D,-0.5).mm(A).mm(torch.pow(D,-0.5)) ``` GCN 本体 ```python class GCN(nn.Module): def __init__(self, A, din, hidden, dout): super(GCN, self).__init__() self.A = norm(A) self.gcn1 = nn.Linear(din, hidden, bias=True) self.gcn2 = nn.Linear(hidden, dout, bias=True) def forward(self, X): X = F.relu(self.gcn1(self.A.mm(X))) X = self.gcn2(self.A.mm(X)) return X ``` ### 测试方法 参考 dgl 的[使用教程](https://docs.dgl.ai/tutorials/basics/1_first.html)