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 + 树模型这个强基线——自己补跑才知道真实差距;
- 常用数据集规模小、标签质量参差,刷分与实际有用是两件事;
- 看时间趋势、方法分布、跨数据集一致性,比看名次有信息量得多。
延伸资源
- 蛋白结构导航:027《Papers with Code 蛋白结构预测导航》;药物发现任务导航:028《Papers with Code 药物发现任务导航》;
- MoleculeNet:132《MoleculeNet 论文精读》;Benchmark 陷阱:169《Benchmark 陷阱》;论文阅读框架:170《AI 制药论文阅读框架》。