简介《化学中的机器学习现在与未来》是由阿斯利康研究员Jon Paul Janet制作的学术报告PDF面向化学、药学及计算科学背景的科研人员和学生系统梳理了机器学习在化学领域的应用图景与未来趋势。报告以AlphaFold在CASP13/14蛋白折叠预测竞赛中的突破为切入点结合阿斯利康药物发现中的多目标设计真实案例深入讲解了分子结构预测、功能材料设计、化学反应预测及数据驱动实验设计等前沿方向帮助读者理解AI方法如何加速药物筛选、催化剂优化与合成路线规划。资源为单个PDF文档共1个文件压缩包大小约41.44MB幻灯片排版清晰、图表完整适合直接阅读、打印或作为组会分享的参考资料。目前已有222人学习下载对于希望快速把握机器学习与化学交叉领域核心框架的读者是一份兼具广度与深度的入门进阶素材。1. 机器学习在化学里不是玄学这篇文章要讲的路径当看到《Machine Learning in Chemistry now and in the future.pdf》这个标题时第一反应可能是“又是一篇综述”。但我更愿意把它当成一张作战地图现在的机器学习早已不是化学研究里的背景板而是能直接缩短实验周期的工具。这篇文章面向那些已经被数据重复性折磨过的化学、材料从业者——手里有反应产率、光谱或催化测试数据想用模型预测下一步又不确定该从哪条路径进场。我会先把数据表示和选型逻辑捋清楚再用可复现的最小实验展示流程最后把未来趋势落到主动学习和自动化闭环。适合有一定基础、准备动手跑模型的读者。2. 化学数据的机器可读化分子表示与数据集构建机器学习模型吃的是数字不是化学式。同样是“把分子变成特征”这一步选错表示方式会让后续所有努力白费。这一章先梳理表示方式再给出完整的数据清洗和指纹生成流程。2.1 从SMILES到图表示怎么选分子编码化学数据不能直接喂给sklearn必须先转成机器能处理的向量或图结构。先看几种常见表示方式。表示方式典型用途优点缺点SMILES 字符串输入生成模型、计算工具文本简洁、生态成熟同一分子有多种写法需规范化InChI数据库去重、数据交换标准唯一可读性差Morgan 指纹ECFP经典 ML 特征计算快、可解释丢失三维构象信息分子图原子键图神经网络输入保留拓扑结构需要深度学习框架选型理由可以很直接数据量在几千到一两万条时Morgan 指纹加随机森林或 XGBoost 往往是最稳的起点因为特征维度适中树模型对离散特征又不敏感。只有当数据量到十万级、且任务涉及长程相互作用或复杂拓扑时切换到图神经网络才有明显收益。换表示之前先确认手头数据量能不能撑起模型复杂度否则就是给自己挖坑。2.2 构建一个可用的训练集数据清洗与划分我见过太多“模型效果差”最后查出来是数据里有金属盐残留、SMILES 没规范化或者测试集和训练集有同骨架分子。清洗这一步必须做扎实顺序也很重要先解析再规范化再去盐最后去重。常见的清洗步骤可以总结成四条用 RDKit 解析 SMILES解析失败的直接丢弃。用Chem.MolToSmiles(mol, canonicalTrue)生成规范 SMILES消除原子编号顺序的差异。去除含.的 SMILES这类写法通常表示盐或非共价复合物不能简单当作单分子处理。按规范 SMILES 去重并把数据按 Murcko 骨架划分训练/测试集避免同骨架分子跨集。下面是一个可以直接套用的清洗脚本片段。import pandas as pd import numpy as np from rdkit import Chem from rdkit.Chem.Scaffolds import MurckoScaffold from sklearn.model_selection import GroupShuffleSplit def clean_smiles(smiles_list): 清洗SMILES列表解析、去盐、规范化。 cleaned [] for smi in smiles_list: mol Chem.MolFromSmiles(smi) if mol is None: continue # 含.的SMILES通常表示盐、溶剂合物等混合物 if . in smi: continue # 生成规范SMILES避免同一分子多种写法 canonical Chem.MolToSmiles(mol, canonicalTrue) cleaned.append(canonical) return cleaned # 假设原始数据有两列smiles 和 property data pd.read_csv(my_data.csv) data[clean_smi] clean_smiles(data[smiles].tolist()) data data.dropna(subset[clean_smi]) # 去重同一个规范SMILES只保留一条 data data.drop_duplicates(subsetclean_smi) # 计算每个分子的Murcko骨架用于后续按骨架划分 data[scaffold] data[clean_smi].apply( lambda s: Chem.MolToSmiles( MurckoScaffold.GetScaffoldForMol(Chem.MolFromSmiles(s)) ) ) # 按骨架分组做随机划分保证同一个骨架不会同时出现在训练和测试 splitter GroupShuffleSplit(n_splits1, test_size0.2, random_state42) train_idx, test_idx next(splitter.split(data, groupsdata[scaffold])) train, test data.iloc[train_idx], data.iloc[test_idx]这段代码的逻辑说明clean_smiles里去掉了含.的条目但没有做更完整的中和电荷操作如果数据里有带电荷分子建议再用标准化工具做一步中和。骨架划分是化学机器学习里非常关键的一步——它把化学空间的重叠度压低测试分数才不会被同系物“虚高”。random_state42锁住划分结果方便复盘对比。2.3 编码与特征用 RDKit 生成 Morgan 指纹数据清理完接下来就是把分子变成向量。Morgan 指纹也叫 ECFPExtended Connectivity Fingerprint它把每个原子周围的环境用圆形子结构编码成二进制或计数向量。两个参数最关键半径radius和位长nBits。半径 2 就是常见的 ECFP4表示考虑了原子周围 2 个化学键内的子结构位长通常选 1024 或 2048。小分子数据集用 2048 位不会太稀疏太大反而让树模型无谓地分叉。生成代码也很短。from rdkit import Chem from rdkit.Chem import AllChem import numpy as np def morgan_fp(smiles, radius2, nbits2048): 把SMILES转换成Morgan指纹位向量。 mol Chem.MolFromSmiles(smiles) if mol is None: return None # radius2对应ECFP4nbits决定特征维度 fp AllChem.GetMorganFingerprintAsBitVect(mol, radiusradius, nBitsnbits) # BitVect转成numpy数组方便进模型 arr np.array(fp.ToList(), dtypenp.float32) return arr # 示例生成一条指纹 smi CC(O)NC1CCCCC1 fp_vector morgan_fp(smi)逻辑说明fp.ToList()把位向量变成 0/1 列表再转成 float32 数组这样可以直接拼成特征矩阵。参数radius2是化学信息学里的默认经验值对大多数性质预测够用如果你处理的是大环或长链分子可以尝试radius3但要注意特征维度膨胀和过拟合风险。需要提醒的是指纹一旦生成就会丢失三维构象信息比如手性、顺反异构的影响。如果你的任务恰好是手性选择性预测单靠 2D 指纹很难达到 80% 以上的准确率那种场景要么加 3D 描述符要么直接换图神经网络。3. 模型选型经典机器学习还是图神经网络数据表示搞定下一步就是选模型。很多初学者的第一反应是“做化学一定要用图神经网络”但真实项目里随机森林和 XGBoost 依然是最可靠的主力。这一章对比两条路线并给出一个可复现的基准实验。3.1 表格数据场景随机森林与 XGBoost 为什么还能打指纹矩阵本质上是一个稀疏的 0/1 表格这种数据对树模型非常友好。随机森林不会因为特征离散而做无意义的分割调参空间小几百棵树跑下来很少出意外。XGBoost 则能捕捉到指纹之间的非线性交互但需要小心学习率和树深度。我把两个模型的关键超参数范围整理如下。模型超参数典型范围备注RandomForestn_estimators500-2000更多树方差更小注意内存RandomForestmax_featuressqrt 或 1/3降低树间相关性XGBoostlearning_rate0.05-0.2配合早停避免过拟合XGBoostmax_depth4-8指纹特征下加深极易过拟合选型逻辑很简单数据量小、噪声大、特征维度几千维树模型是首选。只有当你发现树模型的误差已经收敛且你有足够多的数据去训练更复杂的表达时才需要考虑图神经网络。3.2 图神经网络的适用边界与常用架构图神经网络把分子看成原子和键组成的图通过消息传递机制让每个原子逐步聚合邻居信息。它的长处是能自主学习与性质相关的子结构而不是依赖人工设计的指纹。常见的架构有图注意力网络、图同构网络和消息传递网络。边界条件要讲清楚如果训练集只有几千个分子GNN 很容易把训练集背下来测试集上表现反而不如指纹加树模型。GNN 适合的是数据量至少两万、任务有明确拓扑敏感性比如溶解度、毒性、反应选择性的场景。另外GNN 训练对 GPU 和框架依赖高调参成本也高不是“开箱即用”的工具。3.3 可复现的基准实验指纹RF vs GNN我先给一个完整的指纹加随机森林的训练验证代码这也是我拿到新任务后最先跑的 baseline。from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import cross_val_predict from sklearn.metrics import mean_absolute_error, r2_score # 假设 train 数据里有 fp_list 和 property 列 # fp_list 是上一章生成的指纹列表每条都是 np.array X np.vstack(train[fp_list].values) y train[property].values # 随机森林树数 800特征采样用 sqrt model_rf RandomForestRegressor( n_estimators800, max_featuressqrt, random_state42, n_jobs-1 ) # 5折交叉验证 cv_pred cross_val_predict(model_rf, X, y, cv5) print(CV MAE:, mean_absolute_error(y, cv_pred)) print(CV R2:, r2_score(y, cv_pred))逻辑说明n_jobs-1让随机森林用满所有 CPUmax_featuressqrt可以防止所有树都在同一批强特征上分叉从而提升泛化。cross_val_predict返回的是逐样本的预测值可以直接和真实值算误差。如果 GNN 是备选方案我习惯先用一个最小消息传递层做通路实验确认 pipeline 没有问题再上完整模型。下面是最简的 GNN 核心层片段。import torch import torch.nn as nn class SimpleMessagePassing(nn.Module): 最小可读的消息传递层只做邻居特征聚合。 def __init__(self, in_dim, out_dim): super().__init__() self.W nn.Linear(in_dim, out_dim) self.act nn.ReLU() def forward(self, x, edge_index): # x: 节点特征矩阵 [N, in_dim] # edge_index: 两行张量src和dst src, dst edge_index # 对每个节点把来自邻居的消息求和 messages torch.zeros_like(x) messages.index_add_(0, dst, self.W(x[src])) # 自身变换叠加邻居消息 x_new self.act(self.W(x) messages) return x_new参数说明edge_index的构建来自 RDKit 分子的邻接矩阵index_add_是 PyTorch 提供的索引累加操作比循环邻居快不少。真实 GNN 里还要加 BatchNorm、Dropout 和全局池化层这里只展示消息传递的核心思路。跑通这个小层之后再去叠加层数否则一上来就堆大模型出问题时很难定位是数据、特征还是网络结构的问题。4. 实验验证陷阱从过拟合到数据泄漏的排查清单模型选完、训练跑完不代表可以直接用。化学机器学习里最隐蔽的错误都藏在验证环节数据泄漏、评估指标选错、可解释性用了黑匣子。这一章专门给出一份排查列表。4.1 数据泄漏最常见的翻车原因数据泄漏的特征是交叉验证分数很高但一上线上数据就崩。最常见的原因是特征预处理发生在数据集划分之前。举例说明对性质做标准化StandardScaler时如果先在整个数据集上计算均值和方差再划分训练/测试测试集的统计信息就已经渗透进模型的训练过程。正确做法是先划分再只在训练集上拟合规一化参数。# 错误做法 scaler StandardScaler() X_all_scaled scaler.fit_transform(X_all) # 测试信息提前进入scaler train_test_split(X_all_scaled, y_all) # 正确做法 from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test train_test_split(X_all, y_all, test_size0.2, random_state42) scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) # 只用训练集参数除了标准化泄漏化学数据里还有一个隐藏泄漏点同一分子的规范 SMILES 没有做导致同分异构体同时出现在训练集和测试集。所以第 2 章的清洗步骤不是形式主义而是在给验证集“去污”。4.2 不均衡与回归任务的评价指标分类任务不能只盯 ROC-AUC当正负样本比例悬殊时PR-AUC 更能反映模型在少数类上的真实表现。回归任务则不能只看 R²建议同时看 MAE 和残差分布。我每次跑完回归模型都会画一张残差图检查预测误差是否随真值增大而发散。import matplotlib.pyplot as plt true y_test pred model.predict(X_test_scaled) residual true - pred plt.scatter(true, residual, alpha0.5) plt.axhline(0, colork, linestyle--) plt.xlabel(true property) plt.ylabel(residual (pred - true)) plt.show()如果残差随真值呈喇叭状分布说明模型在极端值上系统性不稳。此时需要检查数据是否有高杠杆点或者考虑对目标变量做对数变换。忘却这一点只报一个 R² 好看很容易把问题掩盖掉。4.3 模型可解释性SHAP 与注意力权重怎么用用确定性模型并不等于黑匣子。指纹随机森林的场景可以直接用 SHAP 解释特征贡献。下面是一个典型用法。import shap # model_rf 是之前训练好的随机森林 explainer shap.TreeExplainer(model_rf) shap_values explainer.shap_values(X_test[:100]) # 画摘要图每个点是特征颜色表示数值高低横轴表示SHAP值 shap.summary_plot(shap_values, X_test[:100], feature_namesfp_names)注意fp_names需要自己定义建议直接用该指纹位对应的子结构片段 ID方便后续回溯到具体官能团。GNN 场景下注意力权重也常被用来解释但要保持谨慎注意力权重反映的是模型计算路径中的相关性不是因果性。把注意力权重当成“哪个原子更重要”之前至少要做一次原子随机置换测试确认模型真的依赖这个原子。5. 避坑 / 常见问题跑化学 ML 时我踩过的几个坑这个方向上手容易做扎实难。我把真实项目里踩过的四个典型问题按“现象 → 原因 → 解决”列出来每一条都对应一种常见的失败模式。5.1 现象训练集 R²0.99测试集 R²0.2这是个很经典的翻车现场。表面看是过拟合但根因常常是数据划分方式不对按随机划分时同一个骨架的分子可以同时出现在训练集和测试集模型实际上学到了“看到这个骨架就输出这个数值”一到新骨架自然崩溃。原因没有做骨架划分测试集和训练集化学空间重叠度太高。解决改用第 2 章里基于GroupShuffleSplit的骨架划分同时统计训练集和测试集骨架重叠数。重叠骨架占测试集比例超过 30%就要警惕分数虚高。5.2 现象模型对同系物失效换骨架就崩另一种常见情况测试集里的分子和训练集的 Tanimoto 相似度都很高模型表现不错但拿到供应商库里的新骨架后误差直接翻倍。这说明模型并没有学会化学规则只是在做近邻回忆。原因训练集化学空间覆盖太窄多样性不足。解决在划分训练集时用 MaxMin 或 Butina 聚类挑选代表性分子并在评估时报告测试集与训练集的最大相似度分布。这个最大相似度的中位数低于 0.4 时模型外推风险很大要谨慎宣称“全局可用”。from rdkit.Chem import DataStructs from rdkit.Chem import AllChem # 计算测试集每个分子与训练集最近邻的相似度 train_fps [AllChem.GetMorganFingerprintAsBitVect(Chem.MolFromSmiles(s), 2, 2048) for s in train[clean_smi]] test_fps [AllChem.GetMorganFingerprintAsBitVect(Chem.MolFromSmiles(s), 2, 2048) for s in test[clean_smi]] test_max_sim [] for fp_test in test_fps: sims DataStructs.BulkTanimotoSimilarity(fp_test, train_fps) test_max_sim.append(max(sims)) print(测试集最大相似度中位数:, np.median(test_max_sim))这段代码用BulkTanimotoSimilarity一次计算一个测试分子和整个训练集的相似度取最大值为最相似训练样本的相似度。中位数过低时说明测试集里有大量离群结构模型预测不靠谱是正常现象不是 bug。5.3 现象特征标准化把分类器搞崩了我见过有人在二进制指纹矩阵上强行套StandardScaler结果模型准确率反而掉了。指纹每一位是 0 或 1标准化后变成带正负的连续值等于破坏了“片段是否存在”的语义。原因特征语义是离散逻辑不适合用高斯标准化。解决指纹特征直接喂树模型不需要任何缩放。如果一定要用线性模型可以试试MaxAbsScaler或直接做 L2 归一化但效果也未必更好。记住一个原则化学里的人工特征能不动就不动。5.4 现象同一个分子因为 SMILES 书写不同被当成两条样本这属于数据血缘问题。数据集来源于多个数据库时比如 PubChem、ZINC、内部旧数据同一个分子可能写成不同的 SMILES比如芳香环的凯库勒式与芳香式。如果没有规范化去重失败模型会把同一个结构当成两个不同样本既造成数据泄漏又稀释了 label 语义。原因没有统一 SMILES 规范。解决用Chem.MolToSmiles(mol, canonicalTrue)得到规范表达再按它去重。如果对精确结构一致性要求高还可以直接用 InChI 字符串做 key因为 InChI 对互变异构的处理更严格。去重后建议随机挑 20 条人工肉眼确认一遍。6. 未来在哪主动学习、大模型与自动化实验闭环看标题里 “now and in the future”最值得关注的是未来三年内能被普通课题组用上的方向。我不建议盲目追大模型而是从三个可落地的技能点说起。6.1 主动学习怎么减少湿实验次数主动学习的大逻辑是先在小训练集上学一个模型然后用模型挑出“最不确定”的分子去做实验把新实验结果加回训练集再重训。核心技巧是选择不确定性度量比如分类任务的预测概率熵或回归集成模型的预测标准差。我常给的做法是训练 5 个随机森林用预测方差作为不确定性。不确定性最高的分子优先做实验能显著减少湿实验次数。import numpy as np from sklearn.ensemble import RandomForestRegressor # 训练多个模型方差大的样本优先推荐 models [RandomForestRegressor(n_estimators500, random_statei) for i in range(5)] for model in models: model.fit(X_train, y_train) preds np.vstack([model.predict(X_test) for model in models]) uncertainty preds.std(axis0) # 用标准差衡量不确定性 top_recommend np.argsort(uncertainty)[::-1][:20]这个代码展示了怎么用集成模型找到当前最“有价值”的实验候选。实际闭环里这些候选会被排进实验队列而不是简单按精度排序。6.2 化学大模型从分子生成到性质预测大模型在化学领域最成熟的应用是分子生成比如给定目标性质生成候选结构。对性质预测大模型不一定会比 GNN 更强尤其在数据量不够的课题组场景下微调大模型反而容易过拟合。我的建议是把它当作生成工具而不是万能预测器。真要微调优先考虑用现有 checkpoint而不是从头训练。6.3 实验闭环ML机器人平台的落地技巧未来两三年自动化实验平台会把“预测-实验-再训练”的闭环从论文推向实验室实操。落地有一个关键细节把每一轮模型预测和对应实验结果都写进同一个数据库用版本号管理。否则闭环跑一个月你根本说不清哪一批预测是被哪一版模型生成的。我现在的习惯是每跑一个模型都保留一份规范 SMILES、骨架划分和指纹参数的副本避免复盘时发现数据版本对不上。实验数据库里的每一行都至少要有一条model_version和data_version字段这个习惯帮我省过好几轮从头排查的苦工。希望帮到你。本文还有配套的精品资源点击获取