SA Score(Synthetic Accessibility Score,Ertl & Schuffenhauer 2009)是最常用的合成难度快速估计。它在分子生成中是必备的护栏——没有它,生成模型会产出大量看起来合理但无法合成的分子。
原理
SA Score 由两部分构成:
- 片段贡献分:统计 PubChem 中约百万个分子的片段(以 Morgan 指纹的形式)出现频率。常见片段 → 容易合成;罕见片段 → 难。分子中所有片段的频率加权得到基础分;
- 复杂度惩罚:手性中心数、螺环、桥环、大环、分子尺寸等带来额外惩罚;
- 最终归一化到 1(易)~ 10(难)。
计算
from rdkit import Chem, RDConfig
import sys, os
sys.path.append(os.path.join(RDConfig.RDContribDir, "SA_Score"))
import sascorer
for smi, name in [
("CC(=O)Oc1ccccc1C(=O)O", "阿司匹林"),
("CN1C=NC2=C1C(=O)N(C)C(=O)N2C", "咖啡因"),
("CC12CCC3C(CCc4cc(O)ccc34)C1CCC2O", "雌二醇"),
]:
mol = Chem.MolFromSmiles(smi)
print(f"{name:8s} SA = {sascorer.calculateScore(mol):.2f}")
# 典型范围:
# 1~3 简单,常规反应可得
# 3~5 中等
# 5~7 较难
# 7~10 很难或需特殊方法
系统性偏差:必须知道
- 它衡量的是「片段常见程度」而非「合成可行性」。这是最根本的局限。一个用常规反应就能做出来、但骨架少见的分子,SA Score 会偏高——这系统性地惩罚了新颖性,而新颖性恰恰是分子生成的价值所在。
- 不考虑起始原料可得性:结构简单但需要罕见砌块的分子,SA 分低但实际难做;
- 对手性的惩罚可能过重:现代不对称合成能高效构建多个手性中心;
- 不考虑官能团兼容性:分子中多个反应性基团的相互干扰、保护基策略完全不在考虑范围;
- 对大环惩罚重:而大环化学近年进展很快。
在生成模型中的正确配置
# REINVENT4 中的配置(见 347)
[[stage.scoring.component]]
[stage.scoring.component.SAScore]
[[stage.scoring.component.SAScore.endpoint]]
name = "SA"
weight = 1.0
transform.type = "reverse_sigmoid" # 分数低(易合成)时接近 1
transform.low = 2.0
transform.high = 6.0
transform.k = 0.4
# 关键设计:
# 1) 用平滑 transform 而非硬阈值
# 直接卡 SA < 5 会造成优化悬崖,
# 模型会在阈值边缘徘徊而非真正往简单方向走
#
# 2) 阈值不要卡太严
# high = 6.0 而非 4.0,给新颖结构留空间
#
# 3) 权重适中
# 权重过高会让模型只产出平凡结构
替代与补充指标
| 指标 | 原理 | 优势 |
|---|---|---|
| SA Score | 片段频率 + 复杂度 | 极快,可用于生成每一步 |
| SCScore | 从反应数据学「产物比反应物复杂」 | 基于真实反应逻辑 |
| RAScore | 用逆合成求解结果训练的分类器 | 直接预测「能否找到路线」 |
| 实际跑逆合成 | AiZynthFinder 等(见 385《AiZynthFinder》) | 最可靠,但慢 |
# RAScore:更贴近实际关心的问题
pip install rascore
from RAScore import RAScore_XGB
scorer = RAScore_XGB.RAScorerXGB()
print(f"RAScore: {scorer.predict('CC(=O)Oc1ccccc1C(=O)O'):.3f}")
# 0~1,越大越可能找到合成路线
# 分级使用策略:
# 生成阶段(每步都算)→ SA Score(毫秒级)
# 生成后初筛(几千个)→ RAScore
# 候选精选(几百个) → 实际跑逆合成
# 最终确认(几十个) → 合成化学家评审
用自家数据校准阈值
import numpy as np
# 通用阈值不一定适合你的团队
# 拿公司历史上真正合成过的分子算分布
historical_sa = [sascorer.calculateScore(Chem.MolFromSmiles(s))
for s in synthesized_compounds]
print(f"历史合成分子的 SA 分布:")
print(f" 中位数 {np.median(historical_sa):.2f}")
print(f" 90 分位 {np.percentile(historical_sa, 90):.2f}")
# 用 90 分位作为 transform.high,比照搬通用的 6.0 更贴合实际
# 更好的做法:
# 同时收集「设计了但最终没做出来」的分子,
# 看它们的 SA 分布 —— 这是真正的负样本
# → 能定出更有判别力的阈值
为什么生成模型必须有这个护栏
- 生成模型会优化你写下的分数:如果只优化活性预测分,模型会产出高分但化学上荒谬的分子;
- SA Score 是最便宜的护栏:毫秒级计算,可以在生成的每一步都算;
- 但它不够:还需要结构警报、PAINS 过滤、相似性约束一起构成完整的护栏(见 347《用 REINVENT4 生成新分子》);
- 最终仍需人工审查:用 mols2grid(见 217《Mols2grid》)让药化专家过一遍 top 分子,几乎总能发现自动指标漏掉的问题。
关键要点
- SA Score 衡量片段常见程度,不是真正的合成路线分析;
- 它会系统性惩罚新颖但易合成的结构,阈值不要卡太严;
- 用平滑 transform 而非硬阈值,避免优化悬崖;
- 分级使用:生成时用 SA Score,候选精选时实际跑逆合成。
延伸资源
- 指标对比:390《合成可及性再讨论》;生成约束:348《在分子生成中加入 SA Score 约束》;
- 逆合成:384《逆合成分析入门》、385《AiZynthFinder》。