化学信息学基础:SMILES、InChI、分子图表示

做分子属性预测,第一件事就是把化学结构变成计算机能懂的语言。你想想看,一个苯环六个碳,在纸上画个六边形就行,但要让机器认出来就得规定一套编码规则。

我刚开始接触这个领域时,也纳闷过:为什么不直接用CAS号?后来踩了坑才明白——CAS号是登记号,跟结构没有直接推导关系。真正的化学信息学三大基础:SMILES、InChI、分子图。这三样东西,你每天都会打交道。

SMILES:最流行的行式表示法

SMILES(Simplified Molecular Input Line Entry System)说白了就是把结构写成一行字符串。这玩意1990年代就有了,到现在还是最常用的。

基本规则其实挺简单:

  • 原子用元素符号直接写,比如C、O、N
  • 单键直接写,双键用=,三键用#
  • 分支用括号()包裹
  • 环结构用数字标记开环位置
  • 芳香环用小写字母,比如c1ccccc1代表苯

举个例子,乙醇的SMILES是CCO。嗯,连字符都省了。再来个复杂点的:对乙酰氨基酚(扑热息痛):

CC(=O)Nc1ccc(cc1)O

这个字符串里,CC(=O)N是酰胺部分,c1ccc(cc1)是苯环,最后的O是羟基。注意看括号里的=O,表示一个双键氧连在碳上。

我在项目中遇到过一个大坑:SMILES不唯一!同一个分子,不同人写出来可能不一样。比如丙酮可以是CC(=O)C,但有人写成C(=O)(C)C,也是对的。这就麻烦了,做数据合并时明明同一个分子,字符串却不相等。

我曾经踩过的坑:用SMILES字符串当唯一键去重,结果同一个分子出现好几遍。后来改用规范化SMILES(Canonical SMILES)才解决。RDKit里直接调用Chem.MolToSmiles(mol, canonical=True)就行。

SMILES的变种与规范化

现在主流工具都能生成规范化SMILES,但要注意:

  • Isomeric SMILES:包含手性、同位素信息,用@表示手性中心方向
  • Kekulé SMILES:把所有芳香键都写成单双键交替,不展示芳香性
  • DeepSMILES:针对序列模型做了优化,去掉一些括号陷阱

我个人习惯是:做机器学习时统一用RDKit的规范化SMILES,不去搞花哨的变种。原因很简单——你训练一个模型,输入必须是确定性的,否则自己坑自己。

InChI:权威的国际标识符

如果说SMILES是"民间高手",那InChI就是"官方认证"。InChI(International Chemical Identifier)由IUPAC主导开发,目标是给每个分子一个唯一且可解析的标识符。

你看它的结构就知道这玩意多严谨:

InChI=1S/C8H9NO2/c1-6(10)9-7-2-4-8(11)5-3-7/h2-5,11H,1H3,(H,9,10)

这是对乙酰氨基酚的InChI。拆开看:

  • 1S表示版本和标准标志
  • C8H9NO2是分子式
  • /c1-6(10)...是连接层,描述原子连接关系
  • /h2-5,11H,...是氢原子层,规定哪些位置有氢

InChI还有一个"亲儿子"叫InChIKey,是个固定长度的哈希值,方便数据库索引。比如上面那个分子的InChIKey是:

RZVAJINKPMORJF-UHFFFAOYSA-N

注意最后三位-N表示非标准(Non-standard),如果加了立体化学信息就会变成-S。

我建议:做分子数据清洗时,先用SMILES转成InChI,再用InChI去重。具体流程:SMILES → RDKit Mol → InChI → 去重。这样你就不用担心规范化问题了。

实际项目中,我常用这个组合:

用途 推荐编码 原因
数据存储、去重 InChI / InChIKey 唯一、可逆、标准化
机器学习输入 SMILES / 分子图 长度短、易解析、可特征化
展示与交互 SMILES 人类可读性好
论文引用 InChI 国际标准、不可歧义

分子图表示:计算机眼中的分子

现在我们把分子看作一个图。原子是节点,化学键是边。你想想看,苯环不就是个六元环图吗?甲基不就是个碳连三个氢吗?

分子图通常包含两部分:

  • 节点特征:原子类型、杂化方式、形式电荷、是否为芳香原子、氢原子数等
  • 边特征:键类型(单/双/三/芳香)、键长、是否在环中、立体化学等

举个例子,用RDKit把SMILES转成分子图特征:

from rdkit import Chem
from rdkit.Chem import AllChem

mol = Chem.MolFromSmiles('CCO')
# 获取原子特征
for atom in mol.GetAtoms():
    print(atom.GetAtomicNum(),        # 原子序数
          atom.GetHybridization(),    # 杂化方式
          atom.GetFormalCharge(),     # 形式电荷
          atom.GetIsAromatic())       # 是否芳香
# 获取键特征
for bond in mol.GetBonds():
    print(bond.GetBeginAtomIdx(), bond.GetEndAtomIdx(),
          bond.GetBondType())         # 键类型

这段代码输出:

6 SP3 0 False
6 SP3 0 False
8 SP3 0 False
0 1 SINGLE
1 2 SINGLE

两个碳原子(原子序数6)和一个氧原子(原子序数8),都是sp3杂化,没有电荷,不做芳香。两个单键连接。

做分子属性预测时,图神经网络(GNN)就是吃这个格式的数据。我早期用手工特征做回归,效果一直上不去;后来换成GNN,直接从分子图学特征,曲线直接起飞。说白了,图才是分子的"原生语言"。

常见分子图表示库对比

现在有几个主流的图表示库,我列一下:

库/工具 图构建方式 优点 缺点
RDKit 基于Mol对象 生态完整、文档多 自定义特征需要手写
DGL-LifeSci 基于DGL图框架 与PyTorch无缝集成 学习曲线陡
PyTorch Geometric 自定义Data对象 灵活、社区活跃 需要手写数据加载
OpenBabel 文件格式转换 支持多种格式 图功能较弱
我的经验:从RDKit开始准没错。先用RDKit拿到原子键特征,再自己拼接成图数据格式。等熟悉了再上DGL或PyG。别一上来就上重型框架,容易迷失在API的海洋里。

三种表示的关系

最后一张图帮你理清楚这三者怎么互相转换:

三种化学信息学表示的关系图 分子结构 SMILES InChI / InChIKey 分子图 (GNN) 日常开发:用SMILES输入,用InChI去重,用分子图做模型 三者可通过RDKit相互转换,形成完整工作流 解析 计算 特征化 转换 转换 间接

从左到右,SMILES是人的语言,InChI是数据库的语言,分子图是机器的语言。三者各司其职,又相互贯通。我个人觉得,做项目时优先掌握SMILES和分子图就够用了,InChI更多是在数据清洗和发布时用到。

小技巧:用RDKit的Chem.MolFromSmiles()和Chem.MolToInchi()就能完成SMILES到InChI的转换。反过来用Chem.MolFromInchi()。转换复杂分子时注意立体化学可能丢失,加sanitize=True参数能避免一些问题。