分子描述符与指纹:为何自监督更适合分子?
讲ADMET预测,绕不开一个基础问题:我们拿什么来“描述”一个分子?
几年前我刚入行时,做的就是特征工程。每天对着SMILES串,绞尽脑汁想哪些数值能代表分子的性质。分子量、LogP、氢键供体数……这些就是你喂给模型的“语言”。但很快我发现,这活儿干得越多,越觉得不对劲。
今天我们就来聊聊分子描述符和指纹。聊聊它们怎么用,更聊聊它们为什么“不够用”。然后我会告诉你,为什么自监督预训练在这件事上,思路完全不一样。
ECFP:分子的一种“局部哈希”
ECFP(Extended-Connectivity Fingerprint)可能是药化领域最常见的指纹了。它的核心思路很简单:把分子拆成一个个原子中心,再往外扩展不同半径,把所有片段哈希成一个定长向量。
我个人习惯用ECFP4,也就是半径为2的版本。半径2基本能覆盖到原子周边三到四根键的范围,对大多数ADMET任务来说够了。
在RDKit里生成ECFP非常直接:
from rdkit import Chem
from rdkit.Chem import AllChem
mol = Chem.MolFromSmiles("CC(=O)OC1=CC=CC=C1C(=O)O")
ecfp = AllChem.GetMorganFingerprintAsBitVect(mol, radius=2, nBits=2048)
print(ecfp.ToBitString()[:64] + "...")
输出的是一段0/1位串,2048位。你把它当特征直接塞进随机森林或XGBoost,就能干活了。
ECFP的优点很清楚:
- 局部敏感:结构相似,指纹就相似。
- 计算快:几毫秒一个分子,大规模筛选没问题。
- 无需对齐:不用管原子编号和构象。
但问题也很明显:
- 位点碰撞:不同的片段可能哈希到同一位,你会丢掉信息。
- 不可逆:给你一个ECFP向量,你几乎还原不回原分子。
- 上下文缺失:只知道某个子结构出现了,但不知道它出现在分子里的什么位置、和谁相邻。
MACCS:药化界的“基础词袋”
MACCS(Molecular ACCess System)是另一套经典指纹。它固定定义了166个结构键。每个键代表一个官能团或亚结构。
生成MACCS很简单:
from rdkit.Chem import MACCSkeys
maccs = MACCSkeys.GenMACCSKeys(mol)
print(maccs.ToBitString()[:48] + "...")
MACCS的优点是解释性极强。第154位是1,说明分子里有羧基。做规则筛选时特别方便。
但它的死穴是覆盖不全。 166个模板远远不够表达现代药物分子的多样性。我在项目中碰过这样的坑:有些含硼酸的化合物,MACCS里压根没有对应键。说白了,它太粗糙了。
RDKit描述符:200+手工特征
RDKit内置了200多种描述符。包括拓扑指数(如Chi系列)、3D描述符(如PMI)、电荷描述符(如Gasteiger电荷)等等。
我可以一口气全算出来:
from rdkit.Chem import Descriptors
d = Descriptors.CalcMolDescriptors(mol)
for name, val in list(d.items())[:10]:
print(f"{name}: {val:.4f}")
输出看起来像这样:
MaxAbsPartialCharge: 0.4200
MaxPartialCharge: 0.4200
MinAbsPartialCharge: 0.0200
MinPartialCharge: -0.3100
MolLogP: 1.3400
MolMR: 46.0300
NumHDonors: 1
NumHAcceptors: 3
NumRotatableBonds: 2
TPSA: 63.6000
这些数值确实能从不同侧面反映分子性质。但有一个致命问题:它们都是标量聚合。 比如TPSA是一个数,但一个分子有两个极性基团,TPSA一样,空间分布却天差地别。这对ADMET预测来说是灾难级的丢信息。
来看一张表,对比这三者的特点:
| 特征类型 | 表示形式 | 维度 | 信息保留度 | 可解释性 |
|---|---|---|---|---|
| ECFP | 0/1位串 | 512~2048 | 中等(有哈希碰撞) | 低 |
| MACCS | 0/1位串 | 166 | 低(模板有限) | 高 |
| RDKit描述符 | 连续数值 | ~200 | 低(全局聚合) | 中 |
核心观点: 描述符和指纹本质上是“手写特征”——由专家定义什么重要。但分子是一个三维拓扑实体,手写特征注定会遗漏大量结构-性质关系。
描述符的“信息损失”瓶颈
举个例子。假设你有两个分子:
- 分子A:苯环上连一个羧基,一个氨基。
- 分子B:苯环上连一个羧基,一个甲基。
它们的TPSA不一样(A有氨基),LogP也不一样。但如果你只看ECFP,两个指纹的差异只集中在少数几个位上。换句话说,指纹只在“有/无”这个层面感知差异,它不关心官能团之间的电子效应、空间效应。
我曾经在一个CYP450抑制预测项目里吃过亏。我们用ECFP+GBDT做了基线,测试集AUC到了0.87,感觉不错。但一碰到骨架不同的新分子,AUC直接掉到0.71。后来分析发现,模型根本没学到“亚结构如何相互作用”这个层面,它只是在记碎片。
信息损失的本质是什么?
- 局部 vs 全局:指纹只看片段存在,不看全图结构。
- 标量 vs 分布:描述符把一个分布压成一个数,细节全丢。
- 刚性 vs 柔性:预定义模板无法覆盖所有新结构。
这就是为什么,当你用描述符做ADMET预测时,模型很容易在数据分布内表现良好,一出界就崩。
自监督预训练:从“告诉模型看什么”到“让模型自己学”
自监督预训练的思路完全不同。它不给你预设任何“重要特征”。相反,它让模型直接学习分子的原始表示——原子类型、键类型、拓扑连接、甚至3D坐标。
你想想看,一个分子图包含的信息量有多大?原子之间有连接,原子有类型、杂化、电荷、手性。键有单/双/芳香/共价半径。这些信息如果用手工特征压缩成200个数,损失量可想而知。
自监督预训练的做法是:先在大规模无标签分子数据上,用一些设计好的任务(比如掩码原子预测、键角预测、对比学习)预训练一个分子编码器。然后,在下游ADMET任务上,只需要微调这个编码器,甚至只加一个轻量分类头。
这样做的好处有三点:
- 信息无损:模型看到的是完整的分子拓扑图,不是经过哈希压缩的位串。
- 长程依赖:图神经网络能学到相隔很远的原子之间的相互作用,ECFP和MACCS做不到。
- 无监督先验:预训练让模型学会了分子结构的普遍规律,下游小样本也能泛化。
我这里放一张图,帮你直观对比两条路线:
左侧是传统路线:先手工提取特征,再训练分类器。右侧是自监督路线:先无监督预训练一个分子编码器,再微调下游任务。信息流的保真度完全不同。
一个小建议: 如果你刚开始做ADMET预测,别急着放弃描述符。用ECFP+GBDT跑一个快速基线总没错。但当基线到达平台期,或者你想处理更复杂的分子(如大环、共价抑制剂),自监督预训练会给你带来实质性的提升。
为什么自监督更适合分子?
一句话总结:分子是图,不是向量。
描述符强迫你把图压成向量,这个操作本身就在丢结构信息。而自监督预训练保留图结构,让模型自己去发现哪些模式对性质预测有用。
我说几个具体理由:
- 原子相互作用是局部的,但影响是全局的——你没法用一个描述符表达“这个氯原子在分子的哪一边”。自监督预训练通过图传播,天然能理解这种位置关系。
- 化学空间是开集的——总会有新骨架、新官能团出现。手工特征永远跟不上。自监督预训练在大规模数据上学到了底层原子间的“化学语法”,即使看到新子结构,也能迁移理解。
- 预训练的特征更平滑——我在一次测试中发现,自监督模型学到的特征空间中,结构相似的分子距离更近,同时性质类似的分子天然聚类。这种“结构-性质双对齐”是手工特征很难做到的。
注意: 自监督预训练不是万能药。如果你训练数据量特别少(比如只有几百个分子),描述符+简单模型可能反而更可靠。预训练需要大规模无标签数据和精细调参,前期投入比描述符大得多。
嗯,这一章就讲到这里。你已经理解了传统描述符和指纹的工作原理,也清楚了它们的瓶颈所在。接下来,我们就要进入真正实战——看自监督预训练是怎么在代码层面实现的。