4、数据准备与划分:训练集/验证集/测试集划分、时间序列数据切分、分层抽样与随机抽样
在 Champion Challenger 对比分析中,数据划分的合理性直接决定模型评估的公正性与泛化能力的可信度。本章将系统讲解三种核心划分策略:随机抽样、分层抽样以及时间序列数据切分,并明确训练集、验证集、测试集各自的作用与划分比例。
4.1 训练集 / 验证集 / 测试集的作用与比例
在对比 Champion 与 Challenger 模型时,必须将数据划分为三个独立子集,避免信息泄露导致的评估偏差。
| 数据集 | 用途 | 典型比例 | 关键要求 |
|---|---|---|---|
| 训练集 | 模型参数学习、特征权重拟合 | 60% – 80% | 数据量充足,覆盖主要分布模式 |
| 验证集 | 超参数调优、模型选择、早停判断 | 10% – 20% | 与训练集分布一致,不参与训练 |
| 测试集 | 最终模型泛化能力评估、Champion vs Challenger 对比 | 10% – 20% | 完全隔离,仅在最终评估时使用一次 |
实践要点:在 Champion Challenger 场景中,测试集必须严格保留至最终对比阶段。若在调参过程中反复使用测试集,会导致“数据窥探偏差”,使对比结果过于乐观。
4.2 随机抽样与分层抽样
随机抽样是最基础的划分方式,适用于数据分布均匀的场景。但当目标变量存在类别不平衡时,随机抽样可能导致验证集或测试集中某些类别缺失,从而无法真实反映模型在少数类上的表现。
分层抽样 通过保持每个子集中目标变量的类别比例与原始数据集一致,解决了上述问题。在 Champion Challenger 对比中,分层抽样能确保两个模型在相同的类别分布下进行公平比较。
选择建议:
- 分类任务:优先使用分层抽样,按目标变量分层。
- 回归任务:若目标变量分布严重偏态,可对目标值分桶后分层抽样。
- 数据量极大(>100万行):随机抽样与分层抽样差异很小,可选用随机抽样以提升效率。
4.3 时间序列数据切分
时间序列数据具有时间依赖性,不能使用随机抽样或分层抽样,否则会造成未来信息泄露。在 Champion Challenger 对比中,若使用时间序列数据(如股票价格、传感器读数、用户行为日志),必须采用时间顺序切分。
常见的时间序列切分方法包括:
- 固定时间窗口切分:按时间顺序取前 70% 为训练集,中间 15% 为验证集,最后 15% 为测试集。
- 滚动窗口切分:适用于在线学习或持续对比场景,训练集随时间推移滚动扩展。
- 时间序列交叉验证:按时间顺序依次划分多个训练/验证对,每个验证集始终在训练集之后。
常见错误:在时间序列数据上使用随机抽样或 K 折交叉验证(不按时间顺序)会导致模型“看到未来”,在 Champion Challenger 对比中产生虚假的高分,实际部署时性能大幅下降。
4.4 数据划分的代码实现
以下代码展示了在 Python 中如何实现随机抽样、分层抽样以及时间序列切分,适用于 Champion Challenger 对比分析的数据准备阶段。
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split, StratifiedShuffleSplit
from datetime import datetime
# ========== 1. 随机抽样 ==========
def random_split(df, target_col, test_size=0.2, val_size=0.1, random_state=42):
"""随机划分训练集、验证集、测试集"""
# 先分出测试集
train_val, test = train_test_split(
df, test_size=test_size, random_state=random_state
)
# 从剩余数据中分出验证集
val_ratio = val_size / (1 - test_size)
train, val = train_test_split(
train_val, test_size=val_ratio, random_state=random_state
)
return train, val, test
# ========== 2. 分层抽样 ==========
def stratified_split(df, target_col, test_size=0.2, val_size=0.1, random_state=42):
"""按目标变量分层划分训练集、验证集、测试集"""
# 先分出测试集(分层)
train_val, test = train_test_split(
df, test_size=test_size, stratify=df[target_col], random_state=random_state
)
# 从剩余数据中分出验证集(分层)
val_ratio = val_size / (1 - test_size)
train, val = train_test_split(
train_val, test_size=val_ratio,
stratify=train_val[target_col], random_state=random_state
)
return train, val, test
# ========== 3. 时间序列切分 ==========
def temporal_split(df, date_col, train_ratio=0.7, val_ratio=0.15):
"""按时间顺序切分训练集、验证集、测试集"""
df = df.sort_values(by=date_col).reset_index(drop=True)
n = len(df)
train_end = int(n * train_ratio)
val_end = int(n * (train_ratio + val_ratio))
train = df.iloc[:train_end]
val = df.iloc[train_end:val_end]
test = df.iloc[val_end:]
return train, val, test
# ========== 使用示例 ==========
# 假设 df 是包含特征和目标变量的 DataFrame
# train, val, test = stratified_split(df, target_col='churn_label')
# train_t, val_t, test_t = temporal_split(df, date_col='transaction_date')
4.5 划分策略选择矩阵
根据数据类型和业务场景,选择合适的划分策略:
| 数据类型 | 推荐策略 | 原因 | Champion Challenger 注意事项 |
|---|---|---|---|
| 独立同分布数据(IID) | 随机抽样 | 数据无依赖关系,简单高效 | 确保随机种子固定,对比可复现 |
| 类别不平衡数据 | 分层抽样 | 保持各子集类别比例一致 | 两个模型在相同类别分布下对比 |
| 时间序列数据 | 时间顺序切分 | 避免未来信息泄露 | Champion 和 Challenger 使用完全相同的切分时间点 |
| 多源异构数据 | 按来源分层 + 随机 | 保证各数据源在子集中均匀分布 | 对比时需控制数据源分布不变 |
最终检查清单:
- 测试集是否从未参与任何训练或调参过程?
- 时间序列数据是否按时间顺序切分,而非随机?
- 分层抽样时,目标变量的类别比例在各子集中是否与全集一致?
- Champion 和 Challenger 是否使用完全相同的数据划分?