课程概述:图神经网络与ADMET预测

说实话,我第一次接触这个领域的时候,心里挺没底的。

那会儿是2019年,我接了一个项目,要预测某个候选分子的心脏毒性。传统方法试了一圈——定量构效关系(QSAR)、分子指纹、物理化学描述符——效果都不理想。数据量小,噪声大,模型动不动就过拟合。我当时就想,有没有什么办法,能把分子结构本身的信息保留得更完整一些?

后来我遇到了图神经网络。嗯,这就是我们今天要聊的东西。

为什么ADMET预测需要GNN?

你可能要问,传统的机器学习方法哪里不好?我简单说说我的体会。

  • 分子指纹的问题:指纹是一个固定长度的二进制向量,它把分子结构“拍扁”了。拍扁之后,原子之间的连接关系、空间构型都丢了。说白了,你给模型看的是一张“零件清单”,而不是“装配图纸”。
  • 描述符的局限:物理化学描述符(比如logP、分子量、氢键供体数)是人工设计的特征。但你想想看,人能想到的特征有限。很多微妙的立体效应、电子效应,人类根本定义不出来。
  • 数据稀疏性:ADMET数据通常很贵,一个化合物测几个性质就要花几万块。数据量少,模型就容易崩。传统方法在这个场景下特别脆弱。

我个人习惯把这个问题想成——“如何让模型理解分子”。

核心观点:分子的本质是一个图。原子是节点,化学键是边。把分子当作图来处理,而不是当作一串数字或者一个固定的向量,这本身就是更符合化学直觉的做法。

图神经网络能做什么?

图神经网络(GNN)是一种直接在图上做深度学习的框架。它不像卷积神经网络那样处理规则的网格,而是处理不规则的、拓扑结构复杂的数据。分子就是这种数据的一个典型例子。

我举个例子。

假设你要预测一个分子是否容易透过血脑屏障。传统做法是:

  1. 计算分子的指纹(比如ECFP4)
  2. 把指纹喂给随机森林或XGBoost
  3. 得到一个分类结果

GNN的做法是:

  1. 把分子表示成一个图,原子是节点,键是边
  2. 在图上做消息传递——每个原子会接收来自邻居原子的信息,更新自己的特征
  3. 经过几轮消息传递后,整个图会形成一个全局表示(图嵌入)
  4. 用这个图嵌入去做预测

关键区别在哪?

传统方法需要人手工设计特征。GNN自己学特征。而且,GNN能捕捉到分子的局部结构和全局性质——这是分子指纹做不到的。

一个小经验:我在2021年做过一个对比实验,用同一个数据集(约5000个化合物,预测CYP450抑制性),GNN模型的AUC比随机森林高出了大约0.08。这个提升在后来的外部验证集上也保持住了。并不是说GNN一定吊打传统方法,但它在分子理解上的优势确实存在。

下面这张图展示了GNN在ADMET预测中的基本逻辑。

图神经网络在ADMET预测中的基本流程 分子结构输入 分子图构建 GNN消息传递层 N层迭代 图读出 (Readout) 全局图嵌入 预测层 分类 / 回归 ADMET 预测结果 清除率 / 毒性 / 渗透性 关键说明 1. 分子图构建:将SMILES或3D结构转换为原子-键图,每个原子带特征向量。 2. GNN消息传递:通过聚合邻居信息更新节点特征,N层后感受野覆盖整个分子。 3. 图读出:将所有节点特征汇集成一个全局向量,用于下游预测任务。

这个课程会讲什么?

这个课程一共30章,我按照自己的实践路径把它分成了几个模块。

模块 章节范围 我打算讲什么
基础篇 第1-5章 GNN基本概念、分子图表示、消息传递原理、常用GNN架构
数据处理 第6-10章 分子数据处理、SMILES解析、特征工程、数据增强、不平衡处理
模型实战 第11-20章 GNN模型搭建、训练、调优、集成、迁移学习、多任务学习
ADMET专讲 第21-25章 具体ADMET性质预测案例——清除率、CYP抑制、hERG毒性、渗透性、血浆蛋白结合
进阶与部署 第26-30章 可解释性、模型压缩、ONNX部署、工业级流水线、失败案例复盘

提前提醒一句:这个课程不是纯理论的。我们会有代码、有数据、有踩坑记录。我曾经在一个项目里花了三周时间调GNN的层数,结果发现问题是分子图构建时原子特征归一化做错了。嗯,这种坑我会尽量帮你提前排掉。

这个模块的核心知识点

我把今天的内容整理成了一张表格,方便你回顾。

知识点 我的理解
ADMET预测的痛点 数据稀疏、结构表示丢失、人工特征设计成本高
分子图的本质 原子是节点,键是边,图结构保留了拓扑信息
GNN的核心流程 图构建 → 消息传递 → 读出 → 预测
为什么GNN更适合分子? 自动学习特征、保留局部与全局结构、对非欧几里得数据友好
课程路线图 基础 → 数据 → 模型 → ADMET专讲 → 进阶部署

一个小作业(自己练手)

如果你手头有分子的SMILES数据,可以试着做一件事:

# 用RDKit把SMILES转成图结构
from rdkit import Chem
from rdkit.Chem import Draw

smiles = "CC(=O)Oc1ccccc1C(=O)O"  # 阿司匹林
mol = Chem.MolFromSmiles(smiles)

# 看看原子和键的数量
num_atoms = mol.GetNumAtoms()
num_bonds = mol.GetNumBonds()
print(f"原子数: {num_atoms}, 键数: {num_bonds}")

# 可视化分子
img = Draw.MolToImage(mol)
# 存成图片或者直接显示

这个练习的目的,是让你直观感受“分子怎么变成一个图”。
我记得我第一次跑这个脚本的时候,看到阿司匹林分子被拆成原子和键的那个瞬间,才真正理解了什么叫“图是分子的自然表示”。

避坑指南

  • 不要直接把SMILES字符串当输入给GNN。我犯过这个错误。SMILES是线性序列,图是拓扑结构,两者不匹配。一定要构建成图。
  • 注意原子特征的标准化。不同分子的原子数不同,但特征向量维度要统一。我曾经因为漏掉氢原子,导致模型对分子大小的敏感度完全不对。
  • 图的连通性检查。有些分子在SMILES转化时会丢失一些信息,比如没有明确氢原子。建议在构建图之前,先Chem.SanitizeMol一下。

嗯,第一章就聊这么多。