简介这份PDF资料是Jon Paul Janet在AstraZeneca期间所作的《Machine Learning in Chemistry now and in the future》报告面向化学、药物研发与材料领域的科研人员及高年级学生帮助读者理解机器学习如何切入结构与功能关系驱动的化学问题。内容以引言、案例研究、化学中的机器学习应用与结论四部分展开涵盖CASP13/14蛋白折叠预测竞赛中AlphaFold的突破、多目标分子设计、分子结构预测、材料设计、反应预测与数据驱动实验设计等主题兼具综述视角与工业实践案例。资源包共1个PDF文件压缩包约41.44MB页面以幻灯片图文形式组织便于快速浏览与专题引用。目前已有222人学习下载适合希望系统了解机器学习在化学中应用现状与未来趋势、并寻找案例素材的读者参考。1. 从一场蛋白折叠竞赛说起化学中的机器学习到底在解决什么问题2018 年那场蛋白折叠预测竞赛一支首次参赛的队伍在“无信息”类别里拿下了 43 项测试中的 25 项第二名只赢了 3 项。这件事在化学与生命科学圈子里被反复提起因为它说明了一个很现实的问题当数据量足够大、模型结构足够贴合问题本身时机器学习能在传统方法算不动的场景里给出可用结果。化学空间里潜在的小分子数量是 10 的 60 次方量级靠实验一个个试、靠量子化学一个个算时间成本根本兜不住。这套方案要解决的就是如何把“性质—特征—实验”这条链路用机器学习压缩到秒级或小时级适合正在做化学信息学、药物设计方向毕设或课程设计的人也适合需要给团队出一份可交付技术方案的从业者。2. 需求分析与技术选型为什么化学场景不能直接套用通用 ML 流程2.1 化学数据的三个特殊约束做化学方向的机器学习第一道坎不是模型是数据。通用 ML 流程里一行样本就是一个特征向量特征之间独立同分布。化学数据不是这样一个分子是一个图原子是节点化学键是边分子的性质往往由局部子结构决定而不是由某个全局统计量决定。这意味着直接把分子展开成指纹向量再喂给全连接网络会丢掉大量拓扑信息。第二个约束是数据规模。公开的化学反应数据集量级通常在十万到百万条之间和视觉、NLP 领域动辄上亿的样本完全不在一个量级。所以化学场景里模型参数量不能盲目堆大迁移学习和预训练策略比从零训练更现实。第三个约束是标签噪声。实验测出来的活性值、产率、毒性本身就有测量误差同一个化合物在不同实验室测出来的值可能差一个数量级。如果按通用回归任务去拟合模型很容易把噪声当信号学进去。通常的做法是在损失函数里加入不确定性估计或者对标签做分箱处理把回归问题转成分类问题来降低噪声敏感度。2.2 模型选型的判断依据化学场景里常见的模型分三类基于描述符的树模型、基于序列的 Transformer、基于图结构的图神经网络。选哪个不是拍脑袋要看你的任务类型和数据形态。如果任务是预测分子是否具有某种活性且你手上只有 SMILES 字符串和对应的二分类标签梯度提升树加 RDKit 描述符是最稳的基线训练快、可解释性好答辩时能说清楚每个特征的含义。如果任务涉及反应预测或逆合成序列到序列的 Transformer 更合适因为反应物和产物都可以表示成 SMILES 序列本质上是翻译问题。如果任务是分子性质回归且数据量在万条以上图卷积网络值得一试它直接在分子图上做消息传递不需要人工设计描述符。我一般会建议学生先跑一个树模型基线把数据划分、评估指标、预处理流程全部跑通再换图网络做对比。这样即使图网络效果不好你手上也有一个能交差的基线结果不至于翻车。# 基于 RDKit 描述符 梯度提升树的基线流程 from rdkit import Chem from rdkit.Chem import Descriptors import pandas as pd from sklearn.ensemble import GradientBoostingRegressor from sklearn.model_selection import train_test_split from sklearn.metrics import mean_squared_error def smiles_to_features(smiles): mol Chem.MolFromSmiles(smiles) if mol is None: return None # 选取 5 个常用描述符作为最小特征集 return [ Descriptors.MolWt(mol), # 分子量 Descriptors.MolLogP(mol), # 脂水分配系数 Descriptors.NumHDonors(mol), # 氢键供体数 Descriptors.NumHAcceptors(mol),# 氢键受体数 Descriptors.TPSA(mol), # 拓扑极性表面积 ] df pd.read_csv(chem_data.csv) # 列smiles, target df[features] df[smiles].apply(smiles_to_features) df df.dropna(subset[features]) X list(df[features]) y df[target].values X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) model GradientBoostingRegressor(n_estimators300, max_depth4, learning_rate0.05) model.fit(X_train, y_train) pred model.predict(X_test) print(RMSE:, mean_squared_error(y_test, pred, squaredFalse))这段代码的关键不在模型本身而在smiles_to_features这个函数它把 SMILES 转成分子对象再抽取描述符。MolFromSmiles返回None的情况必须处理否则后续特征矩阵会出现空值。n_estimators300和max_depth4是化学小数据集上的保守配置树太深容易记住噪声。评估用 RMSE 而不是 R²是因为答辩时 RMSE 有明确的物理单位评委更容易判断模型是否可用。2.3 评估指标不能只看一个数化学回归任务里R² 高不代表模型能用。一个模型在测试集上 R²0.85但如果你把预测值按活性区间分段看可能高活性区间的误差特别大而高活性区间恰恰是药物设计最关心的。所以评估至少要给三个数整体 RMSE、按活性分箱后的 RMSE、以及 Top-K 富集因子。富集因子衡量的是模型把真正的高活性分子排在前面的能力这个指标在虚拟筛选场景里比 RMSE 更有说服力。3. 总体方案设计从分子表示到多目标优化的四层结构3.1 四层架构与模块职责整套方案按数据流拆成四层表示层、模型层、优化层、评估层。表示层负责把分子转成模型能吃的格式包括 SMILES 清洗、描述符计算、图结构构建。模型层是具体的预测器可以是树模型、Transformer 或图网络对外统一暴露predict(features)接口。优化层做多目标设计给定几个待优化性质在化学空间里搜索帕累托前沿。评估层负责离线指标计算和在线筛选结果回溯。这四层之间用配置文件解耦表示层的输出格式、模型层的超参数、优化层的目标权重都写在 YAML 里。这样做的好处是换模型不用改优化层代码换优化目标不用重训模型。毕设里如果只做一个预测任务优化层可以简化成一个排序模块但结构上保留答辩时能讲清楚扩展路径。3.2 多目标设计的核心逻辑化学里的多目标设计典型场景是同时优化活性和选择性或者同时优化溶解度和渗透性。这两个目标往往互相冲突提高活性可能牺牲选择性提高溶解度可能降低渗透性。所以不能简单加权求和而要找帕累托前沿——即不存在另一个分子在所有目标上都不差且至少一个目标更好。实现上通常用两种策略。第一种是标量化给每个目标一个权重把多目标转成单目标跑多次不同权重得到一组解。第二种是贝叶斯优化用高斯过程建模每个目标用采集函数如期望改进 EI决定下一个要评估的分子。前者实现简单适合毕设后者更高效适合有实验反馈闭环的场景。# 多目标标量化示例活性最大化 毒性最小化 import numpy as np from scipy.stats import rankdata def scalarize(activity_pred, toxicity_pred, w_activity0.7): # 先各自转成排名消除量纲差异 act_rank rankdata(-activity_pred) # 活性越高排名越靠前 tox_rank rankdata(toxicity_pred) # 毒性越低排名越靠前 # 加权组合权重之和为 1 score w_activity * act_rank (1 - w_activity) * tox_rank return score # 假设 model_act 和 model_tox 已训练好 candidates [CCO, c1ccccc1, CC(O)O] # 候选 SMILES 列表 act_pred model_act.predict(candidates) tox_pred model_tox.predict(candidates) scores scalarize(act_pred, tox_pred, w_activity0.7) top_idx np.argsort(scores)[:10] # 取前 10 个进入下一轮这里用排名而不是原始预测值做加权是因为活性和毒性的量纲完全不同直接加权会被数值大的那个目标主导。w_activity0.7表示当前阶段更看重活性这个权重在方案里应该做成可配置项方便做敏感性分析。答辩时如果被问到“为什么是 0.7”你可以展示权重从 0.5 到 0.9 变化时 Top-10 分子的重叠率说明结论对这个参数不敏感。3.3 数据划分策略随机划分在化学里会骗人通用 ML 里随机划分训练集和测试集是默认操作但在化学场景里这会高估模型性能。原因很简单化学数据集里存在大量骨架相似的分子随机划分会把相似分子同时分到训练集和测试集模型相当于在“背答案”。正确的做法是按骨架划分scaffold split保证测试集里的分子骨架在训练集里没出现过。from rdkit.Chem.Scaffolds import MurckoScaffold from rdkit import Chem def scaffold_split(smiles_list, test_ratio0.2): scaffolds {} for smi in smiles_list: mol Chem.MolFromSmiles(smi) scaffold MurckoScaffold.MurckoScaffoldSmiles(molmol) scaffolds.setdefault(scaffold, []).append(smi) # 按骨架包含的分子数降序排列大骨架优先进训练集 sorted_scaffolds sorted(scaffolds.items(), keylambda x: len(x[1]), reverseTrue) n_test int(len(smiles_list) * test_ratio) test_smiles [] for scaffold, members in sorted_scaffolds: if len(test_smiles) len(members) n_test: test_smiles.extend(members) train_smiles [s for s in smiles_list if s not in set(test_smiles)] return train_smiles, test_smilesMurckoScaffoldSmiles提取的是分子的骨架结构去掉侧链只保留环系和连接方式。按骨架分组后再划分测试集上的指标才是对“新化学空间”的真实估计。这个细节在毕设答辩里经常被问到提前准备好能加分。4. 关键模块实现表示、训练与推理的落地细节4.1 分子图构建与图卷积网络如果选图网络路线第一步是把分子转成图。节点特征通常包括原子类型、度、形式电荷、是否在环上、杂化方式边特征包括键类型、是否共轭、是否在环上。这些特征用 one-hot 编码维度不高但信息密度大。import torch from torch_geometric.data import Data from rdkit import Chem def mol_to_graph(smiles): mol Chem.MolFromSmiles(smiles) if mol is None: return None # 节点特征原子序数、度、形式电荷、是否在环上 node_feats [] for atom in mol.GetAtoms(): node_feats.append([ atom.GetAtomicNum(), atom.GetDegree(), atom.GetFormalCharge(), int(atom.IsInRing()), ]) # 边索引和边特征 edge_index [] edge_feats [] for bond in mol.GetBonds(): i, j bond.GetBeginAtomIdx(), bond.GetEndAtomIdx() edge_index [[i, j], [j, i]] # 无向图双向边 feat [int(bond.GetBondTypeAsDouble()), int(bond.IsInRing())] edge_feats [feat, feat] x torch.tensor(node_feats, dtypetorch.float) edge_index torch.tensor(edge_index, dtypetorch.long).t().contiguous() edge_attr torch.tensor(edge_feats, dtypetorch.float) return Data(xx, edge_indexedge_index, edge_attredge_attr)edge_index的转置操作容易写错PyG 要求形状是[2, num_edges]所以先按列表构建再.t().contiguous()。无向图每条键要加两个方向否则消息传递只能单向走。节点特征里没有做归一化是因为原子序数这类离散特征归一化反而破坏语义图网络里通常用 embedding 层处理离散特征这里为了简化直接用原始值实际方案里建议换成nn.Embedding。4.2 训练循环里的三个关键控制点图网络训练比树模型麻烦主要是三个地方容易出问题批次内的图大小不一、梯度爆炸、过拟合。PyG 的DataLoader会自动做图批处理把多个小图拼成一个大图用batch向量区分归属。梯度裁剪设max_norm1.0是化学图网络的常规操作因为消息传递层数一多梯度容易累积。早停策略按验证集损失监控patience20在万条级数据集上比较合适。from torch_geometric.loader import DataLoader from torch.optim import Adam model GCN(in_channels4, hidden64, out_channels1) optimizer Adam(model.parameters(), lr1e-3) loader DataLoader(train_graphs, batch_size32, shuffleTrue) best_val float(inf) patience_counter 0 for epoch in range(200): model.train() for batch in loader: optimizer.zero_grad() pred model(batch.x, batch.edge_index, batch.batch) loss torch.nn.functional.mse_loss(pred.squeeze(), batch.y) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step() # 验证集评估省略逻辑同训练 val_loss evaluate(model, val_loader) if val_loss best_val: best_val val_loss patience_counter 0 torch.save(model.state_dict(), best_model.pt) else: patience_counter 1 if patience_counter 20: breakbatch.batch是 PyG 自动生成的向量标记每个节点属于哪个分子做图级池化时按它聚合。pred.squeeze()把[batch_size, 1]压成[batch_size]和batch.y对齐。保存best_model.pt而不是最后一个 epoch 的权重是因为验证集损失最低的点通常泛化最好。4.3 推理阶段的批量处理与缓存推理时如果候选分子有几十万个逐个调mol_to_graph会很慢。通常的做法是先把所有 SMILES 转成图对象存成列表再用DataLoader批量推理batch_size可以设大一些如 128因为推理不需要存梯度。另外描述符计算和骨架提取的结果可以缓存到本地避免重复计算。我在一个十万级虚拟筛选任务里加缓存后整体耗时从 40 分钟降到 12 分钟这个优化在答辩时讲出来很实在。5. 避坑与常见问题化学 ML 项目里最容易翻车的五个点5.1 数据泄漏测试集指标虚高现象是测试集 R² 达到 0.95 以上但拿新分子一测就崩。原因通常是随机划分导致相似分子跨集或者特征里混入了标签相关信息比如用了包含目标性质的描述符。解决办法是按骨架划分并且在特征工程阶段逐个检查每个特征是否在实验测量之后才能获得。我一般会要求学生在方案里列一张特征来源表标注每个特征是“计算可得”还是“实验后可得”后者一律不能进模型。5.2 SMILES 解析失败被静默丢弃现象是训练集明明有 5 万条跑完发现只剩 4.2 万条模型效果怎么调都上不去。原因是MolFromSmiles对不规范 SMILES 返回None如果代码里直接dropna而不记录你根本不知道丢了哪些。解决办法是在清洗阶段把解析失败的 SMILES 单独存一个文件人工抽查前 100 条判断是数据源问题还是编码问题。盐离子、立体化学标记、芳香性写法不统一都会导致解析失败。5.3 多目标权重拍脑袋定现象是答辩时被问“为什么活性权重是 0.7”答不上来。原因是权重没有做敏感性分析。解决办法是跑一组权重网格0.5、0.6、0.7、0.8、0.9记录每个权重下 Top-K 分子的重叠率和帕累托前沿的分布用一张表说明结论对权重不敏感或者说明某个权重区间内结果稳定。这张表放进方案文档比任何口头解释都有力。5.4 图网络过平滑导致所有分子预测值趋同现象是训练 loss 降下去了但验证集上所有分子的预测值都挤在均值附近区分度极低。原因是图卷积层数太多消息传递反复聚合后节点表示趋同。解决办法是把层数控制在 3 到 5 层加入残差连接或 Jumping Knowledge 结构让浅层信息也能传到输出。化学分子图通常不大几十个原子层数超过 5 层基本没有额外收益。5.5 评估只报一个整体指标现象是方案里只写“RMSE0.8”评委追问高活性区间误差就露馅。解决办法是按活性分箱报误差至少分低、中、高三档再补一个 Top-K 富集因子。如果时间允许画一张预测值对真实值的散点图标出高活性区域的偏差方向这比单一数字有说服力得多。6. 验收与答辩怎么证明这套方案真的能用验收这件事我一般按“三层证据”来准备。第一层是离线指标骨架划分下的 RMSE、分箱 RMSE、Top-10% 富集因子三个数放在一张表里和基线模型树模型或随机预测对比。第二层是消融实验去掉图结构只用描述符、去掉多目标优化只做单目标各跑一遍说明每个模块的贡献。第三层是案例展示挑 5 到 10 个候选分子展示模型预测值、真实值如果有、以及它们在化学空间里的分布用具体例子说明模型学到了什么。答辩时最容易被问的三个问题提前准备好答案。第一个是“你的模型和随机筛选比好多少”用富集因子回答比如 Top-10% 富集因子 3.2 意味着前 10% 的候选里命中的活性分子是随机筛选的 3.2 倍。第二个是“换一个靶点还能用吗”回答时强调表示层和模型层是解耦的换靶点只需要换标签重新训练流程不变。第三个是“数据量再大一倍效果会怎样”这个要诚实可以说当前数据量下模型已经接近饱和更大数据量需要配合更强的正则化或预训练策略并给出你观察到的学习曲线趋势。验收项合格线说明骨架划分 RMSE不高于基线模型基线用描述符树模型Top-10% 富集因子大于 1.5随机筛选为 1.0分箱误差一致性高活性区间误差不超过整体 1.5 倍防止模型只拟合低活性区消融实验至少两组对比证明关键模块有效推理耗时万级分子分钟级含图构建和批量推理最后说一个习惯。我早期做化学 ML 项目时有一次答辩前夜发现测试集指标比预期高了 0.1查了两个小时才发现是骨架划分代码里有个set去重把重复 SMILES 删了导致训练集和测试集有重叠。从那以后我每次划分完数据都强制走一遍“训练集和测试集 SMILES 交集必须为空”的断言检查再跑一个相似度分布对比图。这个习惯帮我省掉了好几次返工。希望这套结构和验收思路能帮到你把方案里的每个数字都变成能讲清楚来路的证据。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?