分子表示方法: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,甚至带立体化学标记的版本。如果数据来源不同,模型就会以为这是两个分子。

注意:SMILES没有规范化的通用标准。即使使用RDKit的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更适合做分子匹配和数据库查重,不适合作为模型原始特征。如果非要用,可以提取InChI的子层(比如只取主层)或者用哈希映射压缩。

还有一个细节:InChI有固定长度限制吗?没有理论上限,但实际中多环大分子可能超过2000字符。你想想看,给模型喂这么长的序列,训练效率能好到哪去?

4.3 SELFIES:为机器学习而生的字符串

SELFIES(Self-Referencing Embedded Strings)是2019年由马里奥·克雷恩等人提出的。它专门为了解决SMILES的两大痛点:语法鲁棒性和表示唯一性。

SELFIES的核心思想是自引用:每个符号不仅表示原子或键,还隐含了下一步可以连接的符号类型。所以无论你生成什么字符串,解析器总能给出一个合法的分子(哪怕分子在化学上不稳定)。举个例子:

乙醇的SELFIES表示:
[C][C][O]

注意它是用方括号包围每一个原子,并用.或]跟上键信息。具体语法比这个复杂,但关键点在于:你不可能写出像 CC(C 这样的非法序列。

我记得第一次在项目中测试SELFIES生成时,故意插了几个随机字符,结果解析器依然输出了一个分子图——虽然是个奇怪的自由基,但至少没崩。对于管道中可能出现的脏数据,这简直救命。

SELFIES vs SMILES 核心对比
特性SMILESSELFIES
语法鲁棒性低(非法字符串直接报错)高(任何字符串都能解析)
唯一性不保证(需规范化)保证(每个分子只有一个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计算会变慢且容易过拟合。

避坑指南:我曾经在分子图中使用了太多冗余特征(比如把原子名称One-Hot到118维),结果模型训练时收敛极慢,而且对稀有元素(比如过渡金属)反而产生偏差。后来只保留了原子序数、度数、形式电荷、芳香性、环内标记,共5个特征,效果反而更好。记住:图特征不是越多越好。

4.5 四种表示方法的选择策略

下面这张图把四种表示方法的关系画清楚了:

分子表示方法知识体系 分子结构 SMILES InChI SELFIES 分子图 字符串型:适合序列模型(RNN/Transformer) 图型:适合GNN(GCN/GIN/GAT) 选择策略:根据模型架构和任务需求 优先级:分子图 > SELFIES > SMILES > InChI

概括一下我的选择攻略:

  • 如果你用GNN做ADMET预测:首选分子图。把图结构作为模型输入,原子和键特征手工设计好,一般效果最好。
  • 如果你用Transformer或RNN做序列预测:选SELFIES。它鲁棒性好,且唯一性保证免去了规范化烦恼。
  • 如果你在做数据清洗、去重、数据库匹配:用InChI。标准化唯一,不怕同分异构体混淆。
  • 如果只是快速查看结构:SMILES最直接,可读性强,但记得用CanonicalSMILES统一。
补充一点:在分子图表示中,原子编号和边顺序会影响GNN的排列不变性。建议在图卷积前对节点进行一次无关编号的归一化,或者直接使用对称不变的聚合函数(如sum、mean、max)。

最后提醒一句:无论选哪种表示方法,一定要做一致性检查。我曾经遇到过一个数据集,部分分子用SMILES表示,部分用InChI,混合喂给模型后精度直接掉到随机水平。统一数据表示格式,是ADMET预测工程中第一件要做的事。