图卷积神经网络(GCN):参考答案

目录

\[ \begin{align}\begin{aligned}\newcommand{\ba}{\boldsymbol{a}} \newcommand{\bb}{\boldsymbol{b}} \newcommand{\be}{\boldsymbol{e}} \newcommand{\bq}{\boldsymbol{q}} \newcommand{\bk}{\boldsymbol{k}} \newcommand{\bw}{\boldsymbol{w}} \newcommand{\bx}{\boldsymbol{x}} \newcommand{\by}{\boldsymbol{y}} \newcommand{\bz}{\boldsymbol{z}} \newcommand{\bd}{\boldsymbol{d}} \newcommand{\bv}{\boldsymbol{v}} \newcommand{\bs}{\boldsymbol{s}}\\\newcommand{\btheta}{\boldsymbol{\theta}} \newcommand{\bbeta}{\boldsymbol{\beta}} \newcommand{\bgamma}{\boldsymbol{\gamma}} \newcommand{\bsigma}{\boldsymbol{\sigma}} \newcommand{\md}{\mbox{d}} \newcommand{\bmu}{\boldsymbol{\mu}} \newcommand{\bone}{\boldsymbol{1}} \newcommand{\bzero}{\boldsymbol{0}} \newcommand{\bepsilon}{\boldsymbol{\epsilon}} \newcommand{\bphi}{\boldsymbol{\phi}} \newcommand{\bh}{\boldsymbol{h}} \newcommand{\bc}{\boldsymbol{c}} \newcommand{\br}{\boldsymbol{r}} \newcommand{\bQ}{\boldsymbol{Q}} \newcommand{\bK}{\boldsymbol{K}} \newcommand{\bV}{\boldsymbol{V}} \newcommand{\bSigma}{\boldsymbol{\Sigma}} \newcommand{\bg}{\boldsymbol{g}} \newcommand{\bxi}{\boldsymbol{\xi}} \newcommand{\bvarepsilon}{\boldsymbol{\varepsilon}} \newcommand{\bdelta}{\boldsymbol{\delta}} \newcommand{\bq}{\boldsymbol{q}} \newcommand{\bk}{\boldsymbol{k}} \newcommand{\bJ}{\boldsymbol{J}} \newcommand{\bp}{\boldsymbol{p}} \newcommand{\bi}{\boldsymbol{i}} \newcommand{\bo}{\boldsymbol{o}} \newcommand{\bE}{\boldsymbol{E}} \newcommand{\bH}{\boldsymbol{H}} \newcommand{\bL}{\boldsymbol{L}} \newcommand{\bu}{\boldsymbol{u}} \newcommand{\bLambda}{\boldsymbol{\Lambda}} \newcommand{\trans}{^{\rm\scriptsize T}} \newcommand{\var}{\mathrm{var}}\\\newcommand{\bA}{\boldsymbol{A}} \newcommand{\bB}{\boldsymbol{B}} \newcommand{\bC}{\boldsymbol{C}} \newcommand{\bD}{\boldsymbol{D}} \newcommand{\bG}{\boldsymbol{G}} \newcommand{\bI}{\boldsymbol{I}} \newcommand{\bM}{\boldsymbol{M}} \newcommand{\bP}{\boldsymbol{P}} \newcommand{\bS}{\boldsymbol{S}} \newcommand{\bU}{\boldsymbol{U}} \newcommand{\bW}{\boldsymbol{W}} \newcommand{\bX}{\boldsymbol{X}} \newcommand{\bY}{\boldsymbol{Y}} \newcommand{\bZ}{\boldsymbol{Z}} \newcommand{\cotp}{\textcolor[RGB]{48,209,88}{TP}} \newcommand{\cotn}{\textcolor[RGB]{100,210,255}{TN}} \newcommand{\cofp}{\textcolor[RGB]{94,92,230}{FP}} \newcommand{\cofn}{\textcolor[RGB]{191,90,242}{FN}}\\\newcommand{\numcotp}{\textcolor[RGB]{48,209,88}{50}} \newcommand{\numcotn}{\textcolor[RGB]{100,210,255}{30}} \newcommand{\numcofp}{\textcolor[RGB]{94,92,230}{10}} \newcommand{\numcofn}{\textcolor[RGB]{191,90,242}{10}} \DeclareMathOperator*{\argmin}{arg\,min}\end{aligned}\end{align} \]

图卷积神经网络(GCN):参考答案#

返回正文练习 · 返回答案索引

说明#

以下答案与正文题目逐题对应。前六题给出主要推导与计算;第 7--9 题给出可以运行的程序和检查方法;第 10--12 题说明比较时需要保持哪些条件相同,以及实验结果能够说明什么、不能说明什么。

  1. 加入自环后

    \[\begin{split}\tilde{\bA} =\begin{bmatrix}1&1\\1&1\end{bmatrix}, \qquad \tilde{\bD} =\begin{bmatrix}2&0\\0&2\end{bmatrix}, \qquad \boldsymbol{S} =\frac12\begin{bmatrix}1&1\\1&1\end{bmatrix}.\end{split}\]

    因而

    \[\begin{split}\boldsymbol{H}^{[1]} =\boldsymbol{S}\cdot\bX W =\begin{bmatrix}3\\3\end{bmatrix}3 =\begin{bmatrix}9\\9\end{bmatrix},\end{split}\]

    这里 \(W\) 是标量,所以与向量相乘直接并置;若权重是矩阵,则写为矩阵乘法。

  2. 加自环后的度数是 \((2,3,2)\)⁠。传播矩阵为

    \[\begin{split}\boldsymbol{S} =\begin{bmatrix} \frac12&\frac1{\sqrt6}&0\\ \frac1{\sqrt6}&\frac13&\frac1{\sqrt6}\\ 0&\frac1{\sqrt6}&\frac12 \end{bmatrix}.\end{split}\]

    第一、三行和为 \(1/2+1/\sqrt6\approx0.9082\)⁠,第二行和为 \(1/3+2/\sqrt6\approx1.1498\)⁠。矩阵对称但行和不为 1;只有在特殊的度结构下,对称归一化才可能同时表现为行随机矩阵。

  3. 无向图有 \(\tilde{\bA}\trans=\tilde{\bA}\)⁠,对角矩阵 \(\tilde{\bD}^{-1/2}\) 也等于自身转置,因此

    \[\boldsymbol{S}\trans =\tilde{\bD}^{-1/2}\cdot \tilde{\bA}\trans\cdot \tilde{\bD}^{-1/2} =\boldsymbol{S}.\]

    对置换矩阵 \(\boldsymbol{P}\)⁠,有 \(\tilde{\bA}'=\boldsymbol{P}\cdot\tilde{\bA}\cdot\boldsymbol{P}\trans\)⁠、\(\tilde{\bD}'=\boldsymbol{P}\cdot\tilde{\bD}\cdot\boldsymbol{P}\trans\)⁠,进而 \(\boldsymbol{S}'=\boldsymbol{P}\cdot\boldsymbol{S}\cdot\boldsymbol{P}\trans\)⁠。于是

    \[\sigma(\boldsymbol{S}'\cdot\boldsymbol{P}\cdot\boldsymbol{H}\cdot\bW) =\boldsymbol{P}\cdot\sigma(\boldsymbol{S}\cdot\boldsymbol{H}\cdot\bW),\]

    其中逐元素激活函数与行置换可交换。

  4. 矩阵乘法结合律直接给出两式相等。先变换特征的主要成本约为

    \[nd_{\mathrm{in}}d_{\mathrm{out}}+md_{\mathrm{out}} =81.92\times10^6+3.2\times10^6 =85.12\times10^6.\]

    先传播输入特征的成本约为

    \[md_{\mathrm{in}}+nd_{\mathrm{in}}d_{\mathrm{out}} =25.6\times10^6+81.92\times10^6 =107.52\times10^6.\]

    因为 \(d_{\mathrm{out}}<d_{\mathrm{in}}\)⁠,本例先计算 \(\boldsymbol{H}\cdot\bW\) 更省。实际速度还受稀疏内核、缓存和后向传播影响,运算数不是墙钟时间的完整预测。

  5. \(\boldsymbol{L}_{\mathrm{un}}=\bD-\bA\) 和对角矩阵 \(\bD\) 的定义,先展开二次型:

    \[\begin{split}\begin{aligned} \boldsymbol{x}\trans\cdot \boldsymbol{L}_{\mathrm{un}}\cdot\boldsymbol{x} &=\boldsymbol{x}\trans\cdot (\bD-\bA)\cdot\boldsymbol{x}\\ &=\sum_{i=1}^{n}d_i x_i^2 -\sum_{i=1}^{n}\sum_{j=1}^{n}A_{ij}x_i x_j\\ &=\sum_{i=1}^{n}\sum_{j=1}^{n}A_{ij}x_i^2 -\sum_{i=1}^{n}\sum_{j=1}^{n}A_{ij}x_i x_j, \end{aligned}\end{split}\]

    其中,最后一步使用了 \(d_i=\sum_{j=1}^{n}A_{ij}\)⁠。无向图的邻接矩阵满足 \(A_{ij}=A_{ji}\)⁠。交换两重求和中的下标 \(i,j\)⁠,可得

    \[\sum_{i=1}^{n}\sum_{j=1}^{n}A_{ij}x_i^2 =\sum_{i=1}^{n}\sum_{j=1}^{n}A_{ij}x_j^2.\]

    因此,可以用两个相等和式的平均替换第一个和式。将结果代回二次型的展开式,有

    \[\begin{split}\begin{aligned} \boldsymbol{x}\trans\cdot \boldsymbol{L}_{\mathrm{un}}\cdot\boldsymbol{x} &=\frac{1}{2} \sum_{i=1}^{n}\sum_{j=1}^{n} A_{ij}(x_i^2+x_j^2) -\sum_{i=1}^{n}\sum_{j=1}^{n}A_{ij}x_i x_j\\ &=\frac{1}{2} \sum_{i=1}^{n}\sum_{j=1}^{n} A_{ij}(x_i^2-2x_i x_j+x_j^2)\\ &=\frac{1}{2} \sum_{i=1}^{n}\sum_{j=1}^{n} A_{ij}(x_i-x_j)^2. \end{aligned}\end{split}\]

    两重求和中,无向边 \(\{i,j\}\) 会分别以 \((i,j)\)\((j,i)\) 出现一次,所以系数 \(1/2\) 用于消除这种重复计数。

  6. 因为每个 \(d_i>0\)⁠,所以 \(\bD^{-1/2}\) 存在。令

    \[\boldsymbol{y} =\bD^{-1/2}\cdot\boldsymbol{x}, \qquad y_i=\frac{x_i}{\sqrt{d_i}}.\]

    \(\boldsymbol{x}=\bD^{1/2}\cdot\boldsymbol{y}\) 以及对角矩阵的转置仍等于自身,有

    \[\begin{split}\begin{aligned} \boldsymbol{x}\trans\cdot \boldsymbol{L}\cdot\boldsymbol{x} &=\boldsymbol{x}\trans\cdot \left( \bI-\bD^{-1/2}\cdot\bA\cdot\bD^{-1/2} \right)\cdot\boldsymbol{x}\\ &=\boldsymbol{y}\trans\cdot\bD\cdot\boldsymbol{y} -\boldsymbol{y}\trans\cdot\bA\cdot\boldsymbol{y}\\ &=\boldsymbol{y}\trans\cdot (\bD-\bA)\cdot\boldsymbol{y}\\ &=\boldsymbol{y}\trans\cdot \boldsymbol{L}_{\mathrm{un}}\cdot\boldsymbol{y}. \end{aligned}\end{split}\]

    再应用上一题的未归一化图拉普拉斯二次型恒等式,并代入 \(y_i=x_i/\sqrt{d_i}\)⁠,可得

    \[\begin{split}\begin{aligned} \boldsymbol{x}\trans\cdot \boldsymbol{L}\cdot\boldsymbol{x} &=\frac{1}{2} \sum_{i=1}^{n}\sum_{j=1}^{n} A_{ij}(y_i-y_j)^2\\ &=\frac{1}{2} \sum_{i=1}^{n}\sum_{j=1}^{n} A_{ij} \left( \frac{x_i}{\sqrt{d_i}}- \frac{x_j}{\sqrt{d_j}} \right)^2. \end{aligned}\end{split}\]

    假设 \(d_i>0\) 的直接原因是,当 \(d_i=0\)\(1/\sqrt{d_i}\) 没有定义。若存在零度节点,需要先明确对孤立节点的处理规则;GCN 的重归一化会先加入自环,使新度数为正。

  7. 可先用线性层得到 \(\boldsymbol{Z}=\boldsymbol{H}\cdot\bW\)⁠,再沿边传播:

    import torch
    
    def sparse_gcn(edge_index, x, weight, num_nodes=None, activation=None):
        n = x.shape[0] if num_nodes is None else int(num_nodes)
        if edge_index.ndim != 2 or edge_index.shape[0] != 2 or n != x.shape[0]:
            raise ValueError("边或节点维度错误")
        send, receive = edge_index.long()
        if torch.any((send < 0) | (send >= n) | (receive < 0) | (receive >= n)):
            raise IndexError("边端点越界")
        # 先移除已有自环,再统一加入一个自环,避免重复。
        keep = send != receive
        nodes = torch.arange(n, device=x.device)
        send = torch.cat([send[keep], nodes])
        receive = torch.cat([receive[keep], nodes])
        degree = x.new_zeros(n)
        degree.index_add_(0, receive, x.new_ones(len(receive)))
        if torch.any(degree <= 0):
            raise ValueError("加入自环后度必须为正")
        norm = degree[receive].rsqrt() * degree[send].rsqrt()
        z = x @ weight
        out = x.new_zeros(n, z.shape[1])
        out.index_add_(0, receive, norm[:, None] * z[send])
        return (out if activation is None else activation(out)), norm
    

    若输入是带权图,度和归一化都应使用明确的边权规则;负权图不能直接套用普通正度公式。

  8. 第 1、2 题先按照 手算结果与可信实现对照 的原则逐元素对照 \(\boldsymbol{S}\) 和输出,再将相同参数复制到第 7 题的稀疏实现和库层。库可能默认自行加自环、合并重复边或采用不同方向,必须先关闭或对齐这些选项。用 float64 比较前向及输入、权重梯度,例如 rtol=1e-7, atol=1e-9置换测试验证等变性;单节点和孤立节点验证自环;重复边测试明确是累加还是合并。自动微分结果 一致只说明两份程序实现同一运算,不证明数据划分没有泄漏。

  9. 两层分类器只在 train_mask 上计算交叉熵;验证和测试函数使用 eval 模式且不更新参数。测试 train_mask & valid_mask 等均为空,每个掩码至少含一个节点。固定参数后分别改变验证、测试标签,重新计算一次训练梯度,结果必须完全相同。置换测试同时置换邻接、特征、标签和三个掩码,各集合预测指标应保持不变。若图划分还涉及测试边,应另行检查训练传播图是否允许看到这些边。

  10. 一次性确定数据划分和预处理方法,三个模型使用相同的特征、随机种子、近似参数量和最大更新次数,并使用同一个验证集和相同标准选择模型。模型结构、超参数和训练方法确定后,测试集只使用一次。报告准确率、macro-F1⁠、训练时间、固定节点批量预测时间、峰值内存、参数量和图传播运行次数。

    逻辑回归与 MLP 检验不使用图结构时的可达到水平;GCN 只有在边提供有效信号时才应取得稳定增益。传导节点分类中一次全图前向与独立批量预测语义不同,计时必须说明预测范围和可见结构。

  11. 两种实现使用完全相同的自环、重复边、特征、权重、数据类型和设备;固定种子、预热、前向次数和重复计时方法。先报告最大输出误差,再记录固定图批量时间、峰值内存、实际边数和传播调用次数。稠密内存为 \(O(n^2)\)⁠,稀疏结构内存约为 \(O(m)\)⁠;但在小而密图上稠密矩阵内核可能更快。无法分配稠密矩阵的规模应记录为资源限制,而不是虚构时间。

  12. 固定划分、种子集合、最大更新次数、早停规则和评价代码;所有深度、宽度和学习率组合使用相同网格与验证选模流程。报告测试指标、训练时间、固定批量预测时间、峰值内存、参数量、传播调用次数、节点表示方差和平均余弦相似度。

    深度和宽度会改变模型容量与计算量,学习率会改变训练过程。应列出各个深度、宽度和学习率组合的结果,或者分几次比较、每次只改变其中一项,不能同时改变三项后把结果解释为某一项的作用。如果更深的模型出现节点表示方差降低、相似度升高且性能下降,说明可能出现了过度平滑,但梯度、正则化和过度挤压也可能造成相似现象,因此还不能排除其他原因。