026

Papers with Code 分子性质预测导航

Papers with Code 的分子性质预测板块能快速了解方法进展。这篇讲清怎么用它、以及排行榜的解读陷阱。

Papers with Code 把论文、代码与排行榜关联起来,是快速了解某个任务方法进展的入口。但排行榜的数字有系统性的解读陷阱——不懂这些陷阱,看排行榜可能比不看更误导。

怎么用它

# 【典型的使用流程】
#
# 1) 搜索你关心的任务
#    如 "Molecular Property Prediction"、"Drug Discovery"
#
# 2) 看该任务下的数据集
#    → 了解领域用什么数据评测
#    → 【注意数据集的规模与年代】
#
# 3) 看排行榜
#    → 【不是看谁第一,而是看】:
#      - 整体的性能水平(合理范围)
#      - 【提升的幅度】(是否显著)
#      - 方法的类型分布(哪类方法主流)
#      - 时间趋势(进展是否停滞)
#
# 4) 顺着找论文与代码
#    → 有代码的优先看
#    → 【看 star 数与最近更新】
#
# 5) 【读论文时用五问框架】(见 170)

# 【最有价值的用法】:
#   建立「这类任务的性能大概在什么水平」的认知
#   → 之后判断任何结果是否合理都有了参照系

排行榜的解读陷阱

陷阱 表现 应对
划分方式不统一 有的用随机划分,有的用骨架划分 查每条提交的具体设置
没有标准差 只报告单个数字 提升可能在噪声内
数据集太小 几百到一两千样本 结论稳定性差
过拟合排行榜 反复在测试集上调 虚高
缺少强基线 排行榜上没有 ECFP + 树模型 自己补跑基线
不可复现 无代码或跑不通 看 issue 区

最重要的一点:补上缺失的基线

# 【排行榜上常常没有的基线】:
#   ECFP + 随机森林 / LightGBM
#
# 【为什么缺失】:
#   发表新方法的论文才会提交排行榜,
#   而「用了一个 20 年前的方法」不值得发论文
#   → 【但它可能就是很强】
#
# 【自己补跑的价值】:
#   知道排行榜前几名相对这个基线
#   到底提升了多少
#   → 常常发现提升幅度远小于预期

import numpy as np
import lightgbm as lgb
from rdkit import Chem
from rdkit.Chem import Descriptors, rdFingerprintGenerator
import deepchem as dc

gen = rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048)
DESC = ["MolWt", "MolLogP", "TPSA", "NumHDonors", "NumHAcceptors",
        "NumRotatableBonds", "RingCount", "FractionCSP3",
        "NumAromaticRings", "HeavyAtomCount"]

def featurize(smiles_list):
    X = []
    for s in smiles_list:
        m = Chem.MolFromSmiles(s)
        if m is None:
            X.append(np.zeros(2048 + len(DESC)))
            continue
        fp = np.array(gen.GetFingerprint(m))
        d = np.array([getattr(Descriptors, n)(m) for n in DESC])
        X.append(np.concatenate([fp, np.nan_to_num(d)]))
    return np.array(X)

def benchmark_baseline(dataset_loader, n_seeds=5):
    """在标准数据集上跑基线,多种子"""
    scores = []
    for seed in range(n_seeds):
        tasks, datasets, transformers = dataset_loader(
            featurizer="Raw", splitter="scaffold")
        train, valid, test = datasets
        Xtr = featurize([m.smiles for m in train.X])
        Xte = featurize([m.smiles for m in test.X])
        model = lgb.LGBMRegressor(n_estimators=1000, learning_rate=0.05,
                                  random_state=seed, verbose=-1)
        model.fit(Xtr, train.y.ravel())
        pred = model.predict(Xte)
        rmse = np.sqrt(((pred - test.y.ravel()) ** 2).mean())
        scores.append(rmse)
    print(f"基线 RMSE = {np.mean(scores):.4f} ± {np.std(scores):.4f}")
    return scores

# 【把这个数字与排行榜比较】
# → 差距小 = 新方法的实际价值有限
# → 差距大 = 值得深入了解

常见的分子性质预测数据集

数据集 任务 规模 注意
ESOL / Delaney 水溶解度 约 1.1K 太小,方差大
FreeSolv 水合自由能 约 0.6K 非常小
Lipophilicity logD 约 4.2K
BACE 抑制剂分类 约 1.5K 单一靶点
BBBP 血脑屏障 约 2K 标签质量有已知问题
Tox21 / ToxCast 毒性多任务 约 8K / 8.6K 极度不平衡
HIV 抗 HIV 活性 约 41K 规模较大
QM9 量子性质 约 134K 计算数据,非实验

关键提醒:这些数据集大多来自 MoleculeNet(见 132《MoleculeNet 论文精读》),规模小、标签质量参差、与实际项目的距离较远在它们上面刷分,与在实际项目中有用,是两件不同的事。

更有信息量的观察角度

# 【不看名次,看这些】
#
# 1) 【时间趋势】
#    最近两年 SOTA 提升了多少?
#    → 停滞说明可能触到了数据的噪声天花板
#    → 这本身是有价值的信息
#
# 2) 【方法类型的分布】
#    前十名中:
#      - 有多少是图神经网络?
#      - 有多少是预训练模型?
#      - 【有没有传统方法?】
#    → 反映领域的主流认知
#
# 3) 【不同数据集上的一致性】
#    某个方法在所有数据集上都好,
#    还是只在某几个上好?
#    → 【只在某几个上好 = 可能是调参的结果】
#
# 4) 【代码的可用性】
#    有多少提交提供了可运行的代码?
#    → 比例低说明领域的复现性差
#
# 5) 【与自己数据的相关性】
#    这些数据集的化学空间与你的项目接近吗?
#    → 不接近 → 排行榜的参考价值有限

# 【一个务实的结论】:
#   Papers with Code 适合用来
#   【了解领域全貌与建立参照系】,
#   不适合用来【选择具体方法】
#   → 选方法必须在自己的数据上验证

关键要点

  • 最有价值的用法是建立「这类任务性能大概在什么水平」的参照系
  • 排行榜上常常缺少 ECFP + 树模型这个强基线——自己补跑才知道真实差距;
  • 常用数据集规模小、标签质量参差,刷分与实际有用是两件事;
  • 看时间趋势、方法分布、跨数据集一致性,比看名次有信息量得多

延伸资源