第3节:数据收集与清洗中的常见陷阱及SVA规则定义归纳总结

做ADMET预测这几年,我遇到最多的坑不在模型本身,而在数据上。

模型再花哨,喂进去的数据是脏的,结果就是垃圾。今天咱们就聊聊数据收集和清洗里那些防不胜防的陷阱,以及怎么用SVA规则把这些坑填上。

数据收集 → 清洗 → SVA 规则定义流程 原始数据收集 陷阱:外推误差 陷阱:数据重复 陷阱:批次效应 数据清洗 标准化单位 去重 & 去冗余 化合物结构归一化 SVA 规则定义 全局规则 局部规则 清洗策略规则 核心目标: 从源头消除数据噪声 → 定义可执行的验证规则 → 让每一批新数据都通过 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规则一定要可追溯。每条规则都需要有版本号和生效日期。我曾经因为更新了一条单位转换规则但没做记录,导致后续三个月的数据全用了旧规则,后果可想而知。

总结:SVA规则的归纳方法论

规则类别 覆盖范围 触发操作 典型案例
全局结构规则 所有数据 reject/flag SMILES无效、超分子量范围
局部性质规则 特定性质字段 reject/flag/warn LogD超出适用范围
来源批次规则 按数据来源分组 flag/calibrate 批次间统计数据偏移
清洗策略规则 整个清洗流程 记录/文档化 去重策略、标准化方法

数据收集和清洗,看起来基础,实际上决定了你整个ADMET预测项目的上限。

我见过太多团队花几个月调参、换算法,最后发现模型表现上不去——根子还是在数据质量上。

SVA规则的作用,就是把这些「血的教训」固定下来,让每一批新数据进来都能走一遍同样的质量验证流程。这样下去,你模型的可复现性和可信度,都会有明显提升。