消息传递神经网络:参考答案

目录

\[ \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} \]

消息传递神经网络:参考答案#

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

说明#

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

  1. 加入自环后,第一层为

    \[\boldsymbol{h}^{[1]} =\left(\frac{1+2}{2},\frac{1+2+4}{3},\frac{2+4}{2}\right) =\left(\frac32,\frac73,3\right).\]

    第二层继续使用同一邻域:

    \[\boldsymbol{h}^{[2]} =\left( \frac{\frac32+\frac73}{2}, \frac{\frac32+\frac73+3}{3}, \frac{\frac73+3}{2} \right) =\left(\frac{23}{12},\frac{41}{18},\frac83\right).\]

    两层后端点可以接收另一端点的二跳信息,中间节点的二跳集合没有新增节点。感受野描述可能依赖的最远距离,不表示每个节点都恰好存在该距离的邻居。

  2. \(\pi\) 为节点置换。对新编号节点 \(\pi(v)\)⁠,其邻居集合正是 \(\{\pi(u):u\in\mathcal N(v)\}\)⁠。共享消息函数使对应边消息只被重新编号;聚合器不依赖枚举顺序,因此聚合结果满足 \(\boldsymbol{m}'_{\pi(v)}=\boldsymbol{m}_v\)⁠,共享更新函数进一步给出 \(\boldsymbol{h}'_{\pi(v)}=\boldsymbol{h}_v\)⁠。矩阵写法即

    \[f(\boldsymbol{P}\cdot\bA\cdot\boldsymbol{P}\trans, \boldsymbol{P}\cdot\bX) =\boldsymbol{P}\cdot f(\bA,\bX).\]

    sum 与 mean 对节点行的排列不敏感,所以 \(\operatorname{READOUT}(\boldsymbol{P}\cdot\boldsymbol{H})=\operatorname{READOUT}(\boldsymbol{H})\)⁠。证明要求图结构、特征和边特征同步置换;只打乱其中一项会破坏对应关系。

  3. 基例 \(L=0\)\(\boldsymbol{h}_v^{[0]}\) 只依赖 \(v\)⁠。假设第 \(L-1\) 层每个邻居 \(u\) 的表示只依赖其 \(L-1\) 跳邻域;第 \(L\) 层读取 \(v\) 的一跳邻居表示,所有依赖节点到 \(v\) 的距离至多为 \(1+(L-1)=L\)⁠,归纳完成。

    “最多”是计算图的依赖上界。聚合器可能发生碰撞、权重可能为 0、非线性可能压缩信息,固定宽度也可能造成过度挤压,所以拥有 \(L\) 跳感受野并不保证能够重构或区分所有 \(L\) 跳子图。

  4. 计算表为

    \[\begin{split}\begin{array}{c|ccc} \text{多重集合}&\mathrm{sum}&\mathrm{mean}&\mathrm{max}\\ \hline \{1\}&1&1&1\\ \{1,1\}&2&1&1\\ \{1,3\}&4&2&3\\ \{2,2\}&4&2&2\\ \{0,2\}&2&1&2 \end{array}.\end{split}\]

    mean 无法区分 \(\{1\}\)\(\{1,1\}\)⁠,丢失重复次数;max 也无法区分这两个集合,并忽略所有非最大元素。sum 能区分这对集合,却无法区分 \(\{1,3\}\)\(\{2,2\}\)⁠,因此求和也不是对任意实数多重集合都一一对应;配合足够表达力的映射时才可能增强区分能力。

  5. 一个不构造稠密邻接矩阵的 NumPy 骨架如下:

    import numpy as np
    
    def edge_mean(edge_index, x, num_nodes=None, add_self_loops=True,
                  edge_weight=None):
        edge_index = np.asarray(edge_index, dtype=np.int64)
        x = np.asarray(x, dtype=float)
        n = len(x) if num_nodes is None else int(num_nodes)
        if edge_index.ndim != 2 or edge_index.shape[0] != 2 or len(x) != n:
            raise ValueError("edge_index 或 x 维度错误")
        send, receive = edge_index
        if np.any(send < 0) or np.any(send >= n) or np.any(receive < 0) or np.any(receive >= n):
            raise IndexError("边端点越界")
        weight = (np.ones(len(send)) if edge_weight is None
                  else np.asarray(edge_weight, dtype=float))
        if weight.shape != (len(send),):
            raise ValueError("边权维度错误")
        if add_self_loops:
            nodes = np.arange(n)
            send = np.concatenate([send, nodes])
            receive = np.concatenate([receive, nodes])
            weight = np.concatenate([weight, np.ones(n)])
        out = np.zeros((n, x.shape[1]), dtype=float)
        degree = np.zeros(n, dtype=float)
        np.add.at(out, receive, weight[:, None] * x[send])
        np.add.at(degree, receive, weight)
        if np.any(degree == 0):
            raise ValueError("存在零度接收节点")
        return out / degree[:, None], degree
    

    重复边会重复贡献;若希望先合并重复边,应把规则写入接口。负边权会使“度”不再是普通计数,是否允许需由模型定义。

  6. 对每个图先用手算确认边方向,再比较三种实现,例如 assert_allclose(dense, sparse)置换测试构造 \(\bA'=\boldsymbol{P}\cdot\bA\cdot\boldsymbol{P}\trans\)⁠、\(\bX'=\boldsymbol{P}\cdot\bX\)⁠,检查 \(\boldsymbol{H}'=\boldsymbol{P}\cdot\boldsymbol{H}\)⁠,并比较 readout。只有一条边 \(1\to2\) 且两节点特征不同的例子能暴露端点写反;孤立节点验证自环策略;重复边验证计数;非法端点应报错。浮点比较使用明确容差,整数度数应完全一致。

  7. 层接口可写为 forward(x, edge_index, edge_attr=None, reduce="mean", residual=False)所有边消息都从同一个旧 x 读取并写入新缓冲区;max 对空邻域要定义自环或显式初值。批量多图可附加 batch 编号并断言每条边两端属于同一图。测试改变节点和边枚举顺序、反转有向边、复制边以及关闭残差;对标量损失执行后向传播,断言每个参数梯度存在且有限。自动微分 只能核验实现的导数,不能替代边方向和聚合语义测试。

  8. 可让节点自身特征相同,而标签由邻居中特定特征是否出现决定;这样 MLP 无法使用图结构信息,MPNN 则可以尝试学习邻居信息。一次性确定数据划分,两种模型使用相同的随机种子、相近参数量、更新次数和每次更新的目标节点数,并使用同一个验证集和相同标准选择模型。报告测试准确率或 macro-F1⁠、训练时间、固定节点批量预测时间、峰值内存、参数量和消息函数运行次数。

    若 MPNN 优于 MLP,只能说明所给结构对该任务有用;若使用传导划分,还应明确预测时可见哪些测试节点特征和边。不能通过测试标签构图或选择超参数。

  9. 三种实现接收完全相同的边、多重边规则、特征、数据类型和设备;固定种子、预热和重复次数。先逐项比较输出,再在多个 \(n\) 与密度下记录固定图批量时间、峰值内存和实际处理边数。循环版便于核验但解释器开销大;稠密版成本和内存随 \(n^2\) 增长;稀疏 scatter 通常适合 \(m\ll n^2\)⁠,但在很小或很密的图上未必最快。计时结论只适用于所用库、设备和规模。

  10. 各组使用相同的数据划分、随机种子、隐藏宽度、更新次数、批量处理方法与评价程序,并使用同一个验证集和相同标准选择模型。报告测试指标、训练时间、固定批量预测时间、峰值内存、消息函数运行次数、节点表示各维方差和不同节点表示的平均余弦相似度。比较有无自环时只改变自环设置,比较有无残差连接时只改变残差连接设置,并分别根据结果分析它们可能带来的影响。

    深度增加会扩大感受野和计算量;若表示方差下降、相似度上升且性能恶化,可作为过度平滑的证据,但不是唯一解释。sum 的尺度受节点度影响,mean 丢失计数信息;不同任务可能得到不同排序,不能从单个数据集推出普遍结论。