MOSES(Molecular Sets,Polykovskiy 等,2020)与 GuacaMol 互补:GuacaMol 侧重目标导向优化,MOSES 侧重分布学习。它提供了统一的数据集、基线模型实现与一套细致的分布指标。
MOSES 提供什么
| 组成 | 内容 |
|---|---|
| 标准数据集 | 从 ZINC Clean Leads 筛选的约 190 万分子 |
| 固定划分 | 训练/测试/骨架测试三部分 |
| 基线实现 | 字符 RNN、VAE、AAE、JT-VAE、LatentGAN 等 |
| 评价指标 | 十余个分布与质量指标 |
「骨架测试集」是 MOSES 的一个重要设计:它包含训练集中完全没有出现过的骨架,用于检验模型是否只是在记忆而非学习化学规律。
指标体系
# 基础指标
# Valid 有效 SMILES 比例
# Unique@1k 前 1000 个中唯一的比例
# Unique@10k 前 10000 个中唯一的比例
# Novelty 不在训练集中的比例
#
# 分布相似性指标(都是与参考集比较)
# FCD Fréchet ChemNet Distance —— 综合性最强
# SNN 最近邻相似度:生成分子与参考集最相似分子的平均相似度
# Frag 片段相似度:BRICS 片段的分布差异
# Scaf 骨架相似度:Bemis-Murcko 骨架的分布差异
#
# 性质分布(Wasserstein 距离)
# logP、SA(可合成性)、QED(类药性)、MW
#
# IntDiv(内部多样性)
# 生成集合内部的平均不相似度
# → 【检测模式崩溃的关键指标】
#
# Filters
# 通过 MCF + PAINS 等过滤器的比例
#
# 【指标之间的张力】:
# Novelty 高 vs FCD 低(像训练集)—— 天然冲突
# IntDiv 高 vs 性质分布匹配 —— 也有冲突
# → 【不存在所有指标都最优的模型】
# → 单看一个指标排名毫无意义
使用
pip install molsets
import moses
# 加载标准数据集
train = moses.get_dataset("train") # 约 160 万
test = moses.get_dataset("test")
test_scaffolds = moses.get_dataset("test_scaffolds")
# 评价生成的分子
generated = my_model.sample(30000) # 建议至少 3 万个
metrics = moses.get_all_metrics(
gen=generated,
n_jobs=8,
device="cuda",
test=test,
test_scaffolds=test_scaffolds,
)
for k, v in sorted(metrics.items()):
print(f"{k:20s} {v:.4f}")
# 输出示例:
# valid 0.9750
# unique@1000 1.0000
# unique@10000 0.9990
# FCD/Test 0.4120 ← 越小越好
# SNN/Test 0.5850
# Frag/Test 0.9980 ← 越大越好
# Scaf/Test 0.8500
# FCD/TestSF 1.2300 ← 骨架测试集,通常明显更差
# Scaf/TestSF 0.1200 ← 【这个指标最能说明泛化能力】
# IntDiv 0.8560
# Filters 0.9700
# Novelty 0.8900
# 【判读要点】:
# Scaf/TestSF 低 = 模型无法生成新骨架
# → 只是在重排训练集的结构
# IntDiv 低 = 模式崩溃,生成的分子高度相似
# Filters 低 = 生成了很多有问题的结构
模式崩溃:最需要警惕的失效
# 模式崩溃(mode collapse):
# 模型只学会生成分布中的一小部分
# → 生成的分子看起来都差不多
#
# 为什么危险:
# Valid、Unique、Novelty 都可能很高,
# 但生成的都是同一类分子的微小变体
# → 【对药物发现毫无价值】
#
# 检测方法:
# 1) IntDiv(内部多样性)
# MOSES 直接提供
# IntDiv < 0.8 需要警惕
#
# 2) 骨架计数
# 生成 10000 个分子,有多少个不同的
# Bemis-Murcko 骨架?
# → 比例太低说明多样性不足
#
# 3) 化学空间可视化
# 用 UMAP/t-SNE 把生成分子与参考集
# 投影到同一空间(见 052)
# → 直观看出是否只覆盖了一小块区域
#
# 4) 性质分布的方差
# 不只看均值是否匹配,还要看分布宽度
from rdkit import Chem
from rdkit.Chem.Scaffolds import MurckoScaffold
def scaffold_diversity(smiles_list):
scafs = set()
valid = 0
for smi in smiles_list:
mol = Chem.MolFromSmiles(smi)
if mol is None:
continue
valid += 1
scafs.add(MurckoScaffold.MurckoScaffoldSmiles(mol=mol))
return {
"n_valid": valid,
"n_scaffolds": len(scafs),
"scaffold_ratio": len(scafs) / max(valid, 1),
}
# scaffold_ratio < 0.1 通常说明多样性严重不足
MOSES 与 GuacaMol 的分工
| MOSES | GuacaMol | |
|---|---|---|
| 侧重 | 分布学习 | 目标导向优化 |
| 回答的问题 | 模型学到了化学空间吗 | 模型能优化目标吗 |
| 基线实现 | 提供多个完整实现 | 较少 |
| 骨架泛化测试 | 有(TestSF) | 部分任务涉及 |
| 易被刷分 | 中等 | 严重(见 135《GuacaMol 论文精读》) |
建议两者都跑:MOSES 回答「模型是否学到了合理的化学」,GuacaMol 回答「模型能否朝目标优化」。但两者都不回答「生成的分子有没有用」。
分布指标的根本局限
- 「像训练集」与「有价值」是两回事。这是最根本的问题。药物发现要的是训练集里没有的好分子,而分布指标奖励的恰恰是相似性;
- 指标之间存在内在冲突:不可能同时最大化新颖性与分布匹配度——因此「在所有指标上都好」的模型不存在,看排行榜要看权衡;
- 不考虑目标靶点:分布学习完全不知道你要打什么靶点;
- 不考虑可合成性的真实难度:SA score 只是粗糙的估计(见 135《GuacaMol 论文精读》);
- 实际项目中的生成任务几乎总是有约束的:给定骨架做修饰、给定口袋做设计、多参数同时满足——纯分布学习的设定与实际需求差距很大。
关键要点
- Scaf/TestSF 指标最能说明泛化能力——低值意味着模型只在重排训练集结构;
- IntDiv 是检测模式崩溃的关键,而模式崩溃时其它指标可能都很好看;
- 指标之间存在内在冲突,不存在全面最优的模型,看排行榜要看权衡;
- 分布指标奖励「像训练集」,而药物发现要的是「训练集里没有的好分子」。
延伸资源
- GuacaMol:135《GuacaMol 论文精读》;REINVENT:137《REINVENT 论文精读》;分子生成模型:158《AI 分子生成模型》;
- 化学空间可视化:052《化学空间可视化》;Benchmark 陷阱:169《Benchmark 陷阱》。