3. 分子数据获取与清洗
分子性质图网络预测的第一道工程门槛并不在模型结构,而在数据。一个分子性质预测任务能否复现、能否泛化,往往在数据进入模型之前就已经被决定。本节围绕“从公开数据集到可训练图样本”的完整链路,讲清楚每个环节该做什么、怎么做,以及哪些错误会直接导致指标虚高或模型失效。
一、公开数据集:先明确任务边界再选数据
分子性质预测常用的公开数据源包括:MoleculeNet 系列(ESOL、FreeSolv、Lipophilicity、BBBP、Tox21、QM7/QM8/QM9 等)、PubChem、ChEMBL、ZINC,以及物理化学实验数据库。选择数据集时,先确认三件事:预测目标(回归还是分类)、标签来源(实验测定、计算模拟还是文献汇总)、规模与分布(样本量、性质取值范围、是否高度不平衡)。
例如 ESOL 是约 1128 条水溶性回归数据,标签为 logS;BBBP 是约 2000 条血脑屏障穿透二分类数据。二者都适合做教学基线,但若把 ESOL 的回归流程直接套到 BBBP 上,评价指标和损失函数都会错。因此数据获取的第一步不是下载,而是写清楚任务卡:目标列、单位、任务类型、允许的输入表示。
二、SMILES 解析:把字符串变成可计算的分子对象
SMILES 是分子的线性编码,但它是“语法”而非“语义完整”的表示。解析时推荐使用 RDKit:
from rdkit import Chem
from rdkit.Chem import Descriptors
mol = Chem.MolFromSmiles("CC(=O)Oc1ccccc1C(=O)O") # 阿司匹林
if mol is None:
print("解析失败")
else:
mol = Chem.AddHs(mol) # 补氢,便于后续图构建
print(Chem.MolToSmiles(mol)) # 规范化 SMILES
print(Descriptors.MolWt(mol)) # 分子量
解析失败通常来自非法价键、未闭合环、错误芳香性标记或编码字符问题。工程上必须把 MolFromSmiles 返回 None 的记录单独落盘,而不是直接丢弃,便于回溯数据质量。解析成功后,分子对象可进一步导出原子、键、电荷、手性、芳香性等图结构信息,这是图神经网络输入的原始素材。
三、去重与标准化:同一分子的多种写法
同一分子可能有多个 SMILES 写法(如互变异构、芳香/凯库勒式、原子顺序不同)。若不去重,训练集与测试集会出现“同一分子不同写法”,造成信息泄漏。标准做法是:
- 规范化 SMILES:用
Chem.MolToSmiles(mol, canonical=True)生成唯一表示。 - 去盐与去溶剂:用
rdMolStandardize的LargestFragmentChooser保留最大片段,去除盐离子和溶剂分子。 - 互变异构标准化:用
TautomerEnumerator选择规范互变异构体,避免同一分子因互变被当成两条样本。 - 去重:以规范化 SMILES 或 InChIKey 为键,保留标签一致或按规则合并冲突标签。
标准化后应记录“原始 SMILES → 规范 SMILES”的映射表,便于审计。若同一规范分子对应多个不同标签,需按任务决定取均值、取中位数或标记为冲突样本。
四、缺失值处理:区分“缺失”与“无效”
分子数据中的缺失通常有三类:标签缺失、描述符计算失败、结构信息不完整。处理原则是先分类再处理:
- 标签缺失:回归任务可删除或插补,但插补会引入偏差,建议优先删除并记录删除比例;分类任务若正负样本缺失严重,需重新评估数据源。
- 描述符缺失:如某分子无法计算 LogP,可尝试替代描述符或标记为缺失指示变量,而不是简单填 0。
- 结构缺失:解析失败或原子数异常,应直接剔除并记录原因。
缺失值处理必须保留“处理日志”:删除了多少条、插补了多少条、插补方法是什么。否则后续指标无法解释。
五、数据泄漏风险:最容易被忽视的指标杀手
分子数据泄漏常见于四种场景:
- 重复分子跨集:同一分子同时出现在训练集和测试集,模型只是记忆。
- 相似分子跨集:骨架高度相似的分子被随机划分,导致测试集“过于容易”。
- 标签泄漏:把与标签强相关的描述符(如直接由标签计算得到的量)作为输入。
- 预处理泄漏:在划分训练/测试之前,用全量数据做标准化、特征选择或缺失值插补。
工程上推荐使用 骨架划分(scaffold split) 或 聚类划分,而不是简单随机划分。骨架划分按 Murcko 骨架分组,保证测试集分子骨架与训练集不同,更接近真实药物发现场景。若必须随机划分,至少先按规范化 SMILES 去重,并检查训练/测试的相似度分布。
六、工程化数据管道:从原始文件到图样本
一个可复现的分子数据管道应包含以下阶段:
- 原始层:保存下载的 CSV/SDF 原始文件,不修改。
- 解析层:SMILES → RDKit Mol,记录解析失败。
- 标准化层:去盐、去溶剂、互变异构标准化、规范化 SMILES。
- 去重层:按规范 SMILES/InChIKey 去重,处理标签冲突。
- 划分层:按骨架或聚类划分训练/验证/测试,固定随机种子。
- 图构建层:原子特征(元素、度、电荷、杂化、芳香性、手性)、键特征(键型、共轭、环)→ 图对象。
- 缓存层:将图样本序列化为 PyG/DGL 可读格式,避免每次训练重复解析。
下面这张图展示了从原始数据到图样本的完整数据流,以及各阶段的关键检查点:
七、一个可执行的最小管道示例
下面给出一个简化但完整的处理流程,便于直接改造成项目脚本:
import pandas as pd
from rdkit import Chem
from rdkit.Chem.MolStandardize import rdMolStandardize
def standardize(smi):
mol = Chem.MolFromSmiles(smi)
if mol is None:
return None
mol = rdMolStandardize.LargestFragmentChooser().choose(mol)
mol = rdMolStandardize.TautomerEnumerator().Canonicalize(mol)
return Chem.MolToSmiles(mol, canonical=True)
df = pd.read_csv("raw.csv")
df["std_smiles"] = df["smiles"].apply(standardize)
df = df.dropna(subset=["std_smiles", "label"])
df = df.drop_duplicates(subset=["std_smiles"], keep="first")
df.to_csv("clean.csv", index=False)
这段代码只覆盖了标准化和去重,实际工程中还需加入骨架划分、图构建和缓存。建议把每一步的输出都保存为独立文件,形成可审计的数据版本链。
总结来说,分子数据工程的核心不是“把数据读进来”,而是建立一条可追溯、可复现、防泄漏的管道。公开数据集提供起点,SMILES 解析和标准化保证分子身份一致,去重和缺失值处理保证样本质量,骨架划分和预处理顺序控制泄漏风险,最终以图样本缓存的形式交付给模型。这条管道做扎实,后续的图网络训练才有意义。