3. 训练集切分基础:时间序列切分、随机切分、分层抽样切分的基本原理

在信用额度定价模型中,训练集的切分方式直接影响模型对未来收益的预测能力与稳定性。错误的切分策略会导致时间穿越、样本偏差或收益估计失真。本节系统阐述三种核心切分方法的基本原理、适用场景与数学约束。

3.1 三种切分方法的本质差异

切分方法 核心假设 数据顺序敏感性 类别平衡保证 典型应用场景
时间序列切分 未来分布与过去存在时序依赖 强(必须保留时间顺序) 不保证 贷后收益预测、滚动率建模
随机切分 样本独立同分布(i.i.d.) 无 不保证 A/B测试对照、特征筛选基线
分层抽样切分 关键分层变量在训练/测试集分布一致 无 强制保证 信用评分卡、违约率极低场景
关键原则:信用额度定价中,时间序列切分是默认首选。随机切分仅适用于无时间依赖的特征工程验证,分层抽样切分用于处理严重类别不平衡(如违约率 < 2%)时的收益估计。

3.2 时间序列切分(Time Series Split)

时间序列切分严格保留数据的时间顺序,训练集只使用历史数据,测试集使用未来数据。这是信用额度定价中最符合业务逻辑的切分方式,因为模型上线后只能基于过去预测未来。

3.2.1 切分规则

  • 固定窗口切分:选取连续时间区间作为训练集,后续连续区间作为测试集。例如:使用2023年1月-6月数据训练,2023年7月-9月数据测试。
  • 滚动窗口切分:训练集窗口随时间向前滑动,每次保留固定长度的时间窗口。例如:训练集窗口长度6个月,每次向前滚动1个月,生成多个训练/测试对。
  • 扩展窗口切分:训练集起点固定,终点随时间扩展。例如:训练集从2023年1月开始,依次扩展至2月、3月……测试集始终为下一个完整月份。

3.2.2 时间序列切分流程图

时间序列切分:扩展窗口 vs 滚动窗口 扩展窗口 训练集 T1 (1月-3月) 测试 4月 训练集 T2 (1月-4月) 测试 5月 训练集 T3 (1月-5月) 测试 6月 滚动窗口 训练集 W1 (1月-3月) 测试 4月 训练集 W2 (2月-4月) 测试 5月 训练集 W3 (3月-5月) 测试 6月
实践建议:在信用额度定价中,推荐使用扩展窗口切分。因为随着时间推移,训练数据不断积累,模型能学习到更完整的宏观经济周期与客群行为演变模式。滚动窗口可能丢弃早期有价值的历史违约样本。

3.3 随机切分(Random Split)

随机切分将全部样本按比例(通常 70%/20%/10% 或 80%/20%)随机分配到训练集、验证集和测试集。该方法假设所有样本来自同一分布且相互独立。

3.3.1 适用条件与风险

  • 适用条件:数据无时间结构、无群体结构(如同一客户多次借贷)、仅用于特征重要性排序或基线模型对比。
  • 主要风险:在信用额度定价中,随机切分极易造成时间穿越——用未来的样本信息预测过去的收益表现,导致模型在线上表现远低于离线测试。
  • 数学表达:设总样本量为 N,训练集比例 p,则训练集样本数 = N × p,测试集样本数 = N × (1-p)。每个样本被选入训练集的概率为 p,且相互独立。
避坑提醒:如果数据中包含同一客户在不同时间点的多次借贷记录,随机切分可能导致同一客户的样本同时出现在训练集和测试集中,造成信息泄露。此时应使用客户级分组切分(Group Split)替代随机切分。

3.4 分层抽样切分(Stratified Split)

分层抽样切分在保持训练集与测试集比例的同时,强制保证关键分层变量(如违约标签、信用等级、产品类型)在训练集和测试集中的分布与原始数据集一致。

3.4.1 分层切分流程

  1. 定义分层变量:通常选择目标变量(违约/非违约)或对收益影响最大的类别变量。
  2. 划分层(Strata):将全部样本按分层变量的取值划分为互斥的子集。例如:违约样本层、非违约样本层。
  3. 层内随机切分:在每个层内独立进行随机切分,保持各层训练/测试比例相同。
  4. 合并子集:将所有层的训练子集合并为最终训练集,测试子集合并为最终测试集。

3.4.2 分层抽样 vs 随机切分对比表

对比维度 随机切分 分层抽样切分
违约样本在测试集中的比例 可能偏离原始比例 ±30% 与原始比例严格一致
低违约率场景(< 1%) 测试集可能包含 0 个违约样本 保证测试集包含与原始比例一致的违约样本
收益估计稳定性 方差大,单次切分结果不可靠 方差小,多次切分结果稳定
实现复杂度 低(一行代码) 中(需指定分层变量)

3.5 三种切分方法的决策流程

训练集切分方法决策流程 原始数据集 存在时间依赖? 时间序列切分 类别严重不平衡? 分层抽样切分 随机切分 说明:时间依赖判断优先级最高。若存在时间依赖,无论是否类别不平衡,都应优先使用时间序列切分。 类别不平衡仅在无时间依赖时,作为选择分层抽样或随机切分的依据。

3.6 代码实现示例

以下代码展示三种切分方法在 Python 中的标准实现,使用 sklearn.model_selection 和 pandas。

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split, TimeSeriesSplit

# 模拟信用额度定价数据
np.random.seed(42)
n_samples = 10000
df = pd.DataFrame({
    'loan_date': pd.date_range('2023-01-01', periods=n_samples, freq='D'),
    'credit_score': np.random.normal(650, 50, n_samples),
    'default_flag': np.random.binomial(1, 0.03, n_samples),  # 3% 违约率
    'revenue': np.random.exponential(500, n_samples)
})

# ========== 1. 时间序列切分(扩展窗口) ==========
# 按日期排序
df = df.sort_values('loan_date').reset_index(drop=True)

# 定义切分点:前70%训练,后30%测试
split_idx = int(len(df) * 0.7)
train_ts = df.iloc[:split_idx]
test_ts = df.iloc[split_idx:]

print(f"时间序列切分 - 训练集: {train_ts.shape[0]} 样本, 测试集: {test_ts.shape[0]} 样本")

# ========== 2. 随机切分 ==========
train_rand, test_rand = train_test_split(
    df, test_size=0.3, random_state=42
)
print(f"随机切分 - 训练集: {train_rand.shape[0]} 样本, 测试集: {test_rand.shape[0]} 样本")

# ========== 3. 分层抽样切分(按违约标签分层) ==========
train_strat, test_strat = train_test_split(
    df, test_size=0.3, random_state=42,
    stratify=df['default_flag']  # 按违约标签分层
)

# 验证分层效果
print(f"\n原始数据集违约率: {df['default_flag'].mean():.4f}")
print(f"随机切分测试集违约率: {test_rand['default_flag'].mean():.4f}")
print(f"分层切分测试集违约率: {test_strat['default_flag'].mean():.4f}")
输出解读:分层抽样切分能保证测试集的违约率与原始数据集高度一致(约 0.03),而随机切分在单次运行中可能偏离至 0.025 或 0.035。在信用额度定价中,这种偏差会导致收益估计的系统性误差,进而影响额度定价策略的可靠性。

3.7 切分方法选择清单

  • 第一步:检查数据是否包含时间戳字段(贷款日期、申请日期、放款日期)。如果有,默认使用时间序列切分。
  • 第二步:检查是否存在同一客户多次借贷。如果有,在时间序列切分基础上增加客户级分组约束。
  • 第三步:检查目标变量(违约/非违约)的分布。如果违约率 < 5% 且无时间依赖,使用分层抽样切分。
  • 第四步:仅当数据无时间结构、无客户重复、且目标变量分布均衡时,才使用随机切分。
  • 第五步:无论使用哪种切分方法,始终在切分后验证训练集与测试集的关键特征分布是否一致。