1. 为什么要把数据分开
用做过的练习题考试,不能判断遇到新题会不会做。模型同样如此:训练数据上表现好,可能只是记住了样本。
三种集合承担不同职责:
| 集合 | 作用 | 能参与什么决定 |
|---|---|---|
| 训练集 | 学习模型参数与预处理统计量 | 拟合权重、均值、词表等 |
| 验证集 | 比较模型和超参数 | 选学习率、深度、阈值、停止时间 |
| 测试集 | 对已确定方案做最终检查 | 报告效果;不应继续拿来反复选方案 |
比例没有通用标准。样本量、任务分布和时间关系,比“必须七二一”更重要。验证数据太少,选出来的结果也容易受偶然性影响。
2. 数据泄漏:模型提前知道了本不该知道的信息
泄漏不限于把标签当特征。更隐蔽的是:训练时使用了预测时拿不到的信息。
例如预测“用户未来一个月会不会离职”,却加入“离职手续办理日期”,模型分数可能非常高,但没有实际预测价值。
再如把同一位病人的多张图片随机分到训练与测试,模型可能通过个人特征识别这个人,而非学会疾病规律。公司制度的几个近似版本分散到训练与测试,也会形成类似问题。
判断一列能不能用,可以问:在真实作出预测的那个时刻,这个值已经存在且允许被系统读取吗?
3. 划分方式要对应真实使用
随机划分与分层划分
样本近似独立同分布时,可随机划分。类别不平衡时,分层划分尽量保留各类比例,避免测试集偶然没有少数类。
分层不是解决所有偏差的保证。若训练和测试来自同一个过窄渠道,即使比例一致,仍可能无法代表真实用户。
按人、设备或企业分组
需要检验“遇到新用户是否有效”,就让同一用户的记录留在同一组。可以按用户 ID 做 Group split,避免身份交叉。
按时间划分
要预测未来,就用过去训练、较晚时间验证、更晚时间测试。涉及滚动窗口、未来收益等重叠标签时,还要检查时间边界是否需要间隔,防止训练标签覆盖测试时期。
同一任务可以有多个测试集:同分布、新时间、新地区、新客户,各自回答不同问题。
4. 缺失值与异常值
缺失值表示不知道,不等于 0。“工资缺失”与“工资确实为 0”含义不同。可根据任务使用训练集的中位数、类别占位符或能处理缺失的模型,也可增加“该字段是否缺失”的指示特征。
异常值可能是录入错误,也可能是最有价值的罕见事件。欺诈检测里把所有异常交易删掉,可能恰好删掉目标。先查单位、来源和业务含义,再决定修正、截断、保留或剔除。
预处理决策也要留痕:删了哪些规则命中的记录,丢失了多少样本,是否对某类人群影响更大。
5. 标准化与归一化
标准化常用:
是训练集均值, 是训练集标准差。它把“离平均值多远”改用标准差为单位表示。
Min-Max 缩放常用:
训练范围内的数据映射到 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 编码给每个类别一个位置,例如北京 、上海 。有明确顺序的类别,如低、中、高,才可能使用合理的序数编码。
目标编码使用类别对应的目标统计量,若直接用全部标签计算,就容易把答案泄漏进去,必须在训练折内部妥善估计。
特征工程是把原始信息转换成更有帮助的输入,例如从时间戳得到小时与工作日,从交易序列得到过去七天次数。做滚动特征时只能看当前时点之前的数据。
7. 交叉验证不是重复考试取最高分
K 折交叉验证把开发数据分成 K 份。每次一份验证,其余训练,共训练 K 次,再观察平均表现和波动。
每一折里的填补、缩放、PCA 和特征选择都只能从该折训练部分学习。Pipeline 的价值就在于把这些步骤和模型一起纳入每一折。流程见 scikit-learn 交叉验证文档。
测试集仍然可以单独保留。若做了大量调参、还想估计整个选型流程的效果,可使用嵌套交叉验证:内层选参数,外层评估选型过程。
8. 超参数搜索、早停与概率校准
网格搜索遍历指定组合;随机搜索从范围内抽取组合;更高级的方法可根据已有结果选择下一组。搜索再聪明,也救不了有泄漏的验证集。
早停在验证效果长期不改善时停止训练,并保留合适的模型版本。它不是发现训练损失下降就一直训练。
概率校准尝试让预测概率对应实际频率。校准器也要用独立于原模型拟合的数据或合适的交叉验证生成分数;不能在最终测试集上拟合。
最终要保存的不只是权重,还有预处理规则、特征顺序、标签定义、阈值和版本。特征列顺序错了,模型仍能计算,但含义已经错位。
复习卡片
- 划分目标:模拟未来真正使用时遇到的数据。
- fit:从数据学东西;transform:应用已经学好的规则。
- 泄漏检查:这个信息在预测时真的已存在吗?
- 调参看验证,最终结论看未参与选择的测试。
自测与参考答案
1. 为什么标准化时不能把训练和测试一起计算均值?
参考答案
均值本身是从数据学习的统计量。引入测试数据会让训练过程获得测试分布的信息,评估就不再是完全独立检查。2. 想检查模型能否服务新客户,应该怎样划分?
参考答案
通常至少需要按客户分组,使同一客户的相关记录不跨集合;是否还要按时间切分,取决于实际预测场景。3. 测试分数不好,换几十个方案直到测试分数高,还算独立测试吗?