3. 分子表示方法:SMILES、分子图、3D坐标、描述符

做分子生成,第一关就是“怎么让计算机看懂分子”。不同表示方法,就像不同语言——有的适合写诗,有的适合算账。分布式训练下,选错表示可能让通信开销翻倍,GPU利用率惨不忍睹。我踩过这个坑,所以先把几种主流方法给你拆清楚。

一句话记住:没有万能表示,只有任务匹配。序列、图、坐标、描述符,各有各的脾气。

3.1 SMILES – 最轻量的字符串

SMILES用一串字符描述原子、键、分支和环。比如乙醇写CCO,苯环写c1ccccc1。优点是极简,一个字符串就能扔进Transformer或RNN。但你想想看,同一个分子可能有多种合法SMILES——这叫“规范性问题”。

我在项目里遇到过:两个团队分别生成SMILES,送去训练后模型死活学不对,一查发现各自用的规范化规则不同。后来我们统一用RDKit的Chem.MolToSmiles加canonical参数,才把坑填平。

from rdkit import Chem

mol = Chem.MolFromSmiles("CCO")
canonical_smiles = Chem.MolToSmiles(mol)  # 输出 "CCO"
print(canonical_smiles)
分布式处理注意:SMILES长度差异大,批量训练前要做padding或dynamic batching。我习惯把最长序列按分位数截断,避免尾部稀疏浪费显存。

3.2 分子图 – 保留拓扑结构

分子图把原子当节点,化学键当边。每个节点可带特征(原子类型、电荷、杂化),边可带特征(键级、芳香性)。图神经网络(GNN)天然适合这种输入,消息传递过程就是模拟化学环境的“感知”。

我用过DGL和PyG两个框架,个人习惯选PyG,因为它的DataLoader对分布式训练支持更直接。但要注意,稀疏邻接矩阵在跨GPU通信时,必须处理边索引的重新编号。我曾经没处理好,导致每个GPU看到的图都不一样——模型直接崩溃。

from torch_geometric.data import Data
import torch

# 分子水:一个氧,两个氢,两根单键
edge_index = torch.tensor([[0, 0], [1, 2]], dtype=torch.long)  # (2, num_edges)
x = torch.tensor([[8], [1], [1]], dtype=torch.float)          # 原子序数
data = Data(x=x, edge_index=edge_index)

3.3 3D坐标 – 空间全貌

3D坐标给出每个原子的三维位置(单位Å)。常用于构象生成、结合能预测、力场模拟。表示方法很直白:原子列表 + 坐标矩阵。常用格式:SDF、XYZ。

但有个大坑:分子构象不是唯一的!同一个分子可能有几十个稳定构象。你训练用的构象和推理用的构象不匹配,模型效果就会漂移。我建议:在数据处理阶段固定构象生成流程(比如用ETKDG加力场优化),并记录每个分子的“代表性构象”。

# 乙醇的XYZ格式示例(部分)
C   0.0000  0.0000  0.0000
C   1.5100  0.0000  0.0000
O   1.9900  1.2600  0.0000
H  -0.3800 -0.3800  0.9400
...
分布式存储成本:3D坐标数据量是SMILES的百倍。多GPU并行时,预处理(如对齐构象、归一化位置)最好在数据加载时完成,不要存预处理后的全量数据,否则IO会成为瓶颈。

3.4 分子描述符 – 数值化捷径

描述符是预计算的特征向量,比如分子量(MW)、脂水分配系数(LogP)、拓扑极性表面积(TPSA)、氢键供体数、旋转键数等。传统定量构效关系(QSAR)全靠它,现代分子生成也常把它作为条件约束或辅助损失。

说实话,描述符计算很简单:调用rdkit.Chem.Descriptors即可。但不同软件的结果可能有细微差异——我曾经用两个工具算同一个分子的LogP,差了0.3。后来我们规定全局只用RDKit 2021.03版本,统一计算库,才消掉这个偏差。

from rdkit.Chem import Descriptors

mol = Chem.MolFromSmiles("CCO")
mw = Descriptors.MolWt(mol)       # 46.07
logp = Descriptors.MoLogP(mol)    # -0.14
tpsa = Descriptors.TPSA(mol)      # 20.23

3.5 四种表示对比

表示方法维度数据量(典型)分布式难点适用场景
SMILES序列 (30–150 tokens)极小长度padding浪费Seq2Seq生成、语言模型
分子图图 (节点+边)中稀疏通信、重新编号GNN、反应预测
3D坐标3×N 矩阵大 (×3 float)IO瓶颈、构象对齐构象生成、物理对接
描述符固定长度 (几十~几百)极小计算一致性条件生成、辅助损失

3.6 实际选型建议

做多GPU分布式训练时,我通常按这个思路判断:

  • 优先SMILES:如果你的任务目标是生成新分子,且GPU集群带宽高(如NVLink),SMILES最省计算,Transformer直接上。
  • 分子图做主表示:需要理解分子局部结构(如官能团)时,图比序列更直观。注意用PyG的DistributedNeighborSampler缓解边索引同步压力。
  • 3D坐标当额外输入:只有涉及构象或几何性质时才上。配合等变网络(如SE(3)-Transformer),计算开销翻倍,但精度提升明显。
  • 描述符做条件:不管主表示是什么,加几个描述符约束能稳定训练。我常把LogP、TPSA拼到多模态输入里。
我的经验法则:开新项目时,先用SMILES跑通baseline,再逐步叠加图或3D信息。不要一上来就搞复杂表示,调试成本太高。
分子表示方法关系图 分子表示 SMILES 分子图 3D坐标 描述符 可相互转换(经由RDKit、Open Babel等) 分布式训练需根据计算和通信成本权衡

说到底,分子表示是分布式训练的“地基”。地基不稳,再漂亮的网络结构也白搭。我个人建议:团队先统一表示标准、数据处理流水线,再考虑分布式加速。磨刀不误砍柴工,这个习惯帮我省了不知道多少debug时间。