分子表示方法:SMILES、分子图、指纹
各位,咱们直接进入正题。这一章讲分子表示,说白了就是怎么把化学结构喂给深度学习模型。你想想看,模型又不懂苯环、羧基这些概念,它只认数字。所以分子表示是桥梁,也是瓶颈。我在调批次校正模型时,遇到的最大坑往往不是模型结构不行,而是分子表示这一步就没对齐。
今天咱们聊三种最主流的表示方法:SMILES、分子图、指纹。每种方法都有它的脾气,也有它适合的场景。我个人习惯是先搞清楚数据集的特性,再决定用什么表示。别盲目跟风。
SMILES:最直观的字符串表示
SMILES(Simplified Molecular Input Line Entry System)是用ASCII字符表示分子结构。比如乙醇就是CCO,苯是c1ccccc1。看起来很简单,对吧?
但这里有个大坑:同一个分子可以有多个不同的SMILES字符串。比如水可以是O,也可以是[H]O[H]。这对深度学习模型来说很要命——它会以为这是两个不同的分子。
我建议在预处理阶段,必须对SMILES做标准化(canonicalization)。RDKit提供了标准化的API:
from rdkit import Chem
mol = Chem.MolFromSmiles(smiles)
canonical_smiles = Chem.MolToSmiles(mol, canonical=True)
这一行代码,能避免很多批次校正中的误判。我在一个项目中遇到过,同一个分子因为SMILES写法不同,模型给出了截然不同的ADMET预测。排查了大半天才发现是SMILES不统一的问题。
SMILES作为深度学习输入时,通常会用tokenization+embedding的方式处理。你可以把它看成NLP任务:把SMILES字符串切分成原子符号、键类型、环标记等token,然后输入RNN或Transformer。
# 简单示例:SMILES tokenization
smiles = "CCO"
tokens = ['C', 'C', 'O']
# 然后映射成one-hot或embedding向量
不过要注意,SMILES的长度差异很大。短的像水只有1个字符,长的蛋白质配体可能有几百个字符。这就需要对序列做padding或截断。我在做批次校正时,一般会设定最大长度128,超出部分直接截断。
分子图:保留拓扑结构
SMILES虽然直观,但它丢掉了分子的空间拓扑信息。你想,环丙烷和丙烯的SMILES都是C1CC1,但一个是环,一个是开链,化学反应性完全不同。
分子图(Molecular Graph)就是为了解决这个问题。它把原子看成节点(node),化学键看成边(edge)。这种表示天然保留了三环、芳香性这些结构特征。
图神经网络(GNN)现在很火。我常用的是消息传递神经网络(MPNN)。简单说,每个节点会接收邻居节点的信息,然后更新自己的特征向量。经过多轮传递,整个分子图就被编码成固定长度的向量。
以下是分子图的邻接矩阵和特征矩阵生成示例:
from rdkit import Chem
from rdkit.Chem import AllChem
mol = Chem.MolFromSmiles("CCO")
# 原子特征:每个原子的one-hot
atom_features = []
for atom in mol.GetAtoms():
# 原子类型、度、氢原子数、形式电荷等
feature = [atom.GetAtomicNum(), atom.GetDegree(),
atom.GetTotalNumHs(), atom.GetFormalCharge()]
atom_features.append(feature)
# 邻接矩阵:键的索引
adj = Chem.rdmolops.GetAdjacencyMatrix(mol)
print(atom_features)
print(adj)
输出示例:
[[6, 2, 3, 0], [6, 3, 2, 0], [8, 1, 1, 0]]
[[0, 1, 0],
[1, 0, 1],
[0, 1, 0]]
分子图的好处是鲁棒性强。化学结构决定了图的拓扑结构,这个不会变。批次校正时,不同批次的分子图结构是一致的,不会因为SMILES的写法差异而改变。我个人很推荐在ADMET预测这种对分子结构敏感的任物中使用分子图。
不过也要小心:图神经网络的训练速度比序列模型慢不少。还有一个问题是,分子图的节点顺序是随机的。同一个分子,原子编号不同,生成的邻接矩阵就不同。这会导致模型学习不一致。我建议在数据加载时固定原子编号顺序,比如按原子序数排序。
指纹:高维稀疏向量
指纹(Fingerprint)是一种传统的分子表示方法。它用一个二进制位向量来编码分子的结构信息。比如Morgan指纹,它枚举分子中每个原子周围一定半径内的子结构,然后通过哈希函数映射到位图上。
优点很明显:计算快,解释性好。缺点也明显:维度高,稀疏。常见的Morgan指纹长度是2048或4096位,但大部分位是0。
指纹在深度学习中的使用方式很直接:直接作为全连接层的输入。代码示例:
from rdkit import Chem
from rdkit.Chem import AllChem
mol = Chem.MolFromSmiles("CCO")
# 生成Morgan指纹
fp = AllChem.GetMorganFingerprintAsBitVect(mol, radius=2, nBits=2048)
# 转为numpy数组
import numpy as np
arr = np.array(fp)
print(arr.shape) # (2048,)
指纹向量可以做批次校正吗?当然可以。不过指纹对批次差异的敏感程度不如SMILES和图。因为指纹是子结构的集合,子结构打乱顺序不影响向量。但也有问题:不同批次的同一个分子,指纹完全一致。但如果批次间存在数据污染(比如溶剂残留),指纹会发生变化,这就帮我们发现批次漂移。
我记得有个项目,我们同时用了指纹和分子图作为输入,发现指纹对水的信号很敏感——不同实验室合成的水,指纹居然有差异。后来一查,是溶剂峰没洗干净。指纹意外成了质控指标。
三种表示方法的对比
| 表示方法 | 编码特点 | 对批次差异敏感度 | 训练速度 |
|---|---|---|---|
| SMILES | 序列,存在多种写法 | 高(需要标准化) | 较快 |
| 分子图 | 拓扑结构,排列不变 | 低(结构确定) | 慢 |
| 指纹 | 稀疏位向量,子结构枚举 | 中(溶剂敏感) | 最快 |
实际项目中,我通常不会只用一种。如果是做高通量筛选,指纹足够快;如果是做精确ADMET预测,分子图更可靠;如果要做文本多任务学习,SMILES更能与其他任务对齐。
我的个人经验:如果你在做批次校正,强烈建议同时用分子图和SMILES做对比实验。分子图在批次间一致性上好,但SMILES在捕捉批次特定的噪声模式上更直接。两者互补。
SVG图:三种分子表示与批次校正的衔接
下面这张图展示了三种表示方法如何输入深度学习模型,并如何影响批次校正模块。注意看信息流从分子结构到嵌入表示,再到批次校正层的路径。
关键原则:无论你用哪种分子表示,批次校正的核心是确保“同分子跨批次”的特征表达一致。如果分子表示本身存在批间差异(比如SMILES标准不同),那么批次校正就变成了费力不讨好的事后修补。
避坑指南:我曾经因为分子图的消息传递步骤没对齐,导致不同批次的分子图嵌入向量分布不统一。后来才发现,图神经网络的消息传递次数(num_layers)设置不当,浅层和深层结构无法共享。建议固定消息传递层数,并在批次校正数据增强时保持图拓扑不变。
最后说一句:分子表示没有银弹。SMILES灵活,分子图精准,指纹高效。你的任务是什么?如果只是简单的分类任务,指纹够用;如果是多任务预测,分子图更稳定;如果涉及序列生成(比如逆合成),SMILES不可替代。嗯,把每种表示的特性吃透,之后调批次校正才有把握。