146

MolBERT 论文精读:预训练任务比模型名字更重要

MolBERT 的核心结论是预训练任务的设计比模型名字更重要。这篇讲清各类预训练任务的效果差异与选择依据。

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"
#
# 【判读】:
#   如果「随机初始化」与「预训练完整」的差距
#   小于种子间标准差的两倍,
#   那么预训练的价值存疑

物理化学约束作为归纳偏置

  • 核心思想:与其让模型从数据中学习化学规律,不如把已知的化学知识直接编码进模型或训练目标
  • 常见做法
  • 为什么有效化学数据通常很少,而归纳偏置能大幅降低样本需求——这是分子机器学习与 NLP/CV 最本质的差别;
  • 反向的教训盲目照搬 NLP/CV 的大规模预训练范式,在化学中常常收效有限,因为数据规模与任务性质都不同。

实践中的启示

做法 价值
用 RDKit 描述符做辅助任务 成本极低,效果稳定
把描述符直接拼进特征 更简单,常常同样有效(见 131《Chemprop / D-MPNN 论文精读》
多任务学习相关性质 数据少的任务受益
SMILES 增强 低成本提升(见 145《ChemBERTa 论文精读》
纯粹增大预训练数据 边际收益递减

一个务实的观察:如果目标只是「让模型知道 logP、TPSA」,直接把这些描述符作为输入特征,比通过预训练间接注入更简单也更可靠。预训练的独特价值在于学到无法直接计算的表示。

关键要点

  • 预测可计算描述符的辅助任务贡献最大——用「免费的标签」直接注入化学知识;
  • 更一般的教训:领域中存在可计算的中间量时,用它们做辅助监督优于纯自监督
  • 「随机初始化直接训练」的对照最重要也最常缺失——它才能显示预训练的真实贡献;
  • 化学数据少,归纳偏置比大规模预训练更划算——这是与 NLP/CV 最本质的差别。

延伸资源