006

Stanford CS224W 图神经网络:为什么 GNN 是分子建模核心技术

CS224W 是理解图神经网络的最佳课程。这篇讲清课程重点、与分子建模的对应关系与学习建议。

Stanford CS224W(Machine Learning with Graphs)是图神经网络领域最系统的公开课程。对 AI 药物发现而言,它的价值在于讲清了「为什么图这种结构需要专门的方法」——而分子本质上就是图。

课程结构与药物发现的对应

课程主题 在药物发现中的对应 重要度
图的表示与特征 分子图的原子/键特征
传统方法(节点度、中心性) 拓扑描述符
消息传递与 GNN 分子性质预测的核心 最高
GNN 的表达能力(WL 测试) 理解 GNN 的能力上限
图池化与读出 从原子表示到分子表示
知识图谱嵌入 药物重定位(见 157《AI DTI 预测模型》
图生成 分子生成(见 158《AI 分子生成模型》
大规模图 生物网络分析 低(分子图很小)

最需要理解的三个概念

# ---- 概念一:消息传递 ----
#
#   每个节点从邻居收集信息并更新自己
#   经过 K 层,每个节点包含 K 跳邻域的信息
#
#   【对分子的含义】:
#     一个原子的表示,编码了它周围 K 个键范围内的化学环境
#     → 这与 ECFP 指纹的半径概念【几乎等价】(见 046)
#
#   【关键推论】:
#     3 层 GNN ≈ ECFP(r=3) 的信息范围
#     → 这解释了为什么两者性能常常接近(见 159)
#
# ---- 概念二:表达能力与 WL 测试 ----
#
#   Weisfeiler-Lehman 图同构测试:
#     一个判断两个图是否同构的经典算法
#     它的过程与消息传递惊人地相似
#
#   【重要结论】:
#     标准的消息传递 GNN 的判别能力
#     【不超过 1-WL 测试】
#     → 存在不同的分子图,GNN 无法区分
#
#   【实际含义】:
#     某些环系结构的差异,GNN 「看不见」
#     → 这是架构层面的限制,加数据也解决不了
#     → 应对:加入额外特征(环信息、子结构计数)
#
#   【GIN 的意义】:
#     它是能达到 1-WL 上限的架构
#     → 这是它在分子任务上常常表现好的理论依据
#
# ---- 概念三:过平滑 ----
#
#   层数增加时,所有节点的表示趋于相同
#   → 因为反复平均邻居信息,差异被抹平
#
#   【实际含义】:
#     GNN 通常只能做 3~5 层
#     → 感受野受限
#     → 难以捕捉分子内的长距离相互作用
#
#   应对:残差连接、跳跃连接、虚拟节点

与分子建模的差异:需要意识到的

  • 分子图很小:通常几十个原子,而课程中很多内容针对的是社交网络这类百万节点的大图——可扩展性的讨论对分子不那么重要
  • 分子图有强的化学约束:价键规则、环的性质——通用图方法不知道这些,需要通过特征工程注入
  • 三维信息:课程主要讲拓扑图,而分子的三维构象常常重要——需要额外学等变网络(见 160《等变神经网络》);
  • 数据量:分子数据集通常只有几千个样本,而课程中的方法很多是为大数据设计的——过拟合是分子任务的主要挑战
  • 基线的重要性课程不会告诉你「ECFP + 随机森林常常打平 GNN」——这个实践认识需要另外补(见 131《Chemprop / D-MPNN 论文精读》)。

学习建议

# 【不需要看完整个课程】
#
# 对药物发现,重点是:
#   □ 图的基本概念与表示
#   □ 消息传递框架(【最核心】)
#   □ GCN、GAT、GIN 的差异
#   □ 表达能力与 WL 测试
#   □ 图池化
#   □ 图生成(如果要做分子生成)
#
# 可以跳过或略读:
#   - 大规模图的扩展性
#   - 社交网络特有的任务
#   - 推荐系统
#
# 【配套实践】:
#   课程的 Colab 作业用 PyTorch Geometric
#   → 【这正是分子 GNN 最常用的库】(见 176)
#   → 做完作业就掌握了实现能力
#
# 【学完之后的练习】:
#   1) 用 PyG 实现一个分子性质预测的 GNN
#   2) 【与 ECFP + LightGBM 基线比较】
#   3) 试着加入 RDKit 全局描述符,看提升多少
#   4) 用骨架划分评测,看与随机划分的差距
#   → 这四步做完,你对分子 GNN 的理解
#     会超过大部分只读论文的人

用 PyTorch Geometric 做分子图

from torch_geometric.data import Data
from rdkit import Chem
import torch

def mol_to_pyg(smiles, y=None):
    mol = Chem.MolFromSmiles(smiles)
    if mol is None:
        return None

    # 原子特征 —— 【特征设计比架构更影响结果】
    x = []
    for a in mol.GetAtoms():
        x.append([
            a.GetAtomicNum(),
            a.GetDegree(),
            a.GetFormalCharge(),
            int(a.GetHybridization()),
            int(a.GetIsAromatic()),
            a.GetTotalNumHs(),
            int(a.IsInRing()),
            int(a.GetChiralTag()),
        ])

    # 边 —— 【无向图必须存两个方向】
    ei, ea = [], []
    for b in mol.GetBonds():
        i, j = b.GetBeginAtomIdx(), b.GetEndAtomIdx()
        f = [int(b.GetBondTypeAsDouble() * 2),
             int(b.GetIsConjugated()),
             int(b.IsInRing()),
             int(b.GetStereo())]
        ei += [[i, j], [j, i]]
        ea += [f, f]

    return Data(
        x=torch.tensor(x, dtype=torch.float),
        edge_index=torch.tensor(ei, dtype=torch.long).t().contiguous(),
        edge_attr=torch.tensor(ea, dtype=torch.float),
        y=torch.tensor([y], dtype=torch.float) if y is not None else None,
    )

# 【常见错误】:
#   1) 忘记双向边 → 消息只单向传递
#   2) 原子特征用原始数字而非独热编码
#      → 模型会以为「碳(6) 与氮(7) 相邻」有数值意义
#   3) 忘记处理 mol 为 None 的情况

关键要点

  • K 层 GNN 的信息范围与 ECFP 半径 K 几乎等价——这是理解两者性能接近的关键;
  • 标准 GNN 的判别力不超过 1-WL 测试——某些分子图它「看不见」差异;
  • 过平滑限制了层数(3~5 层),难以捕捉长距离相互作用;
  • 课程不会讲「ECFP 基线常常打平 GNN」——这个实践认识必须另外补

延伸资源