第3节:数据收集与清洗中的常见陷阱及SVA规则定义归纳总结
做ADMET预测这几年,我遇到最多的坑不在模型本身,而在数据上。
模型再花哨,喂进去的数据是脏的,结果就是垃圾。今天咱们就聊聊数据收集和清洗里那些防不胜防的陷阱,以及怎么用SVA规则把这些坑填上。
数据收集阶段:三个躲不开的坑
陷阱一:外推误差
说白了,就是把模型适用范围之外的数据硬塞进去训练。
举个例子,你训练的LogD模型覆盖范围是 -2 到 5。结果你收集了一批 LogD = 8 的化合物数据。模型死活拟合不好,你还以为是算法不行。
我接过一个项目,客户抱怨他们的Caco-2渗透性模型预测不准。调了一周参数,最后发现训练集里混了20%的肽类和大分子数据。这些分子的渗透机理完全不一样,模型根本学不了。
- 建立数据收集时明确的化学空间边界
- 使用 PCA 或 t-SNE 进行分布可视化检查
- 对超出范围的数据做标记,而不是直接丢弃
陷阱二:数据重复与不一致
同一篇文献同一个化合物,不同实验室测出来的 IC50 可能差两个数量级。
你想想看,如果不做去重就直接合并,模型会学到什么?学到的全是噪声。
我曾经一个CYP3A4抑制活性数据集,合并了三个公开库。跑PCA一看,相同SMILES的数据点竟然分布在两个完全分离的簇里。查了原始来源,才知道两个实验室用的底物浓度不一样。
- SMILES 标准化后再去重,别用原始字符串
- 对同一数据点有多个值的,保留最高质量来源
- 必须记录去重策略——用平均还是用中位数
陷阱三:批次效应
这是最隐蔽的坑。不同批次、不同实验室、甚至同一实验室不同日期测的数据,可能存在系统性偏差。
我的做法是:先把数据按来源分批次,然后用 ANOVA 检验各批次间的均值差异。如果 p < 0.01,我建议用 ComBat 算法校正。
数据清洗:我把这步叫「数据的觉醒」
1. 化合物结构归一化
做ADMET预测,结构是最核心的特征。同一个分子,SMILES写法不同,模型会当成两个东西。
# 我常用的标准化步骤
# 1. 所有 SMILES 转为 Canonical SMILES
# 2. 去除盐和溶剂分子
# 3. 对立体化学做一个标准化的标记策略
from rdkit import Chem
def standardize_smiles(smiles):
mol = Chem.MolFromSmiles(smiles)
if mol is None:
return None
# 去盐
mol = Chem.RemoveHs(mol)
# 标准化
smiles_out = Chem.MolToSmiles(mol, canonical=True)
return smiles_out
2. 单位与量纲统一
不同数据源特别喜欢用不同单位——LogD用pH 7.4,有的用pH 6.5;IC50有的写nM,有的写μM。
我见过最离谱的一个项目:部分数据用 LogD 7.4,另一部分用 LogD 7.0,还没做转换就直接合并训练。
# 单位转换检查脚本示例
def check_and_convert_units(row):
# 检查 IC50 单位
if row['unit'] == 'uM':
row['value_nM'] = row['value'] * 1000
elif row['unit'] == 'mM':
row['value_nM'] = row['value'] * 1_000_000
else:
row['value_nM'] = row['value']
return row
3. 数据分布检查
清洗完后一定要看分布。我习惯用直方图+QQ图组合,判断数据是否偏态。
对于明显偏态的数据(比如大部分LogD值集中在-2到2之间,但有几个点在5-8),我会检查这些点是否为边缘或异常值。
- SMILES 标准化率 > 95%
- 无重复化合物(按标准化SMILES去重后)
- 各批次数据的统计分布无明显偏移(t检验 p>0.05)
- 所有数值单位已统一为同一个标准
- 无空值或缺失值(已有合理解释的除外)
SVA规则定义:把经验变成可执行的代码
SVA(Structure Value Alignment)规则,说白了就是把上面我们碰到的所有经验,变成自动化的验证脚本。
全局规则:适用于所有数据类型,比如SMILES有效性检查、分子量范围、原子类型白名单。
# SVA全局规则示例
SVA_GLOBAL_RULES = {
"smiles_valid": {"type": "structure", "action": "reject"},
"mol_weight_range": {"min": 100, "max": 900, "action": "flag"},
"atom_type_whitelist": ["C", "N", "O", "S", "P", "F", "Cl", "Br", "I"],
"num_rotatable_bonds": {"max": 20, "action": "warn"}
}
局部规则:针对特定性质定义的范围。比如LogD的适用范围是 -2 到 5,超出就标记。
# SVA局部规则示例
SVA_LOCAL_RULES = {
"logd": {"range": [-2.0, 5.0], "unit": "pH 7.4", "action": "reject"},
"solubility": {"range": [0.01, 1000], "unit": "ug/ml", "action": "flag"},
"ppb": {"range": [0, 100], "unit": "%", "action": "flag"}
}
清洗策略规则:记录我们在清洗阶段使用的具体方法。比如去重是用平均值还是中位数,单位转换的具体公式。
总结:SVA规则的归纳方法论
| 规则类别 | 覆盖范围 | 触发操作 | 典型案例 |
|---|---|---|---|
| 全局结构规则 | 所有数据 | reject/flag | SMILES无效、超分子量范围 |
| 局部性质规则 | 特定性质字段 | reject/flag/warn | LogD超出适用范围 |
| 来源批次规则 | 按数据来源分组 | flag/calibrate | 批次间统计数据偏移 |
| 清洗策略规则 | 整个清洗流程 | 记录/文档化 | 去重策略、标准化方法 |
数据收集和清洗,看起来基础,实际上决定了你整个ADMET预测项目的上限。
我见过太多团队花几个月调参、换算法,最后发现模型表现上不去——根子还是在数据质量上。
SVA规则的作用,就是把这些「血的教训」固定下来,让每一批新数据进来都能走一遍同样的质量验证流程。这样下去,你模型的可复现性和可信度,都会有明显提升。