MolBERT(Fabian 等,2020)系统地比较了不同的 SMILES 预训练任务。它的价值不在于提出了最好的模型,而在于回答了「哪些预训练任务真的有用」这个更基础的问题。
比较的三类预训练任务
| 任务 | 做法 | 论文的发现 |
|---|---|---|
| 掩码语言建模(MLM) | 遮住 token 并预测 | 基础,有效但不够 |
| SMILES 等价性判断 | 判断两个 SMILES 是否同一分子 | 有帮助 |
| 物化性质预测 | 预测计算得到的描述符 | 贡献最大 |
「预测计算描述符」这个任务贡献最大,是一个很有启发的发现:它相当于用一个免费的、无限量的监督信号(RDKit 可以算任意分子的 logP、TPSA、MW 等),把化学知识直接注入模型——比纯粹的自监督更有针对性。
为什么这个发现重要
# 自监督预训练的一般逻辑:
# 无标注数据很多 → 设计一个不需要标注的任务
# → 让模型学到通用表示
#
# 但「不需要标注」不等于「没有监督信号」
#
# 在化学中,我们有一类特殊的信号:
# 【可以用规则/程序计算出来的性质】
# - 分子量、logP(Crippen)、TPSA
# - 环数、可旋转键数、氢键供受体数
# - 各种拓扑指数
# - 骨架、片段
#
# 这些是「免费的标签」:
# - 对任意分子都能算
# - 数量无限
# - 无噪声(在其定义内是精确的)
#
# 用它们做多任务预训练:
# → 直接把化学知识注入表示
# → 比让模型「自己从 SMILES 里悟出来」高效得多
#
# 【这个思路后来被广泛采用】:
# ChemBERTa-2 的 MTR 预训练(见 145)
# 多个后续工作的辅助任务
#
# 更一般的教训:
# 【当领域中存在可计算的中间量时,
# 用它们做辅助监督,通常比纯自监督更有效】
预训练任务的设计原则
# 一个好的预训练任务应该:
#
# 1) 【需要模型理解你关心的东西】
# 如果下游任务关心官能团,
# 预训练任务就应该迫使模型识别官能团
#
# 反例:
# 只做 MLM,模型可能主要学会了
# 「SMILES 的语法规则」——对下游没有价值
#
# 2) 【难度适中】
# 太简单 → 模型不需要学复杂表示就能完成
# 太难 → 学不动
#
# 例:遮住 15% 的 token 是经验值;
# 遮太少任务太简单,遮太多信息不足
#
# 3) 【不能有捷径】
# 如果模型能用某个简单规则完成任务,
# 它就不会学习深层表示
#
# 例:预测分子量 —— 只需要数原子
# → 单独用它作为唯一任务效果有限
# → 应该与其它任务组合
#
# 4) 【与下游任务有共享的底层能力】
# 这是迁移学习成立的前提
#
# 实践建议:
# 设计预训练任务时,问自己:
# 「完成这个任务,模型必须理解什么?
# 这个理解对下游有用吗?」
如何验证预训练是否真的有用
# 【必做的消融实验】:
#
# 对照 1:同样的架构,【随机初始化】直接训练下游任务
# → 与预训练版本的差距 = 预训练的真实贡献
# → 【这个对照最重要,也最常缺失】
#
# 对照 2:逐个去掉预训练任务
# → 看每个任务的贡献
#
# 对照 3:不同的下游数据量
# → 预训练的价值通常随数据量增加而减小
# → 画曲线比单点比较有信息量
#
# 对照 4:传统基线(ECFP + 树模型)
# → 如果预训练模型打不过它,前面的对照都无意义
import numpy as np
from sklearn.model_selection import KFold
def ablation_study(train_fn, X, y, configs, n_seeds=5):
"""对多个配置做多种子评估"""
results = {}
for name, cfg in configs.items():
scores = []
for seed in range(n_seeds):
scores.append(train_fn(X, y, cfg, seed))
results[name] = (np.mean(scores), np.std(scores))
# 报告时必须带标准差
for name, (m, s) in sorted(results.items(), key=lambda kv: -kv[1][0]):
print(f"{name:30s} {m:.4f} ± {s:.4f}")
return results
# configs 应包含:
# "预训练完整", "仅 MLM", "仅描述符", "随机初始化", "ECFP+LGBM"
#
# 【判读】:
# 如果「随机初始化」与「预训练完整」的差距
# 小于种子间标准差的两倍,
# 那么预训练的价值存疑
物理化学约束作为归纳偏置
- 核心思想:与其让模型从数据中学习化学规律,不如把已知的化学知识直接编码进模型或训练目标;
- 常见做法:
- 用可计算描述符做辅助任务(MolBERT 的做法);
- 用图表示保证价键规则(GNN,见 159《图神经网络 GNN》);
- 用等变架构保证对称性(见 160《等变神经网络》);
- 在损失函数中加入物理约束(如 Uni-Mol Docking V2,见 104《Uni-Mol Docking》)。
- 为什么有效:化学数据通常很少,而归纳偏置能大幅降低样本需求——这是分子机器学习与 NLP/CV 最本质的差别;
- 反向的教训:盲目照搬 NLP/CV 的大规模预训练范式,在化学中常常收效有限,因为数据规模与任务性质都不同。
实践中的启示
| 做法 | 价值 |
|---|---|
| 用 RDKit 描述符做辅助任务 | 成本极低,效果稳定 |
| 把描述符直接拼进特征 | 更简单,常常同样有效(见 131《Chemprop / D-MPNN 论文精读》) |
| 多任务学习相关性质 | 数据少的任务受益 |
| SMILES 增强 | 低成本提升(见 145《ChemBERTa 论文精读》) |
| 纯粹增大预训练数据 | 边际收益递减 |
一个务实的观察:如果目标只是「让模型知道 logP、TPSA」,直接把这些描述符作为输入特征,比通过预训练间接注入更简单也更可靠。预训练的独特价值在于学到无法直接计算的表示。
关键要点
- 预测可计算描述符的辅助任务贡献最大——用「免费的标签」直接注入化学知识;
- 更一般的教训:领域中存在可计算的中间量时,用它们做辅助监督优于纯自监督;
- 「随机初始化直接训练」的对照最重要也最常缺失——它才能显示预训练的真实贡献;
- 化学数据少,归纳偏置比大规模预训练更划算——这是与 NLP/CV 最本质的差别。
延伸资源
- ChemBERTa:145《ChemBERTa 论文精读》;MolFormer:147《MolFormer 论文精读》;Uni-Mol:142《Uni-Mol 论文精读》;
- Chemprop:131《Chemprop / D-MPNN 论文精读》;GNN:159《图神经网络 GNN》;等变网络:160《等变神经网络》。