四、分子表示:SMILES、InChI、分子指纹(Morgan、MACCS)

好,咱们进入第四章节。做AI药物发现,第一步要解决的问题很实在——怎么把分子“喂”给模型?

毕竟计算机看不懂结构式,它只认数字和字符串。所以分子表示法,说白了就是搭一座桥,把化学结构翻译成机器语言。

我个人认为,这块儿是整个Pipeline的地基。地基不稳,后面模型再花哨也白搭。

4.1 SMILES —— 最流行的“分子字符串”

SMILES,全称是简化分子线性输入规范。你把它当成分子的“身份证号”就行了。但它比身份证号灵活,同一个分子能写出好几种合法的SMILES。

举个例子——苯酚,它的SMILES可以是c1ccc(cc1)O,也可以是Oc1ccccc1。两个都对,但模型看了可能以为是两个东西。这就很头疼了。

避坑指南:我曾在项目里吃过这个亏。刚开始做虚拟筛选,直接把SMILES当唯一标识,结果同一个分子在不同数据库里跑出完全不同的ADMET性质。排查了好久才发现,是SMILES写法不同导致的。

那怎么办?标准做法是用规范化SMILES。把分子拿给RDKit洗一遍,统一成标准形式,再输入模型。一段简单的Python代码可以搞定:

from rdkit import Chem

smiles_list = ["c1ccc(cc1)O", "Oc1ccccc1"]
canonical = [Chem.MolToSmiles(Chem.MolFromSmiles(s)) for s in smiles_list]
print(canonical)  # 两个都变成 'c1ccc(cc1)O'

嗯,这样就保证唯一性了。

4.2 InChI —— 比SMILES更“硬核”的标准

SMILES虽然方便,但毕竟是人写的,难免有歧义。InChI呢?它是国际纯粹与应用化学联合会搞出的官方标准,用一套严格算法生成,保证全局唯一。

你看InChI长这样:

InChI=1S/C6H6O/c7-6-4-2-1-3-5-6/h1-5,7H

这里面包含了分子式、连接性、氢原子位置等信息。虽然读起来没SMILES直观,但在数据交换、专利检索、数据库去重这些场景,它的唯一性无可替代。

特性SMILESInChI
可读性高(人能看懂)低(全是代码)
唯一性需规范化严格保证
标准化多种方言IUPAC官方标准
转换速度快稍慢(算法复杂)
适用场景日常建模、训练数据去重、数据库

我的习惯:日常做模型训练,我优先用规范化的SMILES,速度快、够用。但在做数据清洗、合并多个数据库的时候,我一定会把InChI算出来,用它的哈希值做唯一键。省去很多麻烦。

4.3 分子指纹 —— 把分子变成机器学习能吃的向量

字符串表示,终究还是要人读的。模型最爱的,其实是定长数值向量。分子指纹就是干这个的。

你想象一下,一个分子可以用一串0和1来表示,每个位置对应某种结构片段或拓扑特征。两个分子的相似度,可以直接用向量的点积来算。这多方便!

4.3.1 Morgan 指纹

Morgan指纹,又叫ECFP(扩展连通性指纹)。它的核心理念是:从每个原子出发,逐层扩展邻域,生成圆形子结构的哈希。

参数就两个:半径(圈几层邻居)、位数(最终向量的长度)。半径2是默认的,也就是ECFP4。

from rdkit import Chem
from rdkit.Chem import AllChem

mol = Chem.MolFromSmiles("c1ccc(cc1)O")
fp = AllChem.GetMorganFingerprintAsBitVect(mol, radius=2, nBits=2048)
bits = list(fp)  # 得到一个长度为2048的0/1列表
print(bits[:20])  # 看看前20位是啥

为什么会用2048位?其实就是个折中。位太少,哈希碰撞多;位太多,内存开销大,而且稀疏度太高对模型也不友好。2048是行业里试出来的平衡点。

4.3.2 MACCS 指纹

MACCS指纹,也叫公共骨架指纹。它预定义了166个子结构特征,比如“是否有羧基”、“是否有苯环”等。一个分子如果含有某个特征,对应位就是1。

说实话,MACCS的化学意义更直接,但它有个硬伤——只覆盖166个特征,很多新颖的结构它抓不到。

特点Morgan (ECFP4)MACCS
特征类型自学习拓扑特征预定义结构特征
特征数量可调(如2048位)固定166位
对新结构的敏感度高(自动扩展)低(依赖模板)
可解释性中(子结构不直观)高(每个位对应已知基团)
计算速度中快

经验之谈:如果你在做虚拟筛选、相似性搜索或者QSAR建模,我建议优先试Morgan指纹。如果结果不好,再叠加上MACCS作为额外的特征。我曾在一个ADMET预测项目里,只用Morgan AUC是0.85,把两种指纹拼接起来,直接涨到0.91。有时候组合比单打独斗强。

图4-1:分子表示方法知识体系 分子表示 字符串表示 数值向量表示 结构标准标识 SMILES 优点:可读性强 缺点:需规范化去歧义 Morgan(ECFP) 优点:捕获拓扑特征 缺点:可解释性弱 MACCS 优点:化学可解释性 缺点:特征仅166个 InChI 优点:全局唯一标准 缺点:不易人工阅读 核心应用建议 日常训练用规范SMILES + Morgan指纹;数据去重用InChI;可解释性任务叠加MACCS

4.4 实战 —— 用RDKit生成各类表示

光说不练假把式。咱们用RDKit把上面讲的东西串起来,跑一个完整示例。

from rdkit import Chem
from rdkit.Chem import AllChem, MACCSkeys
from rdkit.Chem.inchi import MolToInchi

# 读入分子
smiles = "COc1ccc(CC(=O)N(C)C)cc1"
mol = Chem.MolFromSmiles(smiles)

# 1. 规范化SMILES
canonical_smiles = Chem.MolToSmiles(mol)
print("SMILES:", canonical_smiles)

# 2. InChI
inchi = MolToInchi(mol)
print("InChI:", inchi)

# 3. Morgan指纹(ECFP4, 2048位)
morgan_fp = AllChem.GetMorganFingerprintAsBitVect(mol, radius=2, nBits=2048)
print("Morgan bits count:", len(morgan_fp))

# 4. MACCS指纹(166位)
maccs_fp = MACCSkeys.GenMACCSKeys(mol)
print("MACCS bits count:", len(maccs_fp))

# 5. 组合两种指纹
combined = list(morgan_fp) + list(maccs_fp)
print("Combined feature length:", len(combined))

小技巧:注意看MolToSmiles的默认参数,它默认启用canonical=True,生成的SMILES就是标准形式。如果你从其他工具导入SMILES,先用这个函数洗一遍,能避免很多坑。

你有没有发现一个小细节?MACCS和Morgan指纹都是0/1二值向量,但它们的语义完全不同。MACCS是一张“预设检查表”,勾选已知基团;Morgan则是自动学习的局部结构“印象”。两者互为补充。

4.5 如何选择表示方法?

很多新手会问,到底用哪种最好?我的回答是——看场景。

  • 分子相似性搜索:Morgan指纹(ECFP4)是标配。速度快,效果也好。
  • ADMET预测/QSAR:推荐组合使用Morgan+MACCS。我曾在一个毒性预测项目里试过单用Morgan,准确率在83%晃,加上MACCS后到了89%。
  • 数据去重/数据库合并:InChI哈希值做唯一键,比SMILES靠谱太多。
  • 可解释性需求:当业务方问你“为什么预测这个分子有毒”,你可以用MACCS指给他们看——哦,因为含有某个特定基团。

核心思路:别死磕一种表示法。把多种分子表示作为多模态输入,往往能捕捉更全面的化学信息。这在我们后续讲到图神经网络和多模态融合时会更深入探讨。