分子描述符与指纹:为何自监督更适合分子?

讲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。后来分析发现,模型根本没学到“亚结构如何相互作用”这个层面,它只是在记碎片。

信息损失的本质是什么?

  1. 局部 vs 全局:指纹只看片段存在,不看全图结构。
  2. 标量 vs 分布:描述符把一个分布压成一个数,细节全丢。
  3. 刚性 vs 柔性:预定义模板无法覆盖所有新结构。

这就是为什么,当你用描述符做ADMET预测时,模型很容易在数据分布内表现良好,一出界就崩。

自监督预训练:从“告诉模型看什么”到“让模型自己学”

自监督预训练的思路完全不同。它不给你预设任何“重要特征”。相反,它让模型直接学习分子的原始表示——原子类型、键类型、拓扑连接、甚至3D坐标。

你想想看,一个分子图包含的信息量有多大?原子之间有连接,原子有类型、杂化、电荷、手性。键有单/双/芳香/共价半径。这些信息如果用手工特征压缩成200个数,损失量可想而知。

自监督预训练的做法是:先在大规模无标签分子数据上,用一些设计好的任务(比如掩码原子预测、键角预测、对比学习)预训练一个分子编码器。然后,在下游ADMET任务上,只需要微调这个编码器,甚至只加一个轻量分类头。

这样做的好处有三点:

  • 信息无损:模型看到的是完整的分子拓扑图,不是经过哈希压缩的位串。
  • 长程依赖:图神经网络能学到相隔很远的原子之间的相互作用,ECFP和MACCS做不到。
  • 无监督先验:预训练让模型学会了分子结构的普遍规律,下游小样本也能泛化。

我这里放一张图,帮你直观对比两条路线:

传统管线 vs 自监督管线 输入分子(SMILES / 图) 手工特征提取 ECFP / MACCS / RDKit 描述符 ML模型(GBDT / RF) ADMET预测 ⚠ 痛点 信息损失严重 / 泛化差 需要手工调特征 输入分子(原子图 + 3D坐标) 自监督预训练 掩码原子 / 对比学习 / 构象预测 GNN编码器(分子表征) ADMET预测(微调) ✅ 优势 信息保留完整 / 泛化强 自动学习分子规律

左侧是传统路线:先手工提取特征,再训练分类器。右侧是自监督路线:先无监督预训练一个分子编码器,再微调下游任务。信息流的保真度完全不同。

一个小建议: 如果你刚开始做ADMET预测,别急着放弃描述符。用ECFP+GBDT跑一个快速基线总没错。但当基线到达平台期,或者你想处理更复杂的分子(如大环、共价抑制剂),自监督预训练会给你带来实质性的提升。

为什么自监督更适合分子?

一句话总结:分子是图,不是向量。

描述符强迫你把图压成向量,这个操作本身就在丢结构信息。而自监督预训练保留图结构,让模型自己去发现哪些模式对性质预测有用。

我说几个具体理由:

  • 原子相互作用是局部的,但影响是全局的——你没法用一个描述符表达“这个氯原子在分子的哪一边”。自监督预训练通过图传播,天然能理解这种位置关系。
  • 化学空间是开集的——总会有新骨架、新官能团出现。手工特征永远跟不上。自监督预训练在大规模数据上学到了底层原子间的“化学语法”,即使看到新子结构,也能迁移理解。
  • 预训练的特征更平滑——我在一次测试中发现,自监督模型学到的特征空间中,结构相似的分子距离更近,同时性质类似的分子天然聚类。这种“结构-性质双对齐”是手工特征很难做到的。

注意: 自监督预训练不是万能药。如果你训练数据量特别少(比如只有几百个分子),描述符+简单模型可能反而更可靠。预训练需要大规模无标签数据和精细调参,前期投入比描述符大得多。

嗯,这一章就讲到这里。你已经理解了传统描述符和指纹的工作原理,也清楚了它们的瓶颈所在。接下来,我们就要进入真正实战——看自监督预训练是怎么在代码层面实现的。