分子化学基础:从SMILES到立体化学

要说图神经网络预测ADMET,我们得先聊聊分子长什么样。说白了,模型再厉害,它也得先把分子“看”懂了才能干活。我这些年带项目,发现不少人在图模型上使劲调参,但忽略了分子表示这个地基。地基不稳,楼盖得再高也得塌。

这一章,我们就把分子表示的几个核心概念掰开揉碎讲清楚。你掌握了这些,后面上手GNN才会顺手。

2.1 SMILES与InChI:分子的两种“身份证”

分子怎么用文字描述?这是个古老的问题。目前最通用的两种格式就是SMILES和InChI。我项目中经常拿它们做数据预处理,你想想看,不同数据库出来的分子格式五花八门,没有统一表示就没法训练。

SMILES表示法

SMILES全称是简化分子线性输入规范。它用ASCII字符串来表示分子的结构。举个例子,乙醇的SMILES是CCO。简单吧?

这里有几个基本规则:

  • 原子:用元素符号表示。碳是C,氧是O,氮是N。有机分子里氢通常省略。
  • 键:单键默认不写,双键写=,三键写#。
  • 分支:用圆括号()表示支链。比如异丙醇是CC(C)O。
  • 环:用数字标记环的连接点。苯环可以写成c1ccccc1。

看一下实际例子:

# 几个常见分子的SMILES
阿司匹林: CC(=O)Oc1ccccc1C(=O)O
咖啡因: Cn1c(=O)c2c(ncn2C)n(C)c1=O
布洛芬: CC(C)Cc1ccc(cc1)C(C)C(=O)O
对乙酰氨基酚: CC(=O)Nc1ccc(O)cc1

我刚开始用SMILES时犯过一个错:没注意芳香性表示。苯环里碳用小写c不是大写C。大写C表示sp3杂化的饱和碳。你写C1=CC=CC=C1也行,但那不是标准芳香表示,在ADMET预测里会丢关键信息。

InChI表示法

InChI是国际化合物标识的缩写。它比SMILES更规范,更擅长处理立体化学和互变异构体。我建议做数据合并时用InChI当主键,因为它能避免很多歧义。

典型的InChI长这样:

# 乙醇的InChI
InChI=1S/C2H6O/c1-2-3/h3H,2H2,1H3

# 阿司匹林的InChI
InChI=1S/C9H8O4/c1-6(10)13-8-5-3-2-4-7(8)9(11)12/h2-5H,1H3,(H,11,12)

它分几层:分子式层1S/C2H6O、连接层/c1-2-3、氢原子层/h3H,2H2,1H3。每一层解决一个化学表示问题。

我个人经验是:SMILES适合做模型输入,InChI适合做数据去重。模型看SMILES字符串里的原子序列,人去重用InChI的唯一性。两者互补。

我的习惯做法:拿到一批分子数据后,先用RDKit把SMILES标准化(做归一化),再用InChI去重。这样能保证训练集里同分异构体不被错误合并。

用Python的RDKit做转换很简单:

from rdkit import Chem

# SMILES转Mol对象
mol = Chem.MolFromSmiles("CCO")

# Mol转InChI
from rdkit.Chem import rdmolops
inchi = Chem.MolToInchi(mol)
print(inchi)  # 输出: InChI=1S/C2H6O/c1-2-3/h3H,2H2,1H3

# InChI转SMILES
mol2 = Chem.MolFromInchi(inchi)
smiles = Chem.MolToSmiles(mol2)
print(smiles)  # 输出: CCO

2.2 官能团与分子性质:预测ADMET的核心抓手

为什么我们要讲官能团?因为ADMET预测的本质就是“结构决定性质”。我做过一个项目,用GNN预测药物水溶性,发现模型对羟基(-OH)和羧基(-COOH)特别敏感。为什么?因为这些极性基团直接影响氢键能力。

常见的官能团及其对ADMET的影响:

官能团 化学式 对ADMET的影响
羟基 -OH 增加水溶性,促进代谢(II相葡萄糖醛酸化)
羧基 -COOH 强酸性,增加水溶性,可能引起胃肠道刺激
氨基 -NH₂ 碱性,影响口服生物利用度,可能产生毒性
羰基 C=O 影响氢键受体能力,与CYP450酶结合
芳香环 苯环等 增加脂溶性,提高LogP,但可能引起代谢活化
卤素 -F, -Cl, -Br 调节脂溶性和代谢稳定性,氟原子常用于优化

我在做GNN特征工程时,除了原子类型和键类型,还会显式加入“是否是氢键供体/受体”这个特征。说白了,官能团信息可以编码成原子级别的属性,喂给图神经网络。

举个例子:

# 用RDKit识别官能团
from rdkit import Chem
from rdkit.Chem import Descriptors, MolFromSmiles, Mol

mol = MolFromSmiles("CC(=O)Oc1ccccc1C(=O)O")  # 阿司匹林

# 计算分子描述符
logP = Descriptors.MolLogP(mol)
h_donors = Descriptors.NumHDonors(mol)
h_acceptors = Descriptors.NumHAcceptors(mol)
tpsa = Descriptors.TPSA(mol)

print(f"LogP: {logP:.2f}")
print(f"氢键供体: {h_donors}")
print(f"氢键受体: {h_acceptors}")
print(f"拓扑极性表面积: {tpsa:.2f} Ų")
关键洞察:在图神经网络中,官能团信息不需要显式硬编码。模型可以通过学习原子和键的局部结构自动捕获官能团模式。但如果你知道目标ADMET性质与某个官能团强相关,手动加入该特征可以显著提升预测准确率。我在做Caco-2渗透性预测时,就手动加入了“是否含有羧基”这个二值特征。

2.3 手性与立体化学:容易被忽视的关键维度

啊,立体化学。这是ADMET预测里最容易被忽视但影响极大的因素。我记得有一次,模型对某种药物的代谢半衰期预测偏差巨大。排查了两天,发现是手性中心搞反了——R构型和S构型的药代动力学完全不同。

什么是手性? 简单说就是一个碳原子连着四个不同的基团,它的镜像不能与自身重合。就像左手和右手,看起来一样但无法重叠。

在SMILES中,手性用@或@@标记:

# L-丙氨酸 (S构型)
SMILES: C[C@@H](N)C(=O)O

# D-丙氨酸 (R构型)
SMILES: C[C@H](N)C(=O)O

这里的@表示方向。具体规则我不展开,你记住@和@@表示两种不同构型就够了。

在InChI中,立体化学信息放在/t和/m层:

# L-丙氨酸的InChI
InChI=1S/C3H7NO2/c1-2(4)3(5)6/h2H,4H2,1H3,(H,5,6)/t2-/m0/s1

最后那个/t2-/m0/s1就是立体信息。但说实话,我很少直接解析InChI的立体层。我通常用RDKit提取手性中心信息,然后编码到图特征里。

实战中如何处理手性?这里给个RDKit示例:

from rdkit import Chem
from rdkit.Chem import rdmolops

mol = Chem.MolFromSmiles("C[C@@H](N)C(=O)O")  # L-丙氨酸

# 获取手性中心
chiral_centers = Chem.FindMolChiralCenters(mol, includeUnassigned=True, includeCIP=True)
print(chiral_centers)
# 输出: [(2, 'S')],表示2号碳是S构型

# 获取所有原子手性信息
for atom in mol.GetAtoms():
    if atom.GetChiralTag() != Chem.ChiralType.CHI_UNSPECIFIED:
        print(f"原子{atom.GetIdx()}: 手性类型={atom.GetChiralTag()}")
我曾经踩过的坑:SMILES里没有显式手性标记时(比如C.C(N)C(=O)O),RDKit会默认生成CHI_UNSPECIFIED类型。这种未指定手性的分子,在预测手性敏感的ADMET性质(如CYP450代谢)时会产生巨大误差。一定要确保你的训练数据包含立体化学信息。如果不包含,可以考虑使用InChI补全。

对于GNN来说,手性信息可以编码成边特征或者全局特征。我个人习惯把每个手性中心的方向编码成一个整数特征(R=1, S=2, 未指定=0),然后作为全局图属性传给读出头。这样模型能感知到手性差异。

快速检查:如果你不确定手性信息是否被正确处理,可以用RDKit生成分子的3D构象,然后肉眼检查手性中心是否符合预期。代码一行的事:
from rdkit.Chem import AllChem
mol3d = Chem.AddHs(mol)
AllChem.EmbedMolecule(mol3d, AllChem.ETKDG())
print(AllChem.MMFFOptimizeMolecule(mol3d))

好,现在我们回头看分子表示的全貌。下方这张图展示了从原始分子到图输入的完整链条:

分子表示与图输入构建流程 SMILES / InChI 分子文本表示 RDKit Mol对象 原子、键、构象解析 特征工程 官能团、手性、描述符 分子图 节点=原子, 边=化学键 GNN模型 消息传递 → 图读出 ADMET性质预测 LogP, 渗透性, 毒性, 代谢... 键特征: 单/双/三键 节点特征: 原子类型、电荷 全局特征: 手性、分子量

从图上你能看到,分子表示从SMILES字符串出发,经过RDKit解析成Mol对象,再通过特征工程提取官能团和手性信息,最终构造成图神经网络的输入。每一步都环环相扣。

最后总结三条经验:
  1. SMILES和InChI各有用途。我建议在进模型前统一用一种规范化的SMILES(比如用RDKit的MolToSmiles输出芳香模式)
  2. 官能团是ADMET预测的隐形冠军。不要只依赖图结构自动学习,该加的特征要手动加
  3. 立体化学不是锦上添花,是雪中送炭。做ADMET预测,尤其涉及代谢和转运蛋白时,手性信息必须保留

这一章的内容比较基础,但属于必须打牢的地基。你理解了分子表示、官能团和立体化学,下一阶段我们才能谈怎么把这些信息喂给图神经网络。嗯,先消化这些,到第三章我们开始动手搭第一个GNN模型。