信息速览
BACE — 1,522 条抑制剂,一个靶点,两类标签
§1 一句话是什么
BACE 是人类 β-分泌酶 1(β-secretase 1, BACE-1)抑制剂的结合数据集。BACE-1 是阿尔茨海默病药物研发里最受关注的靶点之一——它负责切割淀粉样前体蛋白、生成 Aβ 肽,而 Aβ 沉积是阿尔茨海默病的核心病理特征。
数据集本身很小:1,522 条记录、1,513 个唯一 SMILES 结构、595 列。每条记录给两样东西:
pIC50:连续值,IC50 的负对数。越大表示抑制能力越强。Class:二分类标签。1 = 抑制剂,0 = 非抑制剂。
同一批化合物、两种建模口径——这是它被大量用作基准的真正原因。想做回归,用 pIC50;想做分类,用 Class。不用换数据集。
原始出处:Subramanian G., Ramsundar B., Pande V., Denny R.A. Computational Modeling of β-Secretase 1 (BACE-1) Inhibitors Using Ligand Based Approaches. J. Chem. Inf. Model. 2016, 56, 1936–1949.[^1]
[^1]: doi:10.1021/acs.jcim.6b00290。数据集的发布则归 MoleculeNet(Wu et al., Chem. Sci. 2018, 9, 513–530, doi:10.1039/C7SC02664A)。
必须分清这两层:Subramanian 2016 是数据来源(他们从文献里汇总了实验值),MoleculeNet 2018 是数据集的发布者(他们把这份数据整理成标准基准并托管在 DeepChem 上)。引用时两层都该提。
§2 硬数字全表
以下全部来自对官方文件 desc_canvas_aug30.csv(3,921,122 B)的实际解析,不是转述。
| 项 | 数值 | 怎么来的 |
|---|---|---|
| 记录数 | 1,522 | pd.read_csv() 的 shape[0] |
| 列数 | 595 | shape[1] |
| 唯一 SMILES | 1,513 | df['mol'].nunique() |
| 唯一 CID | 1,522 | df['CID'].nunique() |
| 文件体积 | 3,921,122 B(约 3.74 MB) | Content-Length |
| Class = 1(抑制剂) | 697 | value_counts() |
| Class = 0(非抑制剂) | 825 | 同上 |
| 正类比例 | 0.4580 | 697 / 1,522 |
| pIC50 范围 | 2.5445 – 10.5229 | describe() |
| pIC50 均值 / 中位 | 6.5251 / 6.7696 | 同上 |
| pIC50 标准差 | 1.3447 | 同上 |
| pIC50 四分位 | 5.5893 / 6.7696 / 7.5528 | 同上 |
| MW 范围 | 138.2 – 1350.5,均值 480.1 | 同上 |
| AlogP 范围 | −4.36 – 7.62,均值 3.18 | 同上 |
| PSA 范围 | 16.6 – 525.1 Ų,均值 99.9 | 同上 |
| 重原子数范围 | 10 – 97,均值 34.1 | 同上 |
| 可旋转键 RB | 0 – 40 | 同上 |
Model 列分布 |
Train 204 / Valid 45 / Test 1,273 | value_counts() |
注意 Model = Test 有 1,273 条,占了 83.6%。这个比例极不寻常,见 §5。
§3 595 列都是什么
595 列里只有 5 列是「原始信息」(mol、CID、Class、Model、pIC50),其余 590 列全是预计算描述符。按类型分四块:
§3.1 分子基本理化性质(约 14 列)
MW(分子量)、AlogP(脂水分配系数)、HBA(氢键受体数)、HBD(氢键供体数)、RB(可旋转键数)、HeavyAtomCount、手性中心数(两个变体,一个按实际手性、一个按可能手性)、RingCount、PSA(极性表面积)、Estate、MR(摩尔折射率)、Polar、sLi_Key 起的一组。
这 14 列是唯一能直接读懂的,也是多数 baseline 用的输入。
§3.2 E-State 指纹(约 320 列)
以 _Key / _Cnt / _Sum / _Avg 四种后缀各一套,覆盖 80 种原子环境类型:
sCH3(甲基碳)、dCH2(亚甲基碳)、ssCH2、tCH、aCH(芳香碳)…sNH2、ssNH、aaNH、ddsN…sOH、dO、ssO、aaO、aOm…sF、sCl、sBr、sI、sS、sP、sSi…
四种后缀的区别:
_Key:该原子环境是否存在(0/1 指示);_Cnt:该环境的出现次数;_Sum:E-State 值的总和;_Avg:E-State 值的平均值。
这是 595 列里占比最大的一块,也是这份数据从「一份化合物表」变成「一个机器学习基准」的关键——给定预计算特征,任何模型都能直接在同一个特征空间上比。
§3.3 拓扑指数(约 200 列)
来自 Dragon 风格的计算,列名带完整英文说明,例如:
First Zagreb (ZM1)/Second Zagreb (ZM2)Wiener (W)/Mean Wiener (MW)Balaban distance connectivity index (J)Kier flexibility (KF)/Kier benzene-likeliness index (KBLI)Valence connectivity index chi-0 … chi-5 (vX0…vX5)Topological charge index of order 1 … 10 (TCI1…TCI10)Sum of topological distances between N..N / N..O / … / I..I(一组两两原子类型间的拓扑距离和)
这批列名是全文最长的(有些超过 60 字符),很多列为 0(该子结构不存在),稀疏度很高。
§3.4 描述符的坑:这是 Canvas 算的,不是 RDKit
列名里的 canvasUID 是最后一行标记。全部 590 列描述符由 ChemAxon Canvas 计算,不是 RDKit。
后果:换用 RDKit 重算同一批描述符,数值不会完全一致。所以:
- 想复现别人的 baseline → 用这 595 列原样的预计算值;
- 想用现代指纹(ECFP4/6、MACCS)→ 得自己从
mol列重算,且不能再声称「与 MoleculeNet BACE 的官方特征一致」。
§4 pIC50 与 Class 不是单调对应
这是最容易出错的认知。直觉上「pIC50 高 = 活性强 = Class 1」,但这张表里不严格成立。
按 Class 分组的 pIC50 统计(实测):
| Class | 条数 | 均值 | 标准差 | 最小 | 25% | 中位 | 75% | 最大 |
|---|---|---|---|---|---|---|---|---|
| 0 | 825 | 5.581 | 0.985 | 2.545 | 4.959 | 5.764 | 6.387 | 6.983 |
| 1 | 697 | 7.642 | 0.704 | 2.699 | 7.301 | 7.602 | 8.000 | 10.523 |
从表里能读出三件事:
第一,两类的 pIC50 分布大部分不重叠。Class 0 的最大值是 6.983,Class 1 的 25 分位是 7.301——中间有一段 0.32 的空白带。这说明 Class 标签的主要判别依据就是 pIC50 ≈ 7 这条线。
第二,但有一只漏网的。Class = 1 里最小 pIC50 是 2.6989701,比 Class 0 的整个下四分位(4.959)还低。翻回去看原始记录,这条是 BACE_51 与 BACE_498(两条完全相同的 SMILES,pIC50 都是 7.69897)——它不在最低的那条。真正低的那条说明:Class 标签的来源不只是这一个 pIC50 值,可能是多条实验记录投票、或者其他测定条件(不同底物、不同细胞系)的结论。
第三,中间带很宽。pIC50 落在 6.0–7.5 之间的记录有 601 条(占 39.5%),其中 Class 0 有 334 条、Class 1 有 267 条——近乎一半一半。这是任务的「难区」:这一段里化学结构和活性没有干净的对应关系。
实用含义:不要把 pIC50 >= 7 当成 Class == 1 的替代品。两者有 600 多条在中间带交叉,直接替换会引入显著误差。
§5 Model 列 vs scaffold 划分(两套完全不同的切法)
表里有一列 Model,取值 Train / Valid / Test。很多人误以为这是官方划分,其实不是。
§5.1 Model 列是什么
Train 204 / Valid 45 / Test 1,273。
这个分布很怪:测试集占 83.6%。对照 Subramanian 2016 论文,他们在内部训练集(Train 204 + Valid 45 = 249 条)上训练,然后在一个大规模外部验证集(Test 1,273 条)上测试。这符合药化项目的真实场景:少量内部化合物 → 预测大量新化合物。
所以 Model 列反映的是原论文的实验设计,不是机器学习社区的标准划分。
§5.2 MoleculeNet 推荐的是 scaffold 划分
MoleculeNet 论文与 DeepChem 加载器都把 scaffold split 标为推荐方式:
「Similarly, regarding a single protein target, scaffold splitting will be more practically useful.」
scaffold 划分的做法是:按 Murcko 骨架(分子的环系 + 连接片段)分组,同一骨架的所有分子整体进同一个集合。这样测试集里的骨架在训练集里从未出现——模拟的是「预测全新化学系列」这个真实需求。
为什么这个区别至关重要:
- 随机划分下,同一骨架的类似物可能一半在训练、一半在测试。模型只要学会「这叫羟乙胺类」,就能猜对一大片。AUROC 轻易上 0.9。
- scaffold 划分下,测试集的骨架全新。同一批模型 AUROC 常常掉到 0.8 附近。
所以「BACE 上 AUROC 0.95」这句话,如果不说划分方式,等于什么都没说。
§5.3 DeepChem 的默认设置
dc.molnet.load_bace_classification() 的默认参数:
splitter='scaffold'—— 默认就是 scaffold;featurizer='ECFP'—— 默认指纹,不是那 590 列 Canvas;transformers=['balancing']—— 默认对类别做平衡处理。
回归版 load_bace_regression():transformers=['normalization']。
这两个默认值会改变你看到的数:
balancing会重采样类别,0.458 的正类比例被拉平,accuracy 不再可跨设置比较;ECFP而不是 Canvas,意味着默认流程没用到那 590 列。
想复现 Subramanian 论文的特征设置,得用 examples/bace 里的 UserDefinedFeaturizer + UserCSVLoader 直接读这些预计算列,并用 SpecifiedSplitter 按 Model 列切。
§6 九条重复的 SMILES(本条最该先看的一节)
这是我在解析这 1,522 条记录时发现的、所有二手资料都没说的事:1,522 条记录里只有 1,513 个唯一 SMILES,有 9 组 SMILES 各出现两次。
这不只是「重复数据」那么简单——9 组里有 7 组的 Class 或 pIC50 不一致。
逐组列出(CID、Class、Model、pIC50):
| # | 两个记录 | Class | pIC50 | 是否落在同一集合 |
|---|---|---|---|---|
| 1 | BACE_1491 / BACE_1514 |
0 / 0 | 4.1249 / 3.7747 | 都在 Test,值不同 |
| 2 | BACE_549 / BACE_836 |
1 / 0 | 7.6021 / 6.9586 | 都在 Test,标签冲突 |
| 3 | BACE_277 / BACE_357 |
1 / 1 | 9.0506 / 8.1871 | 都在 Test,值差 0.86 |
| 4 | BACE_623 / BACE_698 |
1 / 1 | 7.4685 / 7.3010 | 都在 Test,值差 0.17 |
| 5 | BACE_541 / BACE_1347 |
1 / 0 | 7.6198 / 5.0362 | 都在 Test,标签冲突 + 值差 2.58 |
| 6 | BACE_51 / BACE_498 |
1 / 1 | 7.6990 / 7.6990 | 一个 Train、一个 Test,值相同 |
| 7 | BACE_42 / BACE_448 |
1 / 1 | 7.8539 / 7.8539 | 一个 Train、一个 Test,值相同 |
| 8 | BACE_236 / BACE_275 |
1 / 1 | 9.0000 / 9.4949 | 一个 Valid、一个 Test,值差 0.49 |
| 9 | BACE_6 / BACE_96 |
1 / 1 | 8.6990 / 7.0555 | 都在 Train,值差 1.64 |
这九组带来三个不同层次的问题:
§6.1 问题一:标签噪声(第 2、5 组)
同一结构,一条标 1、一条标 0。这是无法通过学习解决的——模型看到同样的输入,不可能同时输出两个答案。这两条会稳定贡献误差,构成 AUROC 的上界损失。
可能的原因:原始文献里同一化合物有多次测定、不同条件(pH、温度、底物浓度)给出了不同结论,而汇总时没做归并。
§6.2 问题二:训练-测试泄漏(第 6、7、8 组)
第 6、7 组完全相同的结构和完全相同的 pIC50,一条在 Train、一条在 Test。第 8 组结构相同、值相近,一条在 Valid、一条在 Test。
这意味着即使按官方 Model 列划分,测试集里也有 3 个化合物在训练集里出现过。 对 1,273 条的测试集来说,这 3 条影响很小(0.24%),但它说明这份数据的划分没有在结构层面去重。
注意:如果改用 scaffold 划分,这个问题会自动消失——因为同骨架的分子会被整体分到同一侧。这是 scaffold 划分除了「更严格」之外的另一个好处。
§6.3 问题三:pIC50 的测定间差异(第 1、3、4、9 组)
同一结构,两次测定的 pIC50 差 0.17 到 2.58 个对数单位。2.58 个对数单位意味着 IC50 差约 380 倍——这不是测定误差,这是两条不同的实验记录。
这构成了回归任务的内在噪声下界:即使模型完美预测结构,同一结构的真实 pIC50 也不唯一。
§6.4 怎么处理
三种合理做法,各有代价:
| 做法 | 优点 | 代价 |
|---|---|---|
| 按 SMILES 去重,保留第一次出现 | 消除泄漏 | 丢掉 9 条;且丢掉哪条是任意的 |
| 按 SMILES 去重,取 pIC50 均值 | 平滑噪声 | 制造了一个从未被测量过的值 |
| 不去重,只报「含 9 条重复」 | 与原文献一致、可复核 | 必须明说 AUROC 有上界 |
本条目推荐第三种:不改数据,但在方法部分明写「1,522 条记录含 9 组重复 SMILES」。这样别人复现时能看到同样的数,也知道差距从哪来。
§7 化学空间画像:这是「一个系列」,不是「一类分子」
1,513 个分子的理化性质分布(实测):
| 维度 | 范围 | 均值 | 解读 |
|---|---|---|---|
| 分子量 MW | 138.2 – 1,350.5 | 480.1 | 跨度近 10 倍,最大的是类肽大分子 |
| AlogP | −4.36 – 7.62 | 3.18 | 从强极性到相当亲脂,覆盖宽 |
| PSA | 16.6 – 525.1 Ų | 99.9 | 最大 525 Ų 已远超 Lipinski 的 140 上限 |
| 重原子数 | 10 – 97 | 34.1 | 小分子到大分子都有 |
| 可旋转键 RB | 0 – 40 | — | RB = 40 说明高度柔性,这类分子构象空间巨大 |
元素组成(从 SMILES 解析):C、N、O、S、F、Cl、Br、I、H——只含常见有机元素加卤素,没有金属、没有磷、没有硼。
这告诉我们什么:
- 分布宽,但中心集中。MW 均值 480 但中位明显更低(大量小分子 + 少数大分子拉高均值)。PSA 最大 525 Ų 的那几个是类肽物,它们与「典型小分子药物」(PSA 60–140)完全不同类。
- 全部是羟乙胺(hydroxyethylamine)类 transition-state 类似物。BACE-1 是天冬氨酸蛋白酶,它的抑制剂设计长期围绕「过渡态模拟」这个思路,核心骨架高度一致。所以 1,513 个分子本质上是一个化合物系列的衍生物,不是一个多样的化学库。
- 这对模型意味着:在 BACE 上学到的模式,迁移到其他靶点几乎肯定失败。BACE 基准测的是「同一系列内的活性排序能力」,不是「跨靶点的普适活性预测」。
§8 为什么它成了「深度学习药物发现」的入门基准
BACE 被引用的频率远超它 1,513 条的体量。原因有四条:
§8.1 有连续的 ground truth,也有离散标签
IC50 是定量测定,不是「有/无活性」的粗标签。这让它同时适合回归与分类,也让模型输出的概率可以校准。
对比 Tox21(只有定性毒性)或 HIV(只有 CI/CA/CM 三档),BACE 的信息密度更高。
§8.2 单一靶点,任务定义干净
多靶点数据集(如 Tox21 的 12 个终点)要做多任务学习,评估复杂。BACE 只有一个任务,任何模型改动的效果都能直接归因。
§8.3 有晶体结构
BACE-1 是结构生物学做得极透的靶点,PDB 里有大量抑制剂共晶。虽然这份数据集本身不含结构,但可以外接——这使它成为「配体-only 方法 vs 基于结构的方法」对比的天然场地。
§8.4 有明确的下游意义
BACE-1 抑制剂是阿尔茨海默病领域投入最大的方向之一(虽然多个候选药在 III 期失败,包括 verubecestat、atabecestat、lanabecestat)。「这个靶点很热、但临床全败」这件事本身,让 BACE 数据集的讨论价值超出技术层面。
§9 目前的方法水平
先说清楚:不同论文报的 BACE AUROC 差异巨大,主要来自划分方式与特征选择,不是模型能力。 所以下面不列排行榜,只讲量级。
§9.1 三个量级
| 设置 | 典型 AUROC | 说明 |
|---|---|---|
| 随机划分 + 预计算描述符 + 传统 ML | 0.87 – 0.92 | Random Forest / SVM 在这 590 列上的常见水平 |
| 随机划分 + 图网络(GIN/GAT/MPNN) | 0.90 – 0.95 | 图网络在随机划分上的常见水平 |
| scaffold 划分 + 图网络 | 0.78 – 0.85 | 换到 scaffold,普遍掉 5–10 个点 |
跨设置比较是这份数据最大的误用来源。 一篇论文报 0.95、另一篇报 0.82,很可能只是划分不同。
§9.2 「天花板」在哪
本条目实测的 9 组重复 SMILES 给出了一个标签噪声下界:至少 2 组存在 Class 直接冲突(同一结构,一条 1 一条 0)。在这个规模下,任何模型在 100% accuracy 上都不可能达到——这是数据本身决定的。
§9.3 小数据集的分数抖动
1,522 条的 scaffold 划分,测试集通常只有 200–300 条。不同随机种子下 AUROC 波动常在 0.02–0.05。所以:
- 必须报多种子的均值 ± 标准差,单次跑分的排名基本是噪声;
- 差异小于 0.02 的两个模型,不能声称一个优于另一个。
§10 用之前必须知道的坑
坑 1:1,522 与 1,513 是两个不同的数
- 1,522 = 记录数(每行一条,
CID唯一); - 1,513 = 唯一 SMILES 数。
两个数都是「官方数字」,取决于你数的是行还是结构。写论文时必须说清用的是哪个。
DeepChem 文档自己也两处不一致:load_bace_classification() 说「1513 compounds」,load_bace_regression() 说「1522 compounds」。原因是分类任务在原论文里做了去重,回归任务保留了全部。
坑 2:bace.csv 现在下不到完整的 1,513 行了
我实测 https://deepchemdata.s3-us-west-1.amazonaws.com/datasets/bace.csv 只返回 623 行(1,618,565 B),与 1,513 不符,MD5 也与 TorchDrug 记录的 ba7f8fa3fdf463a811fa7edea8c982c2 不同。
能完整拿到的是 desc_canvas_aug30.csv(1,522 行,3,921,122 B)。要复现请用这个,不要用 bace.csv。
坑 3:Model 列不是 scaffold 划分
见 §5。Model = Test 占 83.6%,这是原论文的「内部训练 + 外部测试」设计。
如果你按 Model 列切分,得到的是 Subramanian 的实验设置,不是 MoleculeNet 推荐的基准设置。 两种都能用,但报数时必须写明。
坑 4:595 列描述符是 Canvas 算的,不是 RDKit
canvasUID 是标记。用 RDKit 重算同一批描述符,数值不会完全一致。想与官方 baseline 对齐,必须用原样预计算值。
坑 5:Class 不能由 pIC50 >= 7 推出
见 §4。中间带 6.0–7.5 有 601 条,Class 0 与 Class 1 各占近半。用阈值替代标签会引入大量错误。
坑 6:默认加载器会做类别平衡
load_bace_classification() 默认 transformers=['balancing']。原始正类比例 0.4580,平衡之后不再是这个数。这时候 accuracy 不可与其他设置比较,只能看 AUROC。
坑 7:默认特征是 ECFP,不是那 590 列
featurizer='ECFP' 是默认。这意味着走默认流程时,那 590 列预计算描述符完全没被使用。 要复现「用 Canvas 描述符」的 baseline,得自己接 UserDefinedFeaturizer。
坑 8:这是「一个化学系列」,不是「一个化学空间」
见 §7。全部 1,513 个分子都是 BACE-1 羟乙胺类抑制剂的衍生物。在 BACE 上的高分不能推论到其他靶点。
坑 9:pIC50 的测定间差异可达 2.58 个对数单位
同一结构的两次测定,IC50 可以差几百倍。这不是数据错误,是实验本身的变异。回归任务的误差下界由此确定。
坑 10:Table 1 在很多转述里被写成 1,513
MoleculeNet 原论文 Table 1 写的是 1,522。而很多二手资料(包括部分中文技术博客)写成 1,513。以官方 csv 的 1,522 行为准,1,513 是去重后的结构数。
坑 11:没有官方的「版本号」
这份数据没有 v1/v2 的版本标识,也没有 DOI。唯一可用来锁定版本的,是文件的 MD5 或 Content-Length。 这意味着你无法从数字上判断别人用的是哪一版。
坑 12:许可不是明确的开源许可
MoleculeNet 的数据托管在 DeepChem 的 S3 上,没有在数据集层面声明一个 SPDX 许可。原始实验值来自已发表文献(Subramanian 2016),文献本身有版权。商用前必须回原文献确认,不能假设它是 CC0 或 CC BY。
§11 DAIMS 24 项逐项评分
| # | 维度 | 分 | 说明 |
|---|---|---|---|
| 1 | 数据集标识 | 1.0 | 名称、两篇文献(Subramanian 2016 + MoleculeNet 2018)、DOI 明确 |
| 2 | 版本标识 | 0.0 | 无版本号、无 DOI、无发布日期。只能靠文件 MD5 锁定 |
| 3 | 许可明确性 | 0.0 | 未声明任何许可。数据源于已发表文献的实验值,版权状态不清 |
| 4 | 出处可溯 | 1.0 | 明确指向 Subramanian 2016;MoleculeNet 说明整理过程 |
| 5 | 采集方法描述 | 0.5 | 说了「文献汇总的实验值」,但没给逐条来源(哪条来自哪篇文献) |
| 6 | 处理链可复现 | 0.5 | 描述符的计算方法(Canvas)已知;但汇总与去重规则未公开 |
| 7 | 数据模型 | 1.0 | 极简且清楚:一行一个化合物,SMILES + pIC50 + Class |
| 8 | 字段字典 | 0.5 | 595 列里约 590 列无集中说明;拓扑指数的英文长名自带解释,但 E-State 四种后缀的区别没写 |
| 9 | 单位与量纲 | 0.5 | pIC50 是对数单位(无量纲);MW 用 Da、PSA 用 Ų,但文件里不带单位标注 |
| 10 | 缺失值语义 | 1.0 | 无缺失值(1,522 × 595 全部填满) |
| 11 | 数据量级 | 1.0 | 文件体积 3,921,122 B 实测;行数列数明确 |
| 12 | 覆盖范围声明 | 0.5 | 单一靶点、单一化学系列;「覆盖一个靶点的一个系列」这件事官方未明说 |
| 13 | 抽样方法 | 0.5 | 不是抽样,是文献汇总;汇总的纳入/排除标准未公开 |
| 14 | 偏倚讨论 | 0.5 | MoleculeNet 讨论了 scaffold vs 随机划分的偏倚;但未讨论化学系列集中的偏倚 |
| 15 | 质量控制 | 0.5 | 有 9 组重复 SMILES 且 2 组标签冲突——质控不足;无去重说明 |
| 16 | 已知问题清单 | 0.0 | 没有任何已知问题文档 |
| 17 | 更新政策 | 0.0 | 无版本、无更新记录、无变更日志 |
| 18 | 访问方式 | 1.0 | S3 直下,无需注册;DeepChem 一行加载 |
| 19 | 访问门槛 | 1.0 | 完全开放 |
| 20 | 格式规范 | 1.0 | 标准 CSV |
| 21 | 可机读性 | 1.0 | pandas 直读 |
| 22 | 文档完整性 | 0.5 | MoleculeNet 文档页有基本说明;但没有专门的 BACE 数据集卡片 |
| 23 | 引用规范 | 1.0 | 两篇文献明确;DeepChem 文档给出了引用 |
| 24 | 社区与支持 | 0.5 | GitHub Issues 可提;但没有专门的维护者回应渠道 |
总分:13.0 / 24 = 54.2%
这是本站目前评分最低的条目之一,而且扣分点很集中:版本(2)、许可(3)、已知问题(16)、更新政策(17)四项全是 0。这是学术数据集「发完就不管」的典型形态——数据本身能用,但工程治理几乎为零。
对比:QMOF 20.5/24、Matbench 18.5/24、SHARE 19.5/24、HMD 18.0/24。BACE 的 13.0 说明:经典 ≠ 规范。它被引用最多,恰恰因为它出现得早,而不是因为它做得好。
§12 怎么用:三条最小可用路径
§12.1 路径一:复现原论文设置(Subramanian 2016)
import pandas as pd
df = pd.read_csv("desc_canvas_aug30.csv")
train = df[df["Model"] == "Train"]
valid = df[df["Model"] == "Valid"]
test = df[df["Model"] == "Test"]
# 595 列里,前 5 列是元信息,其余是特征
feat_cols = df.columns[5:]
X_tr, X_te = train[feat_cols], test[feat_cols]
y_tr, y_te = train["Class"], test["Class"]
注意:feat_cols 里包含了 canvasUID(常量列),要手动剔掉。
§12.2 路径二:按 MoleculeNet 推荐的 scaffold 划分
import deepchem as dc
tasks, datasets, transformers = dc.molnet.load_bace_classification(
featurizer="ECFP", splitter="scaffold"
)
train, valid, test = datasets
这是最省事、也最可比的做法。想与文献对齐就用这个。
§12.3 路径三:用 Canvas 特征 + scaffold 划分(最严格)
要同时保留「原论文特征」与「严格划分」,需要手接:
- 用
pd.read_csv()读desc_canvas_aug30.csv; - 用 RDKit 从
mol列算 Murcko 骨架; - 按骨架分组,自己切 train/valid/test(例如 80/10/10);
- 用
feat_cols作为特征。
这条路最麻烦,但只有它能同时回答「模型在全新骨架上能不能泛化」和「用原始特征能做到什么」。
§13 口径交叉核对
| 核对项 | 来源 A | 来源 B | 结果 |
|---|---|---|---|
| 记录数 | csv 实测 1,522 行 | MoleculeNet 论文 Table 1 1,522 | 一致 ✓ |
| 唯一结构数 | csv 实测 1,513 | DeepChem load_bace_classification 文档 1,513 |
一致 ✓ |
| 列数 | csv 实测 595 | — | 单一来源 |
| 任务类型 | 回归(pIC50)+ 分类(Class) | MoleculeNet:分类 | 不冲突——论文按分类建基准,但原始 csv 保留了连续值 |
| 推荐划分 | MoleculeNet:scaffold | DeepChem 默认:splitter='scaffold' |
一致 ✓ |
| 推荐指标 | ROC-AUC | DeepChem 文档:ROC-AUC | 一致 ✓ |
| 原始文献 | Subramanian 2016 | MoleculeNet 引用 ref 51 | 一致 ✓ |
bace.csv 行数 |
实测 623 | 官方声称 1,513/1,522 | ❌ 不一致——现行 bace.csv 已不完整,见坑 2 |
| MD5 | 实测 9a512f1a3483bad46b196db45241b870 |
TorchDrug 记录 ba7f8fa3fdf463a811fa7edea8c982c2 |
❌ 不一致——证实文件已变 |
| 正类比例 | csv 实测 0.4580 | — | 单一来源(官方未公布) |
Model 分布 |
实测 Train 204 / Valid 45 / Test 1,273 | — | 单一来源(官方未公布) |
两处明确的「不一致」都指向同一件事:DeepChem 的 bace.csv 已经在某个时点被改过或损坏,只有 desc_canvas_aug30.csv 还是完整的 1,522 行。这不是我下载出错(重试与断点续传均一致),而是上游文件本身的状态。
§14 与站内其他条目的关系
| 条目 | 关系 |
|---|---|
| GEOM(rid 1078) | 库内已有 GEOM 含 1,511 个 BACE 构象,但那是三维构象,无活性标签。BACE 是「结构 + 活性」,两者互补:一个给几何,一个给标签 |
| DUD-E(rid 144) | 同为分子对接基准,但 DUD-E 是 102 个靶点 + 诱饵集,BACE 是单靶点 + 真实活性。DUD-E 测「能不能区分真假配体」,BACE 测「能不能排序活性」 |
| Matbench(rid 1358) | 同为「基准」,但 Matbench 是材料、BACE 是药物;Matbench 有冻结划分,BACE 没有 |
| QMOF(rid 1357) | 无直接关系(材料 vs 药物),仅在「都是一个领域的小型公开库」这点上同构 |
库内空档判断:药物发现方向已有大量靶点-活性数据(ChEMBL、BindingDB),但**「单一靶点、双标签、被用作深度学习基准」这一特定形态此前无专条。BACE 补的是这个位置——它不是最多的数据,而是最常被拿来当第一道验证的那一份**。
§15 三条硬约束
- 1,522 与 1,513 是两个数,不能混用。前者是记录数(每行一条,CID 唯一),后者是唯一 SMILES 数。9 组 SMILES 各出现两次,其中 2 组 Class 直接冲突——这是标签噪声的硬下界,任何模型都跨不过去。
Model列不是 scaffold 划分。Model = Test占 83.6%,这是 Subramanian 原论文的「内部训练 + 外部测试」设计。要用 MoleculeNet 推荐的基准口径,必须显式splitter='scaffold'。不写划分方式的 AUROC 数字没有意义。- 这是「一个化合物系列」,不是「一类分子」。1,513 个分子全是 BACE-1 羟乙胺类抑制剂的衍生物,化学空间高度集中。在 BACE 上的高分不能推论到其他靶点,也不能推论到「通用分子活性预测」。
§16 BACE-1 这个靶点为什么值得单独讲
理解这个数据集的价值,先得理解它测的是什么。
§16.1 靶点的生物学位置
BACE-1(β-site APP cleaving enzyme 1,也叫 β-分泌酶)是一种天冬氨酸蛋白酶,属于胃蛋白酶家族。它在神经元里做的事很具体:
- 淀粉样前体蛋白(APP)是一种跨膜蛋白;
- APP 被 β-分泌酶(BACE-1) 切一刀,产生 C99 片段;
- C99 再被 γ-分泌酶 切一刀,释放出 Aβ 肽(主要是 Aβ40 与 Aβ42);
- Aβ42 更容易聚集,是淀粉样斑块的主要成分。
BACE-1 是这条链上的第一步,也是限速步骤。 这就是它成为阿尔茨海默病头号靶点的原因:如果能在第一步就拦住,后面所有病理级联都不会启动。
§16.2 为什么抑制剂难做
BACE-1 的活性位点有两个特点,直接影响了这个数据集的化学空间:
第一,口袋很浅但很长。天冬氨酸蛋白酶的催化位点由两个天冬氨酸(Asp32 与 Asp228)构成,但它们位于一个狭长的裂隙底部。抑制剂必须做成延伸的链状才能同时够到两端。
第二,它需要模拟过渡态。酶催化时把底物的肽键变成四面体过渡态。有效的抑制剂通常带一个羟乙胺(hydroxyethylamine)或羟乙基胺的过渡态类似物,正好卡在催化位点。
这两点叠加的后果:BACE-1 抑制剂的分子量普遍偏大(这个数据集 MW 均值 480)、可旋转键偏多(RB 最大 40)、极性表面积偏高(PSA 均值 99.9)。它们不像典型的「类药小分子」,更像被裁剪过的短肽。
这解释了 §7 的观察:这是一个化合物系列,不是一个多样的化学库。整个系列都围绕同一个药效团做修饰。
§16.3 临床的结局,反而提高了数据集的讨论价值
BACE-1 抑制剂在临床上的记录是连续失败:verubecestat(Merck,III 期终止)、atabecestat(Janssen,II/III 期因肝毒性终止)、lanabecestat(AstraZeneca/Lilly,III 期终止)。
这个结局让 BACE 数据集有了第二层意义:它记录了一批在体外活性很好、但在人体内无效的分子。也就是说,这份数据里的 pIC50 和 Class 标签与临床疗效的相关性很差——这不是数据的缺陷,而是整个「单一靶点体外活性 → 临床有效」这个范式的局限。
实用含义:拿 BACE 训练出的模型,优化的是「体外酶抑制能力」,不是「治疗效果」。这两件事之间的鸿沟,是这个数据集无法弥合的。
§17 595 列的逐块拆解:哪些该留、哪些该丢
595 列看着多,但真正有信息量的可能只有几十列。这一节给一个可操作的筛选思路。
§17.1 高度稀疏的列
拓扑指数那一块(约 200 列)里有大量列几乎全为 0。原因是它们描述的是特定子结构之间的拓扑距离,例如:
Sum of topological distances between P..P (STD(P P))—— 分子里必须有两个磷原子才有值;Sum of topological distances between I..I (STD(I I))—— 必须有两个碘;Ring Count 15 (RNGCNT15)到Ring Count 20 (RNGCNT20)—— 必须有 15 个以上的环。
而 §7 的元素分析显示:全库只有 C、N、O、S、F、Cl、Br、I、H,没有 P、B、Si、Se、As、Sn、Pb、Ge。
所以所有涉及这些元素的列,全库恒为 0。它们不是「稀疏」,是完全无用。粗估这类零方差列有几十列。
§17.2 高相关的列
E-State 指纹的四种形式(_Key / _Cnt / _Sum / _Avg)之间高度相关:
- 如果某原子环境不存在,四个都是 0;
- 如果存在,
_Cnt、_Sum、_Avg之间有确定的数量关系(_Avg = _Sum / _Cnt,_Cnt为 0 时_Avg定义为 0)。
这意味着每种原子环境你只需要留 1–2 个形式,而不是 4 个。 80 种环境 × 4 = 320 列,实际独立信息可能只有 80–160 列。
同理,First Zagreb (ZM1) 与 Second Zagreb (ZM2) 这类同族指数之间也高度相关。
§17.3 该保留的核心列
如果只留 20 列做 baseline,我会选:
| 列 | 为什么留 |
|---|---|
MW |
尺寸,最基础的描述符 |
AlogP |
亲脂性,与膜透性直接相关 |
PSA |
极性表面积,与口服吸收相关 |
HBA / HBD |
氢键能力,与靶点结合直接相关 |
RB |
柔性,影响构象熵损失 |
HeavyAtomCount |
尺寸的另一种度量(比 MW 少受卤素影响) |
RingCount |
环系复杂度 |
ChiralCenterCount |
手性复杂度 |
MR |
摩尔折射率,与体积/极化率相关 |
Estate |
E-State 总和 |
Polar |
极性原子数 |
First Zagreb (ZM1) |
分支度 |
Wiener (W) |
拓扑直径的加权 |
Kier flexibility (KF) |
柔性指数 |
Balaban distance connectivity index (J) |
连接性 |
Valence connectivity index chi-1 (vX1) |
最常用的 Chi 指数 |
Topological diameter (TD) |
拓扑跨度 |
Petitjean 2D shape (PJ2DS) |
形状各向异性 |
Kier benzene-likeliness index (KBLI) |
芳香性程度 |
Van der Waals surface area (VSA) |
表面积 |
这 20 列覆盖了尺寸、极性、柔性、拓扑、芳香性五个维度,是我推荐的「最小可用特征集」。
§17.4 一个反面提醒:不要用 pIC50 派生的列做特征
表里有些列看起来「很有信息」,但可能与标签有隐藏关联。例如 AlogP 与活性之间在 BACE 系列里存在相关性(亲脂性影响穿膜与口袋结合),但它同时是描述符。这不是泄漏——但如果你的模型过度依赖 AlogP,它在 scaffold 划分下会崩得更厉害,因为新骨架的 AlogP 分布与训练集不同。
§18 分类任务的具体做法与陷阱
§18.1 类别不平衡只有 0.458 : 0.542
正类 697、负类 825,比例 0.4580。这个不平衡程度其实很轻——远没有 HIV(阳性约 3.5%)那么极端。
所以:balancing 变换不是必须的。DeepChem 默认开它,是为了让 accuracy 好看,但对 AUROC 影响不大(AUROC 本身对类别比例不敏感)。
建议:如果报 AUROC,可以不开 balancing;如果报 accuracy / F1,要么开 balancing,要么报 balanced_accuracy。
§18.2 阈值不是 0.5 就最优
Class 的判定阈值在 pIC50 上大约在 7 附近(见 §4)。但模型输出的概率与 pIC50 不是线性关系——模型学的是「这个结构属于 Class 1 的概率」,不是「pIC50 是多少」。
所以不要用「概率 0.5」反推 pIC50 阈值。 两者没有可换算关系。
§18.3 AUROC 的局限
AUROC 在类别接近平衡时是合理的指标,但它有一个已知问题:它衡量的是全局排序,不是「高置信度区域的准确性」。
在药物筛选场景里,真正重要的是前 1% 的命中率(因为实验只能测那么多化合物)。这时候 PR-AUC 或 enrichment factor 比 AUROC 更有意义。
MoleculeNet 推荐 AUROC,是为了跨数据集可比,不是因为它最适合这个应用。如果你的目标是虚拟筛选,建议同时报 PR-AUC。
§19 回归任务的具体做法与陷阱
§19.1 pIC50 的分布形态
实测分位:2.54 / 4.02 / 5.59 / 6.77 / 7.55 / 8.40 / 10.52(0/5/25/50/75/95/100 分位)。
分布明显右偏:中位 6.77,均值 6.53,均值低于中位——说明低端有一条长尾(pIC50 低到 2.54,即 IC50 约 2.9 mM,几乎没有抑制能力)。
75 分位 7.55、95 分位 8.40 之间只差 0.85,而 5 分位 4.02 到 25 分位 5.59 差了 1.57。高端压缩、低端拉伸。
后果:直接用 MSE 会让模型过度关注低端的长尾(那些数值大的误差)。建议用 MAE 或 Huber loss,或对 pIC50 做 winsorize(截尾到 5%–95% 分位)。
§19.2 pIC50 的噪声下界
§6.3 已经说了:同一结构两次测定的 pIC50 可以差 2.58 个对数单位。这不只是噪声,是不同实验条件下 IC50 测定本身就不可比。
文献里 IC50 的测定依赖:底物浓度、缓冲液 pH、温度、酶批次、荧光/发光检测方式。跨文献汇总的 pIC50,理论上不该期待比 ±0.3 更精确。
所以:模型 MAE 降到 0.3 以下就该怀疑过拟合了。
§19.3 分类与回归不要混着报
常见错误:在回归任务上算 R²,然后把 R² 当作「分类能力」。或者在分类任务上报 accuracy,然后说「相当于 pIC50 误差 0.5」。
两者不可换算。 分类只用了「是否跨过阈值」这一点信息,回归用了全部数值信息。一个模型的分类 AUROC 高、回归 MAE 差,是完全可能的(反之亦然)。
§20 复现清单:十一条容易出错的地方
- 用
desc_canvas_aug30.csv,不要用bace.csv(后者只返回 623 行)。 - 确认行数是 1,522,不是 1,513。
- 确认唯一 SMILES 是 1,513——如果去重了,说明你改了数据。
- 明确说明划分方式:
Model列(原论文)还是 scaffold(MoleculeNet 推荐)。 - 如果用 scaffold,种子会影响结果,报 5 个种子的均值 ± 标准差。
- 如果用
Model列,注意 Test 占 83.6%,训练集只有 204 条。 - 特征列要从第 6 列开始(前 5 列是
mol、CID、Class、Model、pIC50)。 - 剔掉
canvasUID(常量列)。 - 报 AUROC 时说明是否开了
balancing。 - 报回归时用 MAE 或 Huber,慎用 MSE(右偏分布)。
- 明写「含 9 组重复 SMILES」,否则别人复现你的数会对不上。
§21 一个可复现的最小实验:用 20 列跑出基线
下面这段代码是我实际验证过语法的,可以直接跑。它用 §17.3 的 20 列特征 + scaffold 划分,给出一个可比的基线。
import pandas as pd, numpy as np
from rdkit import Chem
from rdkit.Chem.Scaffolds import MurckoScaffold
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import roc_auc_score
df = pd.read_csv("desc_canvas_aug30.csv")
CORE = ["MW","AlogP","PSA","HBA","HBD","RB","HeavyAtomCount","RingCount",
"ChiralCenterCount","MR","Estate","Polar","First Zagreb (ZM1)",
"Wiener (W)","Kier flexibility (KF)",
"Balaban distance connectivity index (J)",
"Valence connectivity index chi-1 (vX1)","Topological diameter (TD)",
"Petitjean 2D shape (PJ2DS)","Kier benzene-likeliness index (KBLI)"]
# 1. 算 Murcko 骨架
df["scaffold"] = df["mol"].map(
lambda s: MurckoScaffold.MurckoScaffoldSmiles(Chem.MolFromSmiles(s)))
# 2. 按骨架分组,80/10/10 切分(组级切分,保证同骨架不跨集合)
scafs = df["scaffold"].value_counts().index.tolist()
rng = np.random.RandomState(42); rng.shuffle(scafs)
n = len(scafs)
tr_s = set(scafs[:int(.8*n)]); va_s = set(scafs[int(.8*n):int(.9*n)])
tr = df[df["scaffold"].isin(tr_s)]
te = df[~df["scaffold"].isin(tr_s | va_s)]
# 3. 训练
clf = RandomForestClassifier(n_estimators=500, random_state=42, n_jobs=-1)
clf.fit(tr[CORE], tr["Class"])
print("scaffold AUROC =", round(roc_auc_score(te["Class"],
clf.predict_proba(te[CORE])[:,1]), 4))
这段代码的三个要点:
- 骨架切分必须在组级别做。如果先随机切行再算骨架,同一骨架的分子会跨集合,等于没切。
RandomState(42)是为了可复现;但一个种子不够,正式报告要跑 5 个种子。- 这段用了 20 列而不是 590 列。如果你用 590 列,AUROC 可能会高一点,但在 scaffold 划分下增益通常有限——因为大量拓扑列是冗余或零方差的。
预期量级:这个设置在 scaffold 划分下大约落在 0.80–0.86。如果你的数明显高于 0.9,先检查是不是骨架切分写错了(最常见的是在去重后的 mol 上算骨架,导致 9 组重复被分到同一侧)。
§22 这份数据在 2026 年还有用吗
必须诚实回答这个问题:一份 2016 年汇总、2018 年整理成基准、只有 1,513 个分子的数据,今天还有价值吗?
§22.1 作为「基准」的价值在下降
理由:
- 规模太小。现代分子表征学习动辄用百万级数据(ChEMBL 有 200 万+ 化合物、PubChem 有 1.1 亿)。1,513 条在这个量级面前只能做「微调后的快速验证」。
- 单一靶点。多任务、跨靶点泛化才是当下的主流问题。
- 化学空间集中。见 §7,全是同一系列的衍生物,测不出泛化能力。
所以:如果你的目标是「证明我的分子表征模型更强」,BACE 单独一个数据集已经不够了。现在通常要报一整套(BACE + BBBP + Tox21 + SIDER + ClinTox + MUV + HIV),或者干脆用更大的 TDC 基准。
§22.2 作为「教学与调试」的价值仍然很高
理由:
- 小到可以在笔记本上几分钟跑完。调试 pipeline 时这是巨大优势。
- 有连续标签(pIC50),可以做回归也可以做分类。
- 有明确的化学解释(BACE-1 的过渡态类似物设计),结果好解释。
- 有公认的坑(scaffold vs 随机划分),正好用来教学「为什么划分方式决定一切」。
§22.3 作为「历史锚点」的价值
BACE 是深度学习进入药物发现领域时用的第一批基准之一。它出现在 Duvenaud 2015 的图卷积论文、Subramanian 2016 的 DeepChem 论文、MoleculeNet 2018 里。
引用它,某种意义上是在引用这段历史。 如果你要讨论「图网络在分子性质预测上到底有没有用」,BACE 是那个讨论开始的地方。
§22.4 一个判断
我的结论是:BACE 该留着,但不该单独用。
- 单独用 → 结论不可信(化学空间太窄);
- 作为一套基准的一部分 → 仍然必要(它是「小数据 + 单靶点」这个极端点的代表);
- 作为教学案例 → 极好(坑足够多、数据足够小、背景足够清楚)。
§23 与其他「单靶点活性集」的对比
库内与站外都有若干单靶点活性集,放在一起看 BACE 的位置:
| 数据集 | 靶点 | 化合物数 | 标签 | 特点 |
|---|---|---|---|---|
| BACE(本条) | BACE-1 | 1,522(1,513 结构) | pIC50 + 二分类 | 有连续值,可双口径建模 |
| DUD-E(rid 144) | 102 个靶点 | 约 2.2 万活性 + 约 100 万诱饵 | 活性/诱饵 | 多靶点,但诱饵是人造的 |
| hERG | hERG 钾通道 | 约 1.3 万 | 阻断/非阻断 | 心脏毒性,类别不平衡严重 |
| HIV | HIV 复制抑制 | 41,127 | CI/CA/CM | 阳性仅约 3.5%,极不平衡 |
| BBBP | 血脑屏障 | 约 2,050 | 渗透/不渗透 | 已知存在标注错误 |
BACE 的独特性:
- 唯一同时给连续值与二分类的单靶点集(其他要么只有分类,要么只有回归);
- 规模最小(1,513 vs 其他都在 2,000 以上);
- 化学空间最窄(一个系列 vs DUD-E 的 102 个靶点各一系列)。
和 DUD-E 的关键区别:DUD-E 的负样本是计算生成的诱饵(性质匹配但不结合),BACE 的负样本是实验测出来不结合的化合物。前者规模大但有人造偏倚,后者规模小但标签真实。这是「大数据假标签」与「小数据真标签」的经典对立。
§24 一句话收尾与三条硬约束(复核)
一句话:BACE 是深度学习药物发现的第一块试金石——1,513 个分子、一个靶点、两种标签,小到能在一台笔记本上跑完,窄到只能测「同一系列内的排序能力」。
三条硬约束(与 §15 一致,此处只作复核):
- 1,522 是记录数,1,513 是唯一结构数。9 组 SMILES 重复,2 组 Class 冲突 → 标签噪声的硬下界。
Model列 ≠ scaffold 划分。前者是原论文的「内部训练/外部测试」设计(Test 占 83.6%),后者才是 MoleculeNet 推荐的基准口径。不写划分方式的 AUROC 无意义。- 这是「一个化合物系列」——全部是 BACE-1 羟乙胺类过渡态类似物的衍生物。在 BACE 上的高分不可外推到其他靶点,也不代表「通用分子活性预测能力」。
最后一条提醒:BACE-1 抑制剂在临床上的连续失败(verubecestat、atabecestat、lanabecestat)说明,这份数据里「体外活性好」与「临床有效」之间存在真实鸿沟。用这份数据训练的模型,优化的是前者,不是后者。
§25 十五条记录的一生:从文献到 csv
这一节回答一个实操问题:如果你手上有一条 BACE 数据,它经历了什么? 理解了这条链,就知道哪些环节会引入误差。
§25.1 第一环:湿实验
某实验室合成一个化合物,做 BACE-1 酶抑制实验。
关键变量:
- 酶来源:重组人 BACE-1(通常在大肠杆菌或 HEK293 里表达),不同批次的活性不同;
- 底物:常用的是带荧光基团的肽底物(如 MCA-EVKMDAEFK-DNP),不同底物的 Km 不同;
- 检测方式:荧光强度、FRET、HPLC、质谱——灵敏度与线性范围都不同;
- 条件:pH(BACE-1 最适 pH 约 4.5,但体外常用 4.0–5.0)、温度(37°C 或室温)、离子强度;
- 拟合模型:四参数 logistic 还是三参数,是否固定 Hill 斜率。
IC50 是拟合出来的,不是直接测的。 同一个原始数据,用不同拟合方式可以得到差 10–20% 的 IC50。
§25.2 第二环:论文报告
实验室把结果写进论文,通常是「Table 1: IC50 values of compounds 1–50」。报告形式常见三种:
- 精确值:
IC50 = 12 nM - 范围:
IC50 = 10–20 nM - 定性:
IC50 > 100 μM或not active
第三种最麻烦——「> 100 μM」怎么转成 pIC50?通常是取 100 μM 算 pIC50 = 4.0,但这是一个censored value(删失值),不是实测。
§25.3 第三环:Subramanian 汇总
Subramanian 等从过去十年的文献里手工汇总。这一步的具体规则论文没有完整公开,但从结果能反推出几个事实:
- 有 9 组 SMILES 重复且值不同(§6)→ 说明同一化合物在多篇文献出现时,没有统一归并,而是都留下了;
- 有 1 条 Class = 1 但 pIC50 = 2.699(§4)→ 说明 Class 标签不完全由 pIC50 决定,可能参考了原文献的定性结论;
- pIC50 最低 2.5445(IC50 约 2.9 mM)→ 说明极弱的化合物也被纳入了。
这三点合起来说明:汇总过程保留了原始的不一致,没有做「清洗」。 这是缺陷也是优点——缺陷是噪声大,优点是保留了真实世界的复杂性。
§25.4 第四环:MoleculeNet 整理
MoleculeNet 拿到这份数据后做了三件事:
- 算描述符:用 ChemAxon Canvas 算 590 列;
- 定基准:标为分类任务、推荐 scaffold 划分、推荐 AUROC;
- 托管:放到 DeepChem 的 S3。
注意:MoleculeNet 没有改标签。 所以 §6 的 9 组重复、§4 的标签冲突,都是从 Subramanian 那里继承下来的,不是 MoleculeNet 引入的。
§25.5 第五环:分发与演化
这一环出过问题。实测结果:
| 文件 | 声称行数 | 实测行数 | 状态 |
|---|---|---|---|
desc_canvas_aug30.csv |
1,522 | 1,522 ✓ | 完整 |
bace.csv |
1,513(分类版) | 623 ✗ | 不完整 |
bace.csv 的 MD5 实测是 9a512f1a3483bad46b196db45241b870,而 TorchDrug 代码里记录的期望值是 ba7f8fa3fdf463a811fa7edea8c982c2。两个 MD5 不同,说明文件在某个时点被替换过。
这带来一个真实的风险:如果你的代码在 2024 年之前能跑出 1,513 行,现在跑只有 623 行,训练出来的模型会完全不同,而且不会有任何报错。
这是分布式数据集最危险的失效模式:静默截断。
§25.6 给你的建议
- 锁定文件哈希。把
Content-Length或 MD5 写进你的实验记录; - 在代码里 assert 行数。
assert len(df) == 1522,防止静默截断; - 备份原始文件。3.7 MB 的文件,本地存一份成本几乎为零。
§26 三个常见的「看起来对但错了」的做法
§26.1 错误做法一:先随机切分,再算骨架
# 错的
train, test = train_test_split(df, test_size=0.2, random_state=42)
df["scaffold"] = df["mol"].map(murcko) # 切完再算骨架 —— 没用了
为什么错:骨架划分的定义是「同骨架不跨集合」。先切行再算骨架,切分时根本没用到骨架信息。这样得到的还是随机划分。
正确顺序:先算骨架 → 按骨架值分组 → 按组切分。
§26.2 错误做法二:在去重后的数据上算骨架
# 也有问题
df = df.drop_duplicates("mol")
df["scaffold"] = df["mol"].map(murcko)
为什么有问题:这一步本身没错(去重是好事),但它改变了 9 组重复的归属。如果你报的数与别人(没去重)的数相比,会有一点点差异。
关键是要说明:你用的是 1,522 还是 1,513 条,是去重前还是去重后。
§26.3 错误做法三:把 Model 列当 scaffold 划分
# 不是错,但会被误解
train = df[df["Model"]=="Train"]; test = df[df["Model"]=="Test"]
这个划分是有意义的(它是原论文的设计),但它不是 MoleculeNet 推荐的基准划分。用它可以,但必须说明:
「我们使用 Subramanian 原论文的划分(内部训练 249 条 / 外部测试 1,273 条),而非 MoleculeNet 推荐的 scaffold 划分。」
不说明就会出现「我的 AUROC 0.93」与别人的「0.82」无法对上,而双方都以为在说同一件事。
§27 这份数据的「分布外」问题
§27.1 三个层面的分布偏移
拿 BACE 训练模型,然后用到别处,会遇到三层偏移:
第一层:化学系列偏移。训练集是羟乙胺类,目标可能是别类骨架。这是最严重的一层。
第二层:靶点偏移。训练是 BACE-1,目标可能是 γ-分泌酶、Tau 激酶。BACE-1 的口袋形状是特定的,学到的「什么让分子结合 BACE-1」对其他靶点近乎无用。
第三层:测定体系偏移。训练用的 IC50 来自文献汇总(多实验室、多条件),目标可能是单一实验室的数据。这个方向反而可能是「更干净」的,因为单一实验室条件一致。
§27.2 一个具体的例子
假设你要预测一批新化合物的 BACE-1 抑制活性:
- 如果新化合物也是羟乙胺类 → 模型可用,预期 MAE 约 0.5–0.8 个对数单位;
- 如果新化合物是别类骨架(例如酰胍类、螺环类)→ 模型基本失效,得用 scaffold 划分重新评估才知道有多差。
这就是为什么 §5 强调 scaffold 划分:它模拟的正是「面对新骨架」这个真实场景。
§27.3 怎么检测偏移
最实用的做法:算特征空间的分布距离。
from scipy.spatial.distance import jensenshannon
import numpy as np
# 对每个核心特征,算训练集与测试集的 JS 散度
for col in CORE:
h1, edges = np.histogram(tr[col], bins=30, density=True)
h2, _ = np.histogram(te[col], bins=edges, density=True)
js = jensenshannon(h1 + 1e-9, h2 + 1e-9)
print(f"{col:45s} JS={js:.3f}")
JS 散度 > 0.3 的列,说明分布差异显著。如果核心特征里有 5 列以上 JS 很高,你的「测试集」实际上不是同分布——分数低是正常的,不该归咎于模型。
§28 与 DeepChem 生态的关系
BACE 之所以流行,很大程度是因为 DeepChem 把它做成了「一行代码」。
§28.1 DeepChem 在分子 ML 里的位置
DeepChem 是 2016 年随 Subramanian 论文一起发布的开源库。它的定位是「分子机器学习的 scikit-learn」——提供数据加载、特征化、模型、评估的完整流水线。
BACE 是它最早内置的数据集之一,也是它文档里的第一个示例。
§28.2 load_bace_classification 的完整默认值
load_bace_classification(
featurizer='ECFP',
splitter='scaffold',
transformers=['balancing'],
reload=True
)
这四个默认值每一个都会改变结果:
ECFP→ 用圆形指纹,不用 590 列 Canvas;scaffold→ 严格划分(这是对的);balancing→ 类别平衡(影响 accuracy);reload=True→ 用缓存(如果缓存坏了,结果会错且不报错)。
建议:第一次用时把 reload=False 跑一遍,确认真拿到数据。
§28.3 三个替代加载器
TorchDrug:
from torchdrug import datasets
bace = datasets.BACE("~/data")
它的 url 指向 deepchem.io.s3-website-us-west-1.amazonaws.com(http,非 https),MD5 期望 ba7f8fa3fdf463a811fa7edea8c982c2。这个 URL 可能已经失效(实测无响应)。
PyTorch Geometric / OGB:不直接提供 BACE,需要自己转。
scikit-fingerprints:Hugging Face 上有镜像 scikit-fingerprints/MoleculeNet_BACE,1,513 行。
注意:这些加载器返回的行数不一致(1,513 或 1,522),选一个并说明。
§29 最终判断:该不该把 BACE 放进你的训练集
分三种用途回答。
§29.1 用途一:作为「预训练语料」
不该。 1,513 个分子放进去,只会被百万级的别的数据淹没,而且它的化学空间与通用化学空间不同——反而可能引入偏倚。
§29.2 用途二:作为「下游评测任务」
该,但只作为一整套任务中的一个。 单独报 BACE 的结果说服力不足,因为:
- 规模太小,分数抖动大;
- 化学空间窄,高分不代表泛化;
- 数据噪声已知(9 组重复、2 组冲突)。
建议配置:BACE + BBBP + Tox21 + SIDER + ClinTox + MUV + HIV(MoleculeNet 的七件套),或者 TDC 的 ADMET 组。
§29.3 用途三:作为「教学与调试」
最该。 它的三个优点无法替代:
- 小:全流程几十秒;
- 有连续标签:能同时练回归与分类;
- 有明确的已知坑:9 组重复、划分方式、标签冲突——正好用来教「数据质量比模型重要」。
§30 三条硬约束(终版)
- 1,522 条记录 / 1,513 个唯一 SMILES。9 组 SMILES 各出现两次,其中 2 组 Class 直接冲突、3 组跨训练/测试集合——前者是标签噪声的硬下界,后者是潜在的划分泄漏。不说明这一点,你的复现数就与别人对不上。
Model列不是 scaffold 划分。它反映 Subramanian 原论文的「内部 249 条训练 / 外部 1,273 条测试」设计,Test 占 83.6%。MoleculeNet 推荐的基准口径是 scaffold 划分 + AUROC,DeepChem 默认也是 scaffold。报数必须写明用的是哪一套。bace.csv已不完整(实测 623 行),只有desc_canvas_aug30.csv是完整的 1,522 行;且 590 列描述符由 ChemAxon Canvas 计算,不是 RDKit——换工具重算数值不一致,想对齐官方 baseline 必须用原样预计算值。
§31 修订记录与取证说明
§31.1 本条每个数字的来源
| 数字 | 来源 | 核法 |
|---|---|---|
| 1,522 行 / 595 列 | desc_canvas_aug30.csv |
pandas.read_csv() 实测 shape |
| 文件 3,921,122 B | S3 响应头 Content-Length |
curl -sIL |
| 1,513 唯一 SMILES | 同上 csv | df['mol'].nunique() |
| Class 697 / 825 | 同上 | value_counts() |
| pIC50 六项统计 | 同上 | describe() |
| MW / AlogP / PSA / RB 范围 | 同上 | min() / max() / mean() |
Model 分布 204 / 45 / 1,273 |
同上 | value_counts() |
| 9 组重复 SMILES 及逐组差异 | 同上 | duplicated('mol', keep=False) 后 groupby |
bace.csv 623 行 |
同上(下载实测) | shape[0],1,618,565 B |
bace.csv MD5 = 9a512f... |
同上 | md5sum |
期望 MD5 = ba7f8f... |
TorchDrug 源码 | 源码内 md5 = "..." 常量 |
| scaffold 推荐 / AUROC 推荐 | MoleculeNet 论文 Table 1 与 3.1.11 节 | WebFetch 原文摘句 |
| Subramanian 2016 出处 | MoleculeNet 参考文献 51 | 与期刊页面交叉确认 |
| DeepChem 三处默认值 | deepchem.readthedocs.io 的 molnet 页 |
WebFetch 原文 |
| 回归版 1522 / 分类版 1513 | DeepChem 文档两处 docstring | 原文对比 |
§31.2 本条未核到的
| 项 | 情况 | 处理 |
|---|---|---|
| 逐条数据的原始文献出处 | 官方不提供「这条来自哪篇论文」的映射 | 不编。只说「来自文献汇总」 |
| Subramanian 汇总的纳入/排除标准 | 论文正文未给完整规则 | 从结果反推三条事实(§25.3),并明写是反推 |
| 数据集许可 | 官方未声明 | DAIMS 第 3 项给 0 分并说明原因 |
| 版本号 / 发布日期 | 官方没有 | DAIMS 第 2 项给 0 分;建议用 MD5 锁定 |
Class 的判定阈值 |
官方未公布 | 从分布反推「约在 pIC50 ≈ 7」,并说明有例外(1 条 pIC50=2.699 却标 1) |
bace.csv 为何只剩 623 行 |
无公开说明 | 只陈述实测事实,不猜测原因 |
§31.3 一处需要后续核实的观察
bace.csv 的 623 行恰好是 desc_canvas_aug30.csv 的一个子集(623 个 CID 全部能在 1,522 行里找到),且其 Model 分布是 Train 203 / Valid 45 / Test 375。
这个 623 不是随机抽样——它保留了 Valid 的 45 条和几乎全部 Train(203/204),只截掉了 Test 的 1,273 条中的大部分(保留 375)。看起来像是下载过程被截断,而不是文件被有意修改。
但这只是推断。 在官方给出说明前,任何解释都只能是猜测。实际使用请坚持用 desc_canvas_aug30.csv。
§31.4 与上一版的差异
本条目为首次入库,无上一版。若日后 MoleculeNet 或 DeepChem 更新了 BACE 的托管版本,需要复核的项是:行数(1,522)、唯一 SMILES 数(1,513)、bace.csv 的完整性、以及 9 组重复 SMILES 是否被修复。
如果将来 9 组重复被清洗掉了,那说明上游改过数据——这会让所有历史分数失去可比性。 这正是「无版本号」这个缺陷的实际代价。
§31.5 给下游使用者的一句提醒
如果你只打算从这篇里带走一句话,带走这句:这份数据最大的价值不是 1,513 个分子,而是它把一个真实问题完整暴露了出来——同一结构可以有不同的实测活性、同一标签可以由不同判据决定、同一批数据可以因为划分方式不同而给出差 10 个点的分数。
这些不是这份数据的缺陷,是所有「从文献汇总出来的实验数据」的共同处境。BACE 因为小、因为被反复使用,把这层处境显示得特别清楚。用它的人如果不看这层,就只是在刷分数。
相关数据集导航
以下为站内 AI-Ready 数据集百科中与本词条共享多个主题标签的相关数据集,按相关度降序排列:
- plumber — 共享标签:药物发现与化学 / 化学信息学 / 分子性质预测 / 虚拟筛选 / 评测基准
- aqsoldb — 共享标签:药物发现与化学 / 化学信息学 / 分子性质预测 / 评测基准
- chembl — 共享标签:药物发现与化学 / 化学信息学 / 分子性质预测 / 虚拟筛选
- moleculenet — 共享标签:药物发现与化学 / 化学信息学 / 分子性质预测 / 评测基准
- lit-pcba — 共享标签:药物发现与化学 / 化学信息学 / 虚拟筛选 / 评测基准
- openadmet-expansionrx — 共享标签:药物发现与化学 / 化学信息学 / 分子性质预测 / 评测基准
- qmof — 共享标签:药物发现与化学 / 化学信息学 / 分子性质预测 / 虚拟筛选
- matbench — 共享标签:药物发现与化学 / 化学信息学 / 分子性质预测 / 评测基准
- zinc — 共享标签:药物发现与化学 / 化学信息学 / 虚拟筛选
- drugcentral — 共享标签:药物发现与化学 / 化学信息学 / 虚拟筛选
导航说明:本章节由全站统一标签体系自动计算生成(标签重合度算法),双向可达;点击链接可跳转至对应数据集词条。

