← 返回笔记列表
AI 技术与原理 第 4 / 28 篇

AI 原理学习笔记(四):数据划分、预处理与防止泄漏

查看“AI 技术与原理”专题目录(4/28)
本文目录 14 个小节

返回系列导读 · 先修:机器学习任务与模型

1. 为什么要把数据分开

用做过的练习题考试,不能判断遇到新题会不会做。模型同样如此:训练数据上表现好,可能只是记住了样本。

三种集合承担不同职责:

集合作用能参与什么决定
训练集学习模型参数与预处理统计量拟合权重、均值、词表等
验证集比较模型和超参数选学习率、深度、阈值、停止时间
测试集对已确定方案做最终检查报告效果;不应继续拿来反复选方案

比例没有通用标准。样本量、任务分布和时间关系,比“必须七二一”更重要。验证数据太少,选出来的结果也容易受偶然性影响。

2. 数据泄漏:模型提前知道了本不该知道的信息

泄漏不限于把标签当特征。更隐蔽的是:训练时使用了预测时拿不到的信息。

例如预测“用户未来一个月会不会离职”,却加入“离职手续办理日期”,模型分数可能非常高,但没有实际预测价值。

再如把同一位病人的多张图片随机分到训练与测试,模型可能通过个人特征识别这个人,而非学会疾病规律。公司制度的几个近似版本分散到训练与测试,也会形成类似问题。

判断一列能不能用,可以问:在真实作出预测的那个时刻,这个值已经存在且允许被系统读取吗?

3. 划分方式要对应真实使用

随机划分与分层划分

样本近似独立同分布时,可随机划分。类别不平衡时,分层划分尽量保留各类比例,避免测试集偶然没有少数类。

分层不是解决所有偏差的保证。若训练和测试来自同一个过窄渠道,即使比例一致,仍可能无法代表真实用户。

按人、设备或企业分组

需要检验“遇到新用户是否有效”,就让同一用户的记录留在同一组。可以按用户 ID 做 Group split,避免身份交叉。

按时间划分

要预测未来,就用过去训练、较晚时间验证、更晚时间测试。涉及滚动窗口、未来收益等重叠标签时,还要检查时间边界是否需要间隔,防止训练标签覆盖测试时期。

同一任务可以有多个测试集:同分布、新时间、新地区、新客户,各自回答不同问题。

4. 缺失值与异常值

缺失值表示不知道,不等于 0。“工资缺失”与“工资确实为 0”含义不同。可根据任务使用训练集的中位数、类别占位符或能处理缺失的模型,也可增加“该字段是否缺失”的指示特征。

异常值可能是录入错误,也可能是最有价值的罕见事件。欺诈检测里把所有异常交易删掉,可能恰好删掉目标。先查单位、来源和业务含义,再决定修正、截断、保留或剔除。

预处理决策也要留痕:删了哪些规则命中的记录,丢失了多少样本,是否对某类人群影响更大。

5. 标准化与归一化

标准化常用:

x′=x−μσx'=\frac{x-\mu}{\sigma}

μ\mu 是训练集均值,σ\sigma 是训练集标准差。它把“离平均值多远”改用标准差为单位表示。

Min-Max 缩放常用:

x′=x−xmin⁡xmax⁡−xmin⁡x'=\frac{x-x_{\min}}{x_{\max}-x_{\min}}

训练范围内的数据映射到 0—1,但新数据可以超出这个范围,不应该以为所有未来值自动仍在 0—1。

常数列的分母可能为 0,需要特殊处理。标准化不等于让分布变成正态分布;它主要调整中心和尺度。

最重要的规则是:先划分,再从训练集学习变换,最后用同一个变换处理验证与测试。 这也是 scikit-learn 防止数据泄漏的官方说明强调的原则。

from statistics import mean, pstdev

train = [60.0, 80.0, 100.0]
test = [120.0]
mu = mean(train)
sigma = pstdev(train)
scaled_train = [(x - mu) / sigma for x in train]
scaled_test = [(x - mu) / sigma for x in test]

print(round(mu, 2), round(sigma, 2))  # 80.0 16.33
print([round(x, 3) for x in scaled_train])  # [-1.225, 0.0, 1.225]
print([round(x, 3) for x in scaled_test])   # [2.449]

注意 120 没被用于计算均值和标准差。否则我们就让训练过程提前看到了测试分布。

6. 类别编码与特征工程

城市名称不能直接当作可计算的大小关系。“北京=1、上海=2、广州=3”可能误导距离模型,让它以为广州与北京相距是上海的两倍。

One-hot 编码给每个类别一个位置,例如北京 [1,0,0][1,0,0]、上海 [0,1,0][0,1,0]。有明确顺序的类别,如低、中、高,才可能使用合理的序数编码。

目标编码使用类别对应的目标统计量,若直接用全部标签计算,就容易把答案泄漏进去,必须在训练折内部妥善估计。

特征工程是把原始信息转换成更有帮助的输入,例如从时间戳得到小时与工作日,从交易序列得到过去七天次数。做滚动特征时只能看当前时点之前的数据。

7. 交叉验证不是重复考试取最高分

K 折交叉验证把开发数据分成 K 份。每次一份验证,其余训练,共训练 K 次,再观察平均表现和波动。

每一折里的填补、缩放、PCA 和特征选择都只能从该折训练部分学习。Pipeline 的价值就在于把这些步骤和模型一起纳入每一折。流程见 scikit-learn 交叉验证文档。

测试集仍然可以单独保留。若做了大量调参、还想估计整个选型流程的效果,可使用嵌套交叉验证:内层选参数,外层评估选型过程。

8. 超参数搜索、早停与概率校准

网格搜索遍历指定组合;随机搜索从范围内抽取组合;更高级的方法可根据已有结果选择下一组。搜索再聪明,也救不了有泄漏的验证集。

早停在验证效果长期不改善时停止训练,并保留合适的模型版本。它不是发现训练损失下降就一直训练。

概率校准尝试让预测概率对应实际频率。校准器也要用独立于原模型拟合的数据或合适的交叉验证生成分数;不能在最终测试集上拟合。

最终要保存的不只是权重,还有预处理规则、特征顺序、标签定义、阈值和版本。特征列顺序错了,模型仍能计算,但含义已经错位。

复习卡片

  • 划分目标:模拟未来真正使用时遇到的数据。
  • fit:从数据学东西;transform:应用已经学好的规则。
  • 泄漏检查:这个信息在预测时真的已存在吗?
  • 调参看验证,最终结论看未参与选择的测试。

自测与参考答案

1. 为什么标准化时不能把训练和测试一起计算均值?

参考答案均值本身是从数据学习的统计量。引入测试数据会让训练过程获得测试分布的信息,评估就不再是完全独立检查。

2. 想检查模型能否服务新客户,应该怎样划分?

参考答案通常至少需要按客户分组,使同一客户的相关记录不跨集合;是否还要按时间切分,取决于实际预测场景。

3. 测试分数不好,换几十个方案直到测试分数高,还算独立测试吗?

参考答案不算。测试集已经间接参与选型,应重新建立未被使用的最终评估数据或明确披露这种限制。

原文定位

依据:原资料 2.9—2.11、2.19、10.4—10.7。更多实践见数据预处理与数据集划分。

继续学习本专题