2. 样本工程基础:样本定义、正负样本构建、样本时间窗口设计

在支付风控建模中,样本工程是决定模型上限的基础环节。与常规机器学习任务不同,支付风控面临极端样本不平衡(欺诈交易占比通常低于 0.1%)、时序依赖强、业务概念漂移快等挑战。本章从样本定义、正负样本构建、时间窗口设计三个核心维度,系统讲解如何构建高质量的训练样本集。

2.1 样本定义:明确建模单元与粒度

样本定义的核心是回答“一条样本代表什么”。在支付风控场景中,常见的样本粒度包括:

样本粒度 典型定义 适用场景 注意事项
交易级 每笔支付交易 = 一条样本 实时交易拦截、欺诈评分 同一用户短时间内多笔交易可能高度相关
用户-日级 每个用户每天 = 一条样本 用户日度风险画像、额度管理 需聚合日内交易行为特征
用户-设备级 每个用户每台设备 = 一条样本 设备指纹关联、团伙欺诈识别 设备与用户关系可能随时间变化
账户级 每个账户生命周期 = 一条样本 账户注册风险、长期信用评估 样本量少,需谨慎处理时序特征
实践建议:支付风控模型首选交易级样本,因为它最贴近业务决策单元。如果模型用于用户画像或额度管理,可考虑用户-日级。避免在建模初期使用账户级样本,除非业务场景明确要求。

2.2 正负样本构建:标签定义与不平衡处理

正负样本的构建直接决定模型学习的目标。在支付风控中,正样本(坏样本)通常指确认欺诈交易,负样本(好样本)指正常交易。但实际业务中标签往往存在延迟、噪声和定义模糊的问题。

2.2.1 标签来源与定义规则

标签来源 定义方式 优点 缺点
用户申诉/退单 用户反馈“非本人交易”并成功退单 高置信度,直接反映用户损失 延迟高(通常 3-30 天),覆盖不全
风控人工审核 风控专家标注可疑交易 灵活,可覆盖新型欺诈 成本高,主观性强,一致性难保证
规则引擎命中 命中高置信度规则(如 IP 黑名单) 实时,可自动化 规则可能过时,存在误杀
事后关联分析 通过设备、IP、手机号关联已知欺诈 可发现团伙欺诈 关联关系可能误判,需人工复核

2.2.2 正负样本比例与采样策略

支付风控中欺诈率通常低于 0.1%,直接训练会导致模型偏向预测“正常”。常用采样策略包括:

  • 下采样(Undersampling):随机丢弃部分负样本,使正负比例达到 1:10 ~ 1:100。适用于负样本量极大的场景。
  • 上采样(Oversampling):复制正样本或使用 SMOTE 生成合成样本。适用于正样本量极少的场景,但需注意过拟合。
  • 混合采样:先对负样本下采样,再对正样本上采样,平衡样本量的同时保留多样性。
  • 权重调整:不改变样本数量,在损失函数中给正样本赋予更高权重(如 class_weight='balanced')。
关键提醒:采样必须在划分训练集/测试集之后进行,且测试集必须保持原始分布。否则会导致模型评估结果过于乐观,无法反映真实业务表现。

2.3 样本时间窗口设计:特征与标签的时间对齐

时间窗口设计是支付风控样本工程中最容易被忽视但影响最大的环节。错误的时间窗口会导致特征泄露或标签偏移,使模型在线上失效。

2.3.1 时间窗口三要素

每条样本需要明确三个时间点:

  1. 特征观察窗口(Feature Window):用于计算特征的历史时间段。例如“过去 7 天该用户的交易次数”。
  2. 标签观察窗口(Label Window):用于判断该样本是否为正样本的时间段。例如“交易发生后 30 天内是否被确认为欺诈”。
  3. 特征与标签之间的间隔(Gap):特征窗口结束到标签窗口开始之间的缓冲期,防止特征包含未来信息。
标准时间窗口结构:
特征窗口 [T - 7天, T] → 间隔 [T, T + 1天] → 标签窗口 [T + 1天, T + 31天]
其中 T 为交易发生时间。间隔 1 天是为了确保特征计算时不会用到交易当天的未来信息。

2.3.2 常见时间窗口配置

场景 特征窗口 间隔 标签窗口 说明
实时交易拦截 过去 7 天 0 天 未来 30 天 特征用交易前数据,标签用事后确认
用户日度风险评分 过去 30 天 1 天 未来 7 天 间隔 1 天避免数据穿越
设备风险画像 过去 90 天 3 天 未来 30 天 设备行为变化慢,窗口更长
新账户注册风险 注册后 1 小时 0 天 未来 7 天 注册即评估,特征窗口短

2.3.3 时间窗口设计流程图

样本时间窗口设计流程 特征窗口 过去 7 天交易行为 间隔 1 天缓冲 标签窗口 未来 30 天是否欺诈 当前时间 T 交易发生时刻 时间窗口设计核心原则 1. 特征窗口必须完全在标签窗口之前,不能包含未来信息 2. 间隔用于防止特征计算时用到交易当天的实时数据 3. 标签窗口长度需覆盖欺诈确认的平均延迟(通常 7-30 天) 4. 不同业务场景(实时拦截 vs 日度评分)需调整窗口长度

2.4 样本构建实战代码示例

以下代码展示如何基于交易日志构建带时间窗口的训练样本:

import pandas as pd
import numpy as np
from datetime import timedelta

def build_training_samples(transactions, label_df, feature_window_days=7, gap_days=1, label_window_days=30):
    """
    构建带时间窗口的训练样本集
    
    Parameters:
    -----------
    transactions : DataFrame
        交易日志,包含 user_id, trans_time, amount, device_id 等字段
    label_df : DataFrame
        标签表,包含 user_id, trans_id, label_time, is_fraud
    feature_window_days : int
        特征观察窗口天数
    gap_days : int
        特征窗口与标签窗口之间的间隔天数
    label_window_days : int
        标签观察窗口天数
    
    Returns:
    --------
    samples : DataFrame
        每条样本包含特征 + 标签
    """
    samples = []
    
    for _, trans in transactions.iterrows():
        t = trans['trans_time']
        
        # 特征窗口:[t - feature_window_days, t - gap_days]
        feature_start = t - timedelta(days=feature_window_days)
        feature_end = t - timedelta(days=gap_days)
        
        # 提取特征窗口内的历史交易
        hist = transactions[
            (transactions['user_id'] == trans['user_id']) & 
            (transactions['trans_time'] >= feature_start) & 
            (transactions['trans_time'] < feature_end)
        ]
        
        # 计算特征(示例:过去7天交易次数、金额均值)
        features = {
            'user_id': trans['user_id'],
            'trans_time': t,
            'trans_amount': trans['amount'],
            'hist_count_7d': len(hist),
            'hist_amount_mean_7d': hist['amount'].mean() if len(hist) > 0 else 0,
            'hist_amount_std_7d': hist['amount'].std() if len(hist) > 0 else 0,
        }
        
        # 标签窗口:[t + gap_days, t + gap_days + label_window_days]
        label_start = t + timedelta(days=gap_days)
        label_end = t + timedelta(days=gap_days + label_window_days)
        
        # 查找该交易是否在标签窗口内被确认为欺诈
        label = label_df[
            (label_df['trans_id'] == trans['trans_id']) & 
            (label_df['label_time'] >= label_start) & 
            (label_df['label_time'] < label_end)
        ]
        
        features['label'] = 1 if len(label) > 0 and label['is_fraud'].any() else 0
        samples.append(features)
    
    return pd.DataFrame(samples)

# 使用示例
# samples = build_training_samples(transactions, labels, 
#                                  feature_window_days=7, 
#                                  gap_days=1, 
#                                  label_window_days=30)

2.5 样本工程常见陷阱与避坑指南

陷阱 表现 后果 解决方案
特征泄露 特征中包含了未来信息 模型在训练集表现极好,线上效果差 严格按时间窗口划分特征与标签,加入间隔
标签延迟 标签窗口太短,未覆盖确认延迟 正样本被误标为负样本,模型漏检 根据业务确认延迟设置标签窗口(通常 ≥ 30 天)
样本时间重叠 训练集和测试集的时间段有重叠 模型评估结果虚高,无法反映未来表现 按时间顺序划分训练/验证/测试集,禁止随机划分
采样偏差 采样后训练集分布与真实分布不一致 模型在线上遇到新分布时失效 测试集保持原始分布;采样后验证模型在原始分布上的表现
概念漂移 欺诈模式随时间变化 旧样本训练的模型无法识别新欺诈 定期用最近 3-6 个月数据重新训练,监控模型衰减
样本工程黄金法则:在支付风控中,时间顺序比样本量更重要。宁可样本量少一些,也要保证时间窗口正确、无特征泄露、标签定义清晰。一个时间窗口错误的百万级样本集,不如一个时间窗口正确的十万级样本集。