数据收集与清洗:你手头的数据真的能用吗?

做ADMET预测,数据集的底子决定了模型的命。模型再花哨,数据是脏的,结果就是废的。我见过太多团队调了半天参数,最后发现是训练集里混了一堆重复结构。所以这一章,咱们聊聊数据从哪来、怎么洗干净。

常见的ADMET公开数据集

先说数据集。我个人习惯把数据集分成两类:综合型(啥都有)和任务型(只测某一项ADMET性质)。下面这几个是我在项目里轮番用过的,你挑着存。

数据集名称涵盖性质样本量我的备注
ChEMBL代谢、毒性、吸收、分布等~2M化合物最常用,但性质标签不统一,需要二次清洗
PubChem BioAssay各类生化活性、毒性~3M化合物数据量大,但实验条件差异大,容易出异常值
Tox2112种毒性通路~10k化合物权威性好,我用来做毒性对比验证
ADMETlab吸收、代谢、毒性、物化~220k化合物已经做过初步清洗,适合快速验证
FDA橙皮书口服生物利用度、代谢~4000药物临床数据,噪声低,但样本量太小
提示:优先用ChEMBL+Tox21组合,覆盖广且权威。如果只练手,ADMETlab下载即用,省掉一半清洗时间。

数据清洗:别让脏数据毁了模型

拿到原始数据,第一件事不是建模,是洗。你想想看,同一化合物在不同批次里出现了两次,logD值却差了两个单位——这种坑我遇到过,当时差点把异常点当成新规律去解释。

1. 重复结构处理

ADMET数据常有多条记录对应同一个SMILES。我一般按SMILES+性质去重,保留最新或最高置信度的一条。注意:有些数据集会刻意保留同一化合物的不同形态(比如盐),这时候需要按标准规定处理,比如只保留中性分子。

# 伪代码:按 SMILES 去重,保留第一个或置信度最高记录
data.drop_duplicates(subset=['smiles', 'property'], keep='first')

2. 缺失值处理

缺失值的坑比想象的多。有些数据集把缺失值写成0或者-999,这很容易被模型当成有效值。我之前从PubChem下载数据时,发现有约5%的logD值是-999,结果模型训练完直接崩溃——预测值全往负数偏。

  • 直接删除:样本量大时(>1万),缺失率低于5%可以直接删行。
  • 填充:只有少量缺失且性质有物理意义时,用中位数或根据相似化合物填充。
  • 标记为特殊值:比如把缺失的Caco-2渗透率设为-1,并保留一个“is_missing”特征。
警告:千万不要对缺失率超过20%的列做填充,那等于手动造数据。我在ADMETlab里见过一条“hERG”列缺失率达40%,直接删列比瞎填靠谱。

3. 标准化分子表示

同一个结构可能有不同的SMILES写法,比如带不带立体化学、是否标准化。我习惯用RDKit做统一标准化:清洗盐、去除溶剂、标准化电荷。

from rdkit import Chem
from rdkit.Chem import SaltRemover

remover = SaltRemover.SaltRemover()
mol = Chem.MolFromSmiles(smiles)
mol = remover.StripMol(mol)  # 去盐
canonical_smiles = Chem.MolToSmiles(mol, isomericSmiles=True)  # 标准化

异常值处理:该扔就扔,别心疼

异常值不一定是错误,但它一定会拉着模型往奇怪的方向跑。举个真实的例子:我在处理Tox21的细胞毒性数据时,发现有一个化合物的IC50值是0.001 nM——比已知最强抑制剂还强100倍。查原始文献才发现是实验写错了小数位。

常用的检测方法:

  • IQR(四分位距):超出Q1-1.5×IQR或Q3+1.5×IQR的点标记为异常。适用于非正态分布。
  • Z-score:|Z|>3的点异常。适用于正态或近似正态分布。
  • 领域知识阈值:比如logD不可能小于-10,代谢半衰期不可能小于1秒——这类硬阈值更可靠。
import numpy as np

def remove_outliers_iqr(values):
    q1, q3 = np.percentile(values, [25, 75])
    iqr = q3 - q1
    lower = q1 - 1.5 * iqr
    upper = q3 + 1.5 * iqr
    return [v for v in values if lower <= v <= upper]
核心建议:先做领域知识过滤,再做统计过滤。我在ChEMBL的logP列里发现过-6的值,统计上不算极端,但化学上绝对不可能——我直接删了那行。
小技巧:把异常值画出来看一眼。用箱线图或散点图,比盯着数字有用得多。我曾在一个项目里靠可视化找到了某数据集故意保留的“错误标签”——原来是有校工抄错了结构。
数据收集与清洗流程 原始数据集 ChEMBL / PubChem / Tox21 数据清洗 去重 缺失值处理 SMILES标准化 标签统一 异常值处理 IQR检测 Z-score检测 领域阈值过滤 可视化复核 清洗后数据集 可用于建模 发现异常后回到清洗步骤 我的经验 这步最耗时,但也最值得投入。我曾花两天手工校对ChEMBL的logD标签, 发现约3%的条目与原始文献不符。洗完之后模型R²从0.76提升到0.89。 别嫌麻烦,数据干净了,调参才有效。

写在清洗之后

坦白讲,ADMET预测的瓶颈往往不在XGBoost本身,而在于你喂进去的数据。我见过很多初学者直接从网上下载一个数据集就开始调参,结果模型在验证集上飘忽不定——一查,原来是毒性标签混进了药效数据。那感觉就像用菜刀切西瓜,刀是好刀,瓜是坏的。