新学期开始,班主任小林接到一个任务:快速判断新接手的每个班级是“优秀班”还是“普通班”。她拿到的不是成绩单,而是一张班级人际关系图——每个学生是一个圆点,学生之间谁和谁是好朋友就连一条线。小林逐个找学生谈话,再听他们聊起朋友的情况,慢慢掌握每个人的状态;接着把全班几十个人的信息汇总成一份班级总结,最后对照学校的评定标准给出结论。整个过程,就是人工智能里“图分类”的缩影:输入一整张图,输出一个类别标签。图分类被广泛用在分子毒性预测、蛋白质功能分类、程序图分析等真实工作中,本篇我们就沿着小林的评估流程,把图神经网络、图池化、分类器和数据增强这些概念一一讲透。

岗位关联

图分类是图神经网络最核心的应用之一。掌握从节点特征到图级特征的完整流程,你就能直接上手分子属性预测、蛋白质分类等真实业务,这是成为图学习方向工程师的必修课。

目录
  1. 可视化演示
  2. 图分类初识
  3. 图编码器GNN
  4. 图池化机制
  5. 分类器设计
  6. 数据增强
  7. 评估与调试
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示SSTF和FCFS两种磁盘调度算法的磁头移动过程,逐步演示磁头如何在各柱面间移动并计算总寻道距离。

本节配有交互动效(进入视口后自动加载,约 3KB)。

'+caps.sstf+'

图分类初识

小林站在教室门口,手里拿着一张画满圆点和连线的纸。这份“班级人际关系图”里,每个圆点是一个学生,每条线是一对好朋友。她要在不看成绩单的前提下,判断这个班级整体是“优秀班”还是“普通班”。这就是图分类的直观画面:输入一整张图,输出一个类别标签,而不是给某个学生单独下结论。传统机器学习处理的是“一行数据一个样本”,而图分类的样本是“一整张关系网”。

在数学上,一张图写作 $\mathcal{G}=(\mathcal{V},\mathcal{E})$,其中 $\mathcal{V}$ 是节点的集合(学生),$\mathcal{E}$ 是边的集合(朋友关系)。图分类的任务,就是学习一个映射函数,把整张图映射到有限的类别集合上,比如分子是否有毒(有毒/无毒)、蛋白质承担什么功能(催化/运输/免疫等)。它与我们熟悉的节点分类有本质区别:节点分类是评定“某个学生”的好坏,图分类是评定“整个班级”的风气;前者只看局部,后者必须把握全局。

对分子而言,节点是原子,边是化学键;对班级而言,节点是学生,边是朋友关系。小林看懂了这张图,但她不可能一个个记住所有学生——她需要一套系统的方法,先从每个学生入手,再整合成全班的判断。这套方法的第一环,就是图编码器 GNN。

f:G=(V,E)→{1,2,…,C}\displaystyle f:\mathcal{G}=(\mathcal{V},\mathcal{E})\rightarrow\{1,2,\dots,C\}
$f$ 是分类器,它把整张图映射到 $C$ 个离散标签之一。
  1. 图结构图由节点集和边集组成,$\mathcal{V}$ 存放所有原子(学生),$\mathcal{E}$ 记录它们之间的连接关系(化学键、好友关系)。
    G=(V,E)\displaystyle \mathcal{G}=(\mathcal{V},\mathcal{E})
  2. 映射记号箭头表示“输入图,输出标签”,$\rightarrow$ 左边是输入,右边是输出空间。
    f:G→{… }\displaystyle f:\mathcal{G}\rightarrow\{\dots\}
  3. 类别空间大括号里有 $C$ 个取值,$1,2,\dots,C$ 代表不同的类别编号,例如 $0$=无毒、$1$=有毒。
    {1,2,…,C}\displaystyle \{1,2,\dots,C\}

图编码器GNN

小林开始逐个找学生谈话。她发现一个聪明的办法:不只问学生本人的情况,还要问“你和谁最要好?他们是什么样的人?”朋友勤奋,他多半也受感染;朋友散漫,他多少会被影响。于是每谈完一个学生,她就在笔记本上更新一次对这位学生的认识——这轮谈话中获得的“朋友的情报”,会融入下一位学生的档案里。在 GNN(图神经网络)中,这个动作叫消息传递。

消息传递是 GNN 的灵魂:每一层网络让每个节点“收集”邻居节点的特征,再和自己的特征融合,生成新的节点表征。就像小林组织全班讨论:第一轮,每个学生只了解同桌;第二轮,通过同桌的了解延伸到朋友的朋友;若干轮之后,每个学生的档案里其实已经包含了整个班级的间接信息。这些携带全局信息的节点特征,就是后续图分类的基础。

经过 $K$ 轮消息传递,每个节点都拥有了包含全局视野的表征。但对小林来说,她要评定的是整个班级,而不是某几个学生。几十份学生档案摊在桌上,怎么浓缩成一句话?这正是下一节“图池化”要做的事。

hv(k)=σ(W(k)∑u∈N(v)hu(k−1))\displaystyle \mathbf{h}_v^{(k)}=\sigma\left(\mathbf{W}^{(k)}\sum_{u\in\mathcal{N}(v)}\mathbf{h}_u^{(k-1)}\right)
在第 $k$ 轮,节点 $v$ 把邻居们的旧表征求和,经过线性变换与激活函数,得到自己的新表征。
  1. 节点表征$\mathbf{h}_v^{(k)}$ 是节点 $v$ 在第 $k$ 轮更新后的特征向量,可以理解为小林笔记本上关于学生 $v$ 的“最新档案记录”。
    hv(k)\displaystyle \mathbf{h}_v^{(k)}
  2. 邻居集合$\mathcal{N}(v)$ 是节点 $v$ 的所有邻居,求和符号 $\sum$ 表示把这些邻居的档案逐项加起来,相当于小林综合了所有朋友的情报。
    ∑u∈N(v)hu(k−1)\displaystyle \sum_{u\in\mathcal{N}(v)}\mathbf{h}_u^{(k-1)}
  3. 线性变换$\mathbf{W}^{(k)}$ 是第 $k$ 层可学习的权重矩阵,它决定“哪些情报更重要”,就像小林判断“乐天派的话可信、暴躁鬼的话要打折扣”。
    W(k)(… )\displaystyle \mathbf{W}^{(k)}\left(\dots\right)
  4. 激活函数$\sigma$ 是激活函数(如 ReLU),把加权结果压缩到合理范围,防止数值无限膨胀,相当于在档案上盖一个“规范化”的章。
    σ(… )\displaystyle \sigma(\dots)

图池化机制

谈话结束了,小林面对厚厚一摞学生档案,却犯了难:评“优秀班”需要一个整体结论,不能把几十份档案直接堆给校长。她灵机一动,把每份档案里的关键信息摘出来,按“学习态度、互助氛围、纪律表现”几个维度做平均,最后汇成一张一页纸的《班级总结报告》。这个“从几十个学生信息浓缩成一个图级向量”的操作,就是图池化(也叫 Readout)。

最常用的池化是平均池化:把所有节点的表征逐维度求平均,得到一个固定长度的向量。此外还有求和池化(适合保留“总量”,比如分子总原子数影响毒性)和最大池化(适合保留“最突出的信号”,比如某个异常原子)。进阶方法还有注意力池化(让模型自己决定哪些节点更重要)和层次池化(像合并同类项一样逐步把班级分成小组再汇总)。它们的共同目标是:把变长数量的节点特征,压缩成固定长度的图级特征。

班级总结报告出炉,小林接下来要做的就是对照学校的评定标准给出结论。这套“评定标准”从哪来?靠数据学出来——这就是分类器的职责。

hG=1∣V∣∑v∈Vhv(L)\displaystyle \mathbf{h}_{\mathcal{G}}=\frac{1}{|\mathcal{V}|}\sum_{v\in\mathcal{V}}\mathbf{h}_v^{(L)}
将所有节点的最终表征取平均,得到表示整张图的向量 $\mathbf{h}_{\mathcal{G}}$。
  1. 图级向量$\mathbf{h}_{\mathcal{G}}$ 是代表整张图的向量,相当于那份《班级总结报告》,它不关心班级有 30 人还是 50 人,输出维度始终固定。
    hG\displaystyle \mathbf{h}_{\mathcal{G}}
  2. 求和取平均$\frac{1}{|\mathcal{V}|}\sum_{v\in\mathcal{V}}$ 先对所有节点的表征求和,再除以节点总数,得到“平均水平”,就像把全班成绩加总再除以人数得到平均分。
    1∣V∣∑v∈V\displaystyle \frac{1}{|\mathcal{V}|}\sum_{v\in\mathcal{V}}
  3. 最终表征$\mathbf{h}_v^{(L)}$ 是第 $L$ 层(最后一层)输出的节点表征,代表消息传递全部结束后每位学生的最新档案。
    hv(L)\displaystyle \mathbf{h}_v^{(L)}

分类器设计

小林手里有了《班级总结报告》,但“优秀班”和“普通班”的界限模糊得很。她决定用一个打分模型:把报告中的各项指标加权组合,经过层层提炼,最终算出“优秀班”的得分和“普通班”的得分,哪个高就选哪个。这个打分模型在深度学习里就是MLP(多层感知机),它由若干全连接层和激活函数堆叠而成,可以拟合非常复杂的评定规则。

为了让打分结果变成“概率”,模型最后接一个 Softmax 层:把两个原始得分归一化成和为 1 的概率,比如“优秀班 82%、普通班 18%”。训练时,我们用交叉熵损失衡量模型预测与真实标签的差距:如果真实标签是“优秀班”(one-hot 向量为 [1,0]),而模型预测 [0.82,0.18],损失就很低;预测偏了损失就高。通过梯度下降不断更新 MLP 的权重,模型慢慢学会像资深班主任一样精准定级。

有了分类器和损失函数,小林开始拿历史数据训练自己的评定能力。但训练样本总归有限,怎样让班主任在班型千奇百怪的情况下依然稳准狠?答案是给训练数据“添点花样”——数据增强。

L=−∑c=1Cyclog⁡(softmax(MLP(hG))c)\displaystyle \mathcal{L}=-\sum_{c=1}^{C}y_c\log\left(\mathrm{softmax}\left(\mathrm{MLP}\left(\mathbf{h}_{\mathcal{G}}\right)\right)_c\right)
交叉熵损失比较模型给每个类别的预测概率与真实标签的差距,差距越大,损失越大,模型就越受惩罚。
  1. 真实标签$y_c$ 是真实类别的 one-hot 编码,若真实类别是 $c$ 则 $y_c=1$,否则为 $0$,相当于“标准答案”。
    yc\displaystyle y_c
  2. 预测概率$\mathrm{softmax}(\mathrm{MLP}(\mathbf{h}_{\mathcal{G}}))_c$ 是模型预测该图属于类别 $c$ 的概率,$\log$ 对概率取对数便于计算。
    log⁡(softmax(… ))c\displaystyle \log\left(\mathrm{softmax}(\dots)\right)_c
  3. 求和惩罚$\sum_{c=1}^{C}$ 遍历所有类别,$y_c$ 为 $1$ 的那一项被保留,其余项为 $0$,所以损失只看“真实类别对应的预测概率”。
    −∑c=1Cyclog⁡(… )\displaystyle -\sum_{c=1}^{C}y_c\log(\dots)

数据增强

小林发现,自己见过的班级类型太少了:全是规规矩矩的环形座位分布。万一遇到一个“两派分裂”的班级,她可能误判。有经验的同事给她支招:把几个学生的座位调换一下(改变边),把档案上的措辞换种说法(扰动特征),甚至截取班级里的小圈子单独评审(子图采样)——这样就能变出大量“分身班级”,让小林在相似但略有不同的样本上反复练习,评定的稳定性大幅提高。这就是图数据增强的直觉。

图数据增强要格外小心:图的结构信息非常敏感,乱改边可能制造出毫无意义的样本。常用的安全策略有三类。特征扰动:给节点特征加少量高斯噪声,相当于评语微调,不改变图结构。边丢弃:以一定概率随机删掉边,模拟“某两个学生这学期不熟了”,让模型不那么依赖某条具体的边。子图采样:随机抽取图的一部分作为新样本,迫使模型学习局部与整体的关系。增强不是越多越好,力度过猛会把“优秀班”扭成“普通班”,反而伤害训练。

有了丰富的“分身班级”练手,小林的评定经验越来越足。但她怎么知道自己的评定水平到底如何?还需要一套客观的检验机制——评估与调试。

h~v=hv+εv,εv∼N(0,σ2I)\displaystyle \tilde{\mathbf{h}}_v=\mathbf{h}_v+\boldsymbol{\varepsilon}_v,\qquad \boldsymbol{\varepsilon}_v\sim\mathcal{N}(\mathbf{0},\sigma^2\mathbf{I})
给每个节点的特征向量叠加上一个服从高斯分布的小噪声,生成扰动后的新样本。
  1. 扰动后特征$\tilde{\mathbf{h}}_v$ 是加噪后的节点特征向量,它在原特征 $\mathbf{h}_v$ 的基础上产生微小偏移,相当于评语换了种说法但意思不变。
    h~v=hv+εv\displaystyle \tilde{\mathbf{h}}_v=\mathbf{h}_v+\boldsymbol{\varepsilon}_v
  2. 高斯噪声$\boldsymbol{\varepsilon}_v\sim\mathcal{N}(\mathbf{0},\sigma^2\mathbf{I})$ 表示噪声向量从均值为 $0$、方差为 $\sigma^2$ 的高斯分布中随机抽取,$\sigma$ 控制扰动强度,噪声太大就会掩盖真实信息。
    N(0,σ2I)\displaystyle \mathcal{N}(\mathbf{0},\sigma^2\mathbf{I})

评估与调试

期末到了,教导处拿出历年的真实班级档案(模型没见过的测试集),让小林重新评定一遍。她评对了多少个班,准确率一目了然。准确率是图分类最直观的指标:预测正确的样本数除以总样本数。但这里暗藏一个陷阱——如果 100 个班里只有 5 个优秀班,小林闭着眼睛全评“普通班”也能有 95% 准确率,这种虚高的分数在类别不平衡时毫无意义。这时要看每个类别的精确率和召回率,或者用 F1 值综合衡量。

训练过程中还要警惕过拟合:训练准确率一路飙升到 99%,测试准确率却卡在 70%,说明小林把历年档案背得滚瓜烂熟,却没有真正学会“评估班级的方法”。对策包括增加数据增强力度、加 Dropout、减小模型规模或提前停止训练。此外,可视化调试非常实用:用工具把图池化后的向量投影到二维平面,如果同类别样本聚成一团,说明模型学到了结构特征;如果杂乱无章,就要回头检查 GNN 层数或聚合方式。

教学评估结束,小林把整个流程总结成一张闭环图:读图(输入)→ 逐一了解学生(GNN 编码)→ 汇成报告(池化)→ 对照标准定级(分类器)→ 收集反馈优化(评估与调试)。这正是图分类模型从训练到部署的完整生命周期。

Acc=1N∑i=1N1(y^i=yi)\displaystyle \mathrm{Acc}=\frac{1}{N}\sum_{i=1}^{N}\mathbb{1}\left(\hat{y}_i=y_i\right)
准确率等于预测正确的样本数占总样本数的比例,$\mathbb{1}$ 是“条件为真则计 1”的指示函数。
  1. 样本总数$N$ 是测试集里图样本的总数,分母 $N$ 让准确率成为 $0$ 到 $1$ 的比例。
    1N∑i=1N\displaystyle \frac{1}{N}\sum_{i=1}^{N}
  2. 指示函数$\mathbb{1}(\hat{y}_i=y_i)$ 中,若第 $i$ 个图的预测标签 $\hat{y}_i$ 与真实标签 $y_i$ 相等则取 $1$,否则取 $0$,相当于“评对了记一分”。
    1(y^i=yi)\displaystyle \mathbb{1}\left(\hat{y}_i=y_i\right)

AI 应用场景

分子毒性预测

阶段:模型推理与决策

解决的问题:药物研发早期需要筛选海量化合物,传统做法是逐一做湿实验,耗时数年且成本高昂。用图分类模型把每个分子建模成原子-化学键的图,直接预测“有毒/无毒”,把候选化合物从几十万快速筛到几千。

具体案例:某制药公司收集了 3 万个小分子的化学结构图及实验毒性标签,训练一个 GCN+平均池化+MLP 的图分类模型,在外部测试集上达到 85% 的准确率,将初筛阶段的实验量减少了 70%。

蛋白质功能分类

阶段:特征提取与建模

解决的问题:蛋白质由氨基酸链折叠成三维结构,其功能类别与空间结构密切相关。把蛋白质残基看作节点、空间相邻关系看作边,构建蛋白质结构图,用图分类自动识别“酶/抗体/运输蛋白”等功能类别,替代昂贵且主观的人工比对。

具体案例:生物信息团队利用 AlphaFold 预测的蛋白质结构构建残基接触图,训练图分类模型对未知蛋白按 Gene Ontology 功能类别打标,辅助基因功能注释流水线,将注释效率提升约 40%。

程序图恶意代码检测

阶段:数据增强与鲁棒性优化

解决的问题:恶意软件常通过加壳、混淆逃避基于签名的检测。把程序的控制流图和数据依赖图作为输入,用图分类判断“恶意/正常”,并通过边丢弃、子图采样等增强策略提升对混淆变体的鲁棒性。

具体案例:安全团队对 1.2 万个程序样本构建控制流图,训练图分类模型识别勒索软件家族。配合边 Dropout 增强后,模型对未见过的混淆样本检出率从 68% 提升到 83%。

Python 演示

python103 行
import numpy as np  def make_circle(n):    """构造一类图:环形结构,每个节点度数为2"""    adj = np.zeros((n, n))    for i in range(n):        adj[i, (i + 1) % n] = 1        adj[(i + 1) % n, i] = 1    return adj  def make_two_clusters(n):    """构造另一类图:两个小团用一条边连接"""    adj = np.zeros((n, n))    half = n // 2    for i in range(n):        for j in range(n):            if i != j and ((i < half and j < half) or (i >= half and j >= half)):                adj[i, j] = 1    adj[half - 1, half] = 1    adj[half, half - 1] = 1    return adj  def build_dataset(n_samples, n_nodes=10):    """生成两类图样本:环形图标0,双团图标1"""    graphs, labels = [], []    for i in range(n_samples):        y = i % 2        adj = make_circle(n_nodes) if y == 0 else make_two_clusters(n_nodes)        adj = adj + np.eye(n_nodes)  # 加自环        x = np.ones((n_nodes, 1))    # 所有节点初始特征为1        graphs.append((x, adj))        labels.append(y)    return graphs, np.array(labels)  def relu(x):    return np.maximum(x, 0)  def softmax(z):    e = np.exp(z - np.max(z))    return e / np.sum(e)  class SimpleGNN:    """单层GCN + 平均图池化 + 线性分类器"""     def __init__(self, in_dim=1, hidden=16, out=2, lr=0.1):        self.W1 = np.random.randn(in_dim, hidden) * 0.2        self.W2 = np.random.randn(hidden, out) * 0.2        self.lr = lr     def forward(self, x, adj):        xp = adj @ x                        # 消息传递:聚合邻居特征        h = relu(xp @ self.W1)              # 全连接+激活        hg = h.mean(axis=0)                 # 平均图池化        logits = hg @ self.W2               # 分类打分        return logits, h, hg     def train_step(self, x, adj, y):        xp = adj @ x        h = relu(xp @ self.W1)        hg = h.mean(axis=0)        logits = hg @ self.W2        probs = softmax(logits)        dlogits = probs.copy()        dlogits[y] -= 1                     # 交叉熵梯度        dW2 = np.outer(hg, dlogits)        dhg = dlogits @ self.W2.T        dh = np.broadcast_to(dhg / h.shape[0], h.shape)        dh = dh * (h > 0)                   # ReLU 梯度        dW1 = xp.T @ dh        self.W2 -= self.lr * dW2        self.W1 -= self.lr * dW1     def predict(self, x, adj):        logits, _, _ = self.forward(x, adj)        return int(np.argmax(logits))  def main():    graphs, labels = build_dataset(200, n_nodes=10)    split = 150    model = SimpleGNN()    for epoch in range(300):        for g, y in zip(graphs[:split], labels[:split]):            model.train_step(g[0], g[1], int(y))        if epoch % 100 == 0:            train_acc = np.mean(                [model.predict(g[0], g[1]) == y for g, y in zip(graphs[:split], labels[:split])]            )            print("epoch {} train_acc {:.2f}".format(epoch, train_acc))    test_acc = np.mean(        [model.predict(g[0], g[1]) == y for g, y in zip(graphs[split:], labels[split:])]    )    print("test_acc {:.2f}".format(test_acc))  if __name__ == "__main__":    main()
目的:用 NumPy 从零实现一个可训练的简易 GNN,在合成的两类图(环形图 vs 双团图)上完成图分类,演示“消息传递 → 平均池化 → 分类器”的完整流程。
  1. adj = adj + np.eye(n_nodes)给邻接矩阵加自环,让节点在聚合时把自己的特征也算进去,这是 GCN 的常见技巧。
  2. xp = adj @ x矩阵乘法一次性完成所有节点的邻居特征聚合,是消息传递的向量化实现。
  3. hg = h.mean(axis=0)对所有节点特征取平均,实现全局平均池化,把图压缩成一个固定长度的向量。
  4. dlogits[y] -= 1交叉熵损失对 logits 的梯度推导结果,是反向传播的关键一步。
  5. np.broadcast_to(dhg / h.shape[0], h.shape)平均池化的梯度回传:把图级梯度均摊到每个节点上。
  6. dh = dh * (h > 0)ReLU 的梯度 mask,负区间梯度为 0,保证反向传播正确。

常见误区

误区图分类不就是把图中所有节点的预测结果做个投票吗?比如先预测每个节点属于哪类,再统计多数决定整张图的类别。

正解这是视角的根本错误。图分类是对整体结构的建模,就像班主任评定班级风气不能靠“把每个学生打晕后投票”——整体大于部分之和。分子是否有毒,并不取决于某个原子单独的性质,而是原子们连接成的拓扑模式。所以必须用池化把节点特征融合成全局表征,再交给分类器,而不是简单投票。

为何常见:初学者刚学完节点分类,自然想复用“逐点预测”的思路,忽略了图级任务需要全局信息整合的本质。

误区图池化就是求个平均或者求个和,所有节点一视同仁,反正最后都是个向量。

正解平均池化只是最简单的选择,且不一定好用。求和池化能保留图的大小信息(比如分子越大毒性可能越强),最大池化能捕捉最异常的节点信号,注意力池化则让模型学习“哪些节点对判定更关键”。就像写班级总结,有些调皮鬼的信息要弱化,班长的话要重点参考——选什么池化方式,本质是选择“如何压缩信息”,直接影响分类效果。

为何常见:看到 READOUT 函数长得很像“求和/平均”,就以为它只是简单统计,忽视了它承载的结构语义。

误区图像可以随便旋转、裁剪、加噪声,图数据也一样,随机乱删边、乱加边总能增加样本量。

正解图的结构是有语义的!分子图里删掉一条化学键,可能就从“无毒”变成“剧毒”。图增强必须保证语义基本不变:特征加噪要控制幅度,边丢弃要适度,子图采样要合理。这就像调换班级座位可以增加样本多样性,但如果把整个教室拆了重建,班级就不是原来的班级了。增强力度过猛,等于在训练数据里掺假。

为何常见:图像增强的成功经验容易让人惯性迁移,忽略了图和图像的本质差异——图的结构本身就是语义的一部分。

误区测试集准确率 95%,模型肯定很牛,可以放心上线了。

正解如果类别不平衡(比如 100 个分子里只有 5 个有毒),模型全预测“无毒”也有 95% 准确率,但一个毒药都筛不出来,毫无实用价值。必须同时看各类别的精确率/召回率,或使用 F1 值;还要对比训练集与测试集的差距判断是否过拟合。就像班主任评定 100 个班,如果他不分青红皂白全评“普通班”,准确率也能有 95%,但这显然不是好班主任。

为何常见:准确率是最直观的指标,人们容易只盯这一个数,忽略了数据分布对指标的扭曲。

面试题

Q1. 图分类任务与节点分类任务的核心区别是什么?

单选题 · easy

  • A.图分类输出的是节点类别,节点分类输出整图类别
  • B.图分类对整图输出标签,节点分类对单个节点输出标签← 正确答案
  • C.两者完全相同,只是叫法不同
  • D.图分类不需要图结构信息

答案:B。图分类把整张图当作一个样本,输出图的类别标签;节点分类关注图中某个节点的标签。B 正确描述了二者的区别。A 把两者说反了,C 忽略了粒度差异,D 错误——图分类恰恰依赖图结构。

Q2. 消息传递中聚合的对象是哪些节点?

单选题 · easy

  • A.图中所有节点
  • B.当前节点的所有邻居节点← 正确答案
  • C.当前节点自身
  • D.随机选择的节点

答案:B。消息传递的核心是聚合 $\mathcal{N}(v)$,即当前节点 $v$ 的邻居集合。就像小林找学生谈话时只参考朋友的情报,而非全班的每一个人。A 范围太大,C 忽略了邻居,D 不具备结构语义。

Q3. 下列哪些属于图分类的真实应用场景?

多选题 · easy

  • A.预测分子是否有毒性← 正确答案
  • B.对蛋白质功能进行分类← 正确答案
  • C.判断程序控制流图是否恶意← 正确答案
  • D.识别图片中猫和狗

答案:ABC。分子毒性预测、蛋白质功能分类、程序图恶意代码检测都能建模成“整图→标签”的问题。D 是图像分类而非图分类,图片是规则网格而非任意拓扑结构。

Q4. 平均图池化输出的特征向量有什么特点?

单选题 · mid

  • A.长度随图中节点数量变化
  • B.长度固定,且包含全图平均信息← 正确答案
  • C.只保留最大特征值
  • D.与节点顺序强相关

答案:B。平均池化把所有节点特征逐维求平均,输出向量的维度只取决于特征维度而非节点数量,因此长度固定,且对节点顺序不敏感。A 错误(长度不受节点数影响),C 是最大池化的行为,D 与池化的置换不变性矛盾。

Q5. Softmax 层在图分类模型中的作用是什么?

单选题 · mid

  • A.把特征映射成固定长度的图向量
  • B.把分类得分归一化成概率分布← 正确答案
  • C.在图结构中传播消息
  • D.增加训练样本数量

答案:B。Softmax 把 MLP 输出的原始得分转换成所有类别上求和为 1 的概率,便于用交叉熵损失训练。A 是池化的作用,C 是 GNN 的职责,D 是数据增强的功能。

Q6. 下列哪些是合理的图数据增强策略?

多选题 · mid

  • A.给节点特征添加小幅度高斯噪声← 正确答案
  • B.随机删除少量边构造扰动样本← 正确答案
  • C.随机抽取子图作为新训练样本← 正确答案
  • D.把图中所有边反向连接

答案:ABC。特征扰动、边丢弃、子图采样都是课堂介绍的安全增强策略,能在保持语义基本不变的前提下增加样本多样性。D 把所有边反向没有意义,因为图常视为无向,且可能破坏结构语义。

Q7. 交叉熵损失中,当模型对真实类别的预测概率越高,损失会怎样变化?

单选题 · mid

  • A.损失越大
  • B.损失越小← 正确答案
  • C.损失不变
  • D.损失随机波动

答案:B。交叉熵损失 $-\log(\hat{p}_c)$ 是减函数,真实类别的预测概率越高,负对数越小,损失就越低。这鼓励模型对正确类别给出更高概率,与“预测越准惩罚越小”的直觉一致。

Q8. 关于求和池化与平均池化的区别,下列说法正确的是?

单选题 · hard

  • A.求和池化对图的大小敏感,平均池化消除了大小影响← 正确答案
  • B.平均池化保留总能量,求和池化保留平均强度
  • C.求和池化的输出维度是平均池化的两倍
  • D.两者在数学上完全等价

答案:A。求和池化会把“节点数量”的信息编码进结果,图越大向量值越大;平均池化除以 $|\mathcal{V}|$ 后,反映的是平均强度而非规模。因此求和池化对图大小敏感,适合分子大小影响性质的场景。B 把两者说反,C 维度相同,D 显然错误。

Q9. 训练图分类模型时,要如何判断是否过拟合?

多选题 · hard

  • A.训练准确率远高于验证集准确率← 正确答案
  • B.验证损失先降后升,训练损失持续下降← 正确答案
  • C.训练集和验证集准确率都接近满分
  • D.增加数据增强后测试准确率明显提升

答案:AB。过拟合的典型信号是训练集与验证集表现差距拉大:训练准确率居高不下而验证准确率停滞或下降,验证损失曲线呈 U 形而训练损失一直降。C 说明模型拟合得很好不一定过拟合,D 只是说明增强有效。

Q10. 多层 GNN 堆叠后出现过平滑问题时,最直接有效的应对策略是?

单选题 · extreme

  • A.无脑增加 GNN 层数
  • B.减少层数或用残差连接与跳跃机制← 正确答案
  • C.把池化换成最大池化
  • D.删除所有邻居信息

答案:B。过平滑指层数过多后所有节点的表征趋于一致,丧失区分能力。减少层数、加残差连接或 JK-Net 等跳跃机制能让梯度与信息跨越较多层传播。A 会加剧过平滑,C 是池化策略与过平滑无关,D 破坏了消息传递的根基。