3. 样本切分实战:基于放款时间戳的滑动窗口切分、跨时间段的样本稳定性检验

在额度模型开发中,样本切分不是简单的随机抽样。由于信贷业务存在明显的时序依赖——宏观经济、客群结构、风控策略都会随时间变化——因此必须采用基于放款时间戳的滑动窗口切分,并辅以跨时间段的样本稳定性检验,才能保证模型在未来的真实表现。

3.1 为什么不能随机切分?

随机切分(如 70% 训练 / 30% 测试)假设样本独立同分布,这在信贷场景中几乎不成立:

切分方式 适用场景 信贷风险
随机切分 A/B 测试、横截面数据 未来样本信息泄露,高估模型效果
时间序列切分 信贷、风控、时序预测 模拟真实上线环境,避免时间穿越
⚠️ 常见陷阱: 如果训练集包含 2023 年 12 月的样本,而测试集包含 2023 年 6 月的样本,模型会“看到”未来的逾期表现,导致验证 AUC 虚高 0.05~0.10。

3.2 滑动窗口切分策略

基于放款时间戳,我们采用滑动窗口(Sliding Window)进行样本切分。核心参数包括:

  • 窗口长度:训练集覆盖的时间跨度(如 12 个月)
  • 验证/测试偏移:训练集结束时间点到验证集开始时间点的间隔(如 1 个月)
  • 步长:每次滑动的时间长度(如 3 个月)

以下是一个典型的滑动窗口切分流程:

滑动窗口样本切分示意图 2023-01 2023-04 2023-07 2023-10 2024-01 2024-04 训练集 (12个月) 验证集 测试集 训练集 (12个月) 验证集 测试集 步长=3个月 说明:每次滑动 3 个月,训练集始终保持 12 个月窗口,验证集和测试集紧随其后。 这种切分方式能模拟模型在真实时间线上的迭代效果。

3.3 实战切分步骤

以某消费金融公司额度模型为例,放款时间戳范围为 2022-01 至 2024-06,我们执行以下切分:

  1. 确定观察期与表现期:额度模型通常使用 6 个月表现期(如是否逾期 30+)。
  2. 设定窗口参数:训练窗口 12 个月,验证窗口 3 个月,测试窗口 3 个月,步长 3 个月。
  3. 生成切分时间点:从 2023-01 开始,每 3 个月生成一组训练/验证/测试切分。
  4. 剔除表现期不足的样本:对于测试集,必须确保放款时间 + 6 个月表现期 ≤ 当前数据截止时间。
💡 关键代码片段: 基于 Pandas 的滑动窗口切分
import pandas as pd
from datetime import timedelta

def sliding_window_split(df, date_col='loan_date', window_months=12,
                         gap_months=1, test_months=3, step_months=3):
    """
    基于放款时间戳的滑动窗口切分
    """
    df = df.sort_values(date_col).reset_index(drop=True)
    min_date = df[date_col].min()
    max_date = df[date_col].max()
    
    splits = []
    current_start = min_date
    
    while current_start + timedelta(days=window_months*30) <= max_date:
        train_end = current_start + timedelta(days=window_months*30)
        val_start = train_end + timedelta(days=gap_months*30)
        val_end = val_start + timedelta(days=test_months*30)
        test_end = val_end + timedelta(days=test_months*30)
        
        if test_end > max_date:
            break
            
        train_mask = (df[date_col] >= current_start) & (df[date_col] < train_end)
        val_mask = (df[date_col] >= val_start) & (df[date_col] < val_end)
        test_mask = (df[date_col] >= val_end) & (df[date_col] < test_end)
        
        splits.append({
            'train': df[train_mask].index.tolist(),
            'val': df[val_mask].index.tolist(),
            'test': df[test_mask].index.tolist(),
            'train_period': f'{current_start.date()} ~ {train_end.date()}',
            'val_period': f'{val_start.date()} ~ {val_end.date()}',
            'test_period': f'{val_end.date()} ~ {test_end.date()}'
        })
        
        current_start += timedelta(days=step_months*30)
    
    return splits

3.4 跨时间段的样本稳定性检验

切分完成后,必须检验不同时间窗口的样本分布是否稳定。常用的检验方法包括:

检验方法 适用变量类型 阈值 说明
PSI (Population Stability Index) 连续变量(分箱后) PSI < 0.1 衡量整体分布偏移
KS 检验 连续变量 p-value > 0.05 检验两个分布是否相同
卡方检验 分类变量 p-value > 0.05 检验类别分布是否一致
特征缺失率对比 所有变量 差异 < 5% 检验数据质量是否稳定

3.5 PSI 计算与解读

PSI 是信贷风控中最常用的稳定性指标,计算公式如下:

PSI = Σ (实际占比_i - 预期占比_i) * ln(实际占比_i / 预期占比_i)

其中:
- 预期占比_i:训练集在第 i 个分箱中的样本比例
- 实际占比_i:验证/测试集在第 i 个分箱中的样本比例

PSI 的判定标准:

  • PSI < 0.1:分布稳定,无需处理
  • 0.1 ≤ PSI < 0.25:轻度偏移,需监控
  • PSI ≥ 0.25:严重偏移,需重新训练或调整特征
💡 实战建议: 在额度模型中,建议对以下关键特征进行 PSI 检验:收入、负债率、征信查询次数、历史逾期天数。如果某个特征的 PSI 连续两个窗口超过 0.2,应考虑特征工程层面的修正(如分箱调整、WOE 平滑)。

3.6 稳定性检验报告模板

每次切分后,应输出一份稳定性检验报告,格式如下:

跨时间段稳定性检验报告(示例) 特征名称 训练集 PSI 验证集 PSI 测试集 PSI 结论 年收入 0.02 0.03 0.04 稳定 负债率 0.05 0.08 0.12 轻度偏移 征信查询次数 0.03 0.06 0.07 稳定 历史逾期天数 0.08 0.18 0.31 严重偏移 说明:历史逾期天数在测试集 PSI 达到 0.31,超过 0.25 阈值,表明该特征分布发生显著变化。 建议:检查该特征的分箱方式,或考虑使用 WOE 平滑处理,必要时剔除该特征。

3.7 稳定性检验后的处理策略

根据稳定性检验结果,采取相应的处理措施:

  • PSI < 0.1:特征保留,直接用于模型训练。
  • 0.1 ≤ PSI < 0.25:尝试分箱调整或 WOE 平滑;若调整后仍不稳定,则降权使用。
  • PSI ≥ 0.25:分析偏移原因(如策略调整、客群变化),若无法解释则剔除该特征。
⚠️ 重要提醒: 稳定性检验不是一次性工作。在模型上线后,应建立月度/季度监控机制,持续跟踪特征的 PSI 变化。一旦发现 PSI 持续上升,应及时触发模型重训流程。

3.8 本章小结

基于放款时间戳的滑动窗口切分是额度模型开发的基础步骤,它保证了训练集、验证集、测试集在时间上的严格隔离。跨时间段的样本稳定性检验(尤其是 PSI)则是模型泛化能力的“体检报告”。只有通过稳定性检验的样本切分,才能用于后续的特征筛选和模型训练。