分子表示方法:SMILES、InChI、SELFIES、分子图表示
做ADMET预测的第一道坎,往往不是模型选什么,而是分子长什么样。数据进了模型,模型得看懂分子式、官能团、三维构象——但它只能吃数字。所以我们需要一种分子表示,把化学结构变成字符串或图,喂给神经网络。
我个人接触过的分子表示不下十种,但真正在工业界站稳脚跟的就四个:SMILES、InChI、SELFIES、分子图。它们各有脾气,用不对地方,模型精度直接掉十个点。今天我们就一个个拆开看。
4.1 SMILES:最老牌,坑也最多
SMILES(Simplified Molecular Input Line Entry System)是1988年提出来的。说白了,就是把分子结构压成一行字符串,用原子符号、分支括号、环标号来表示连接关系。
举个最简单的例子:乙醇(C₂H₅OH)写成 CCO。没错,SMILES里氢原子通常是隐式的,只写重原子骨架。
- 苯:
c1ccccc1或C1=CC=CC=C1 - 手性中心:用
@或@@表示方向 - 芳香键:小写字母(
c)代表芳香碳
我在项目中遇到过一个大坑:同一分子的SMILES写法不唯一。比如2-丁醇可以写成CCC(C)O,也可以写成CC(C)CO,甚至带立体化学标记的版本。如果数据来源不同,模型就会以为这是两个分子。
CanonicalSMILES,不同版本的RDKit产生的正规化结果也可能不同。建议在预处理时统一用同一工具、同一版本生成CanonicalSMILES,并在训练/推理流程中完全一致。
另外一个问题是:SMILES对无效语法几乎无防御。你随手写个CC(C,括号不闭合,解析器就崩了。我曾经在线上推理时因为一条脏数据导致整个批处理失败,排查了两小时才发现是SMILES里多了一个左括号。
4.2 InChI:标准化之王,但太啰嗦
InChI(International Chemical Identifier)是IUPAC搞的标准化方案。它分成很多层:主层、电荷层、立体化学层、同位素层……每层用 / 分隔。
比如乙醇的InChI:
InChI=1S/C2H6O/c1-2-3/h3H,2H2,1H3
你看,它显式写出了所有原子和氢(C2H6O),再用 /c 开头描述连接图,/h 描述氢原子位置。比起SMILES,它更完整,但也更冗余。
InChI的杀手锏是标准化。同一分子无论从什么途径生成,只要用同一InChI版本,结果一定是唯一的。这对数据库去重、分子精确匹配非常有用。
但InChI不适合直接作为模型输入。为什么?它太长,变化维度高,而且把氢原子展开后特征空间剧增。我尝试过把InChI当字符串用LSTM训练一个ADMET分类器,效果远不如SMILES。
还有一个细节:InChI有固定长度限制吗?没有理论上限,但实际中多环大分子可能超过2000字符。你想想看,给模型喂这么长的序列,训练效率能好到哪去?
4.3 SELFIES:为机器学习而生的字符串
SELFIES(Self-Referencing Embedded Strings)是2019年由马里奥·克雷恩等人提出的。它专门为了解决SMILES的两大痛点:语法鲁棒性和表示唯一性。
SELFIES的核心思想是自引用:每个符号不仅表示原子或键,还隐含了下一步可以连接的符号类型。所以无论你生成什么字符串,解析器总能给出一个合法的分子(哪怕分子在化学上不稳定)。举个例子:
乙醇的SELFIES表示:
[C][C][O]
注意它是用方括号包围每一个原子,并用.或]跟上键信息。具体语法比这个复杂,但关键点在于:你不可能写出像 CC(C 这样的非法序列。
我记得第一次在项目中测试SELFIES生成时,故意插了几个随机字符,结果解析器依然输出了一个分子图——虽然是个奇怪的自由基,但至少没崩。对于管道中可能出现的脏数据,这简直救命。
| 特性 | SMILES | SELFIES |
|---|---|---|
| 语法鲁棒性 | 低(非法字符串直接报错) | 高(任何字符串都能解析) |
| 唯一性 | 不保证(需规范化) | 保证(每个分子只有一个SELFIES) |
| 可读性 | 高(专家可一眼读出结构) | 中等(需要熟悉符号) |
| 模型友好度 | 一般(需要预处理) | 好(适合直接做序列模型) |
不过SELFIES也有短板:它生成的字符串通常比SMILES长。比如乙醇SMILES是CCO,SELFIES是[C][C][O],长度翻倍。对于大分子,序列长度会明显增长。另外,SELFIES目前社区工具不如SMILES成熟,RDKit虽然支持(自2019年后),但自定义算子可能不如SMILES丰富。
4.4 分子图表示:最自然,模型最爱的数据结构
说完字符串,聊聊分子图。分子本身就是一张图:原子是节点,化学键是边。所以分子图表示是最直观的方式,也是当前GNN(图神经网络)做ADMET预测的主流输入。
一个标准的分子图包含:
- 节点特征:原子类型、杂化状态、形式电荷、在环中否、芳香性、氢原子数等。常用One-Hot编码或向量化特征。
- 边特征:键类型(单键、双键、三键、芳香键)、是否立体、是否在环中,有时还加上键长或键角。
- 全局特征(可选):分子量、LogP、环数等,可以通过全局池化加入。
举个例子,用RDKit生成一个分子的图结构:
from rdkit import Chem
from rdkit.Chem import AllChem
mol = Chem.MolFromSmiles('CCO')
# 原子特征:原子序数、形式电荷、杂化类型等
atom_feats = []
for atom in mol.GetAtoms():
feats = [
atom.GetAtomicNum(),
atom.GetFormalCharge(),
atom.GetHybridization(),
atom.GetIsAromatic(),
atom.GetDegree(),
atom.GetImplicitValence()
]
atom_feats.append(feats)
# 边特征:键类型、立体化学等
edge_list = []
edge_feats = []
for bond in mol.GetBonds():
u = bond.GetBeginAtomIdx()
v = bond.GetEndAtomIdx()
edge_list.append((u, v))
edge_feats.append([
bond.GetBondTypeAsDouble(),
bond.GetIsAromatic(),
bond.GetStereo()
])
注意:这里只是演示基本思路。实际项目中,特征工程需要精心设计。我建议把原子特征维度控制在50以内,边特征控制在10以内,否则GNN计算会变慢且容易过拟合。
4.5 四种表示方法的选择策略
下面这张图把四种表示方法的关系画清楚了:
概括一下我的选择攻略:
- 如果你用GNN做ADMET预测:首选分子图。把图结构作为模型输入,原子和键特征手工设计好,一般效果最好。
- 如果你用Transformer或RNN做序列预测:选SELFIES。它鲁棒性好,且唯一性保证免去了规范化烦恼。
- 如果你在做数据清洗、去重、数据库匹配:用InChI。标准化唯一,不怕同分异构体混淆。
- 如果只是快速查看结构:SMILES最直接,可读性强,但记得用CanonicalSMILES统一。
最后提醒一句:无论选哪种表示方法,一定要做一致性检查。我曾经遇到过一个数据集,部分分子用SMILES表示,部分用InChI,混合喂给模型后精度直接掉到随机水平。统一数据表示格式,是ADMET预测工程中第一件要做的事。