3、时间序列数据特性:时间依赖性、非平稳性、时序相关性对切分的约束
实时风控数据本质上是时间序列数据。与独立同分布(i.i.d.)的静态数据集不同,时间序列数据在时间维度上存在内在结构。如果忽略这些结构直接进行随机切分,会导致严重的数据泄露和过拟合,使模型在线上表现远低于离线评估。本节深入剖析时间序列的三个核心特性,并阐明它们对训练集切分的硬性约束。
3.1 时间依赖性:切分不能破坏时序因果
时间依赖性是指当前时刻的观测值(如交易是否欺诈)与过去时刻的观测值存在关联。在风控场景中,这种依赖表现为:
- 用户行为序列:用户过去1小时的登录、浏览、支付行为,直接影响当前交易的风险评分。
- 设备指纹演化:设备在过去7天内关联的账号数量,是判断设备是否被恶意控制的关键特征。
- 团伙关联图:当前交易对手方是否与历史黑名单账户存在资金往来。
切分约束:训练集和测试集必须保持严格的时间先后顺序,即训练集的时间窗口必须完全早于测试集的时间窗口。任何随机打乱或交叉验证都会将未来的信息泄露到训练集中,导致模型“预知未来”。
典型错误:使用K折交叉验证对时间序列数据进行随机切分。例如,将第1天和第30天的样本同时放入训练集,而将第15天的样本放入测试集。模型会学到第30天之后才出现的模式,导致测试集AUC虚高。
3.2 非平稳性:切分窗口必须适应分布漂移
非平稳性是指数据的统计特性(均值、方差、协方差)随时间变化。在风控领域,非平稳性无处不在:
- 概念漂移:欺诈分子的手法不断进化,今天有效的规则,明天可能失效。
- 周期性波动:工作日与周末的交易模式不同,大促期间的欺诈率显著上升。
- 外部冲击:监管政策变化、黑产工具升级、宏观经济波动都会导致数据分布突变。
切分约束:训练集和测试集必须来自相同或相近的分布。如果训练集只包含1-6月的数据,而测试集包含7-12月的数据,且7月发生了重大政策变更,则模型评估结果将不可靠。切分策略需要引入时间衰减权重或滑动窗口来适应分布漂移。
| 非平稳类型 | 风控实例 | 切分应对策略 |
|---|---|---|
| 概念漂移 | 欺诈团伙从“盗刷”转向“虚假注册” | 使用最近N天的数据训练,丢弃过时样本 |
| 周期性 | 周末交易量下降但欺诈率上升 | 确保训练集和测试集包含完整的周期 |
| 突变 | 新支付通道上线导致特征分布剧变 | 在突变点前后分别评估,或使用增量学习 |
3.3 时序相关性:切分不能破坏特征与标签的时间对齐
时序相关性不仅存在于样本之间,还存在于特征与标签之间。在风控特征工程中,许多特征本身就是基于历史时间窗口计算的:
- 滑动窗口特征:过去30天内该用户的交易次数、过去7天内该设备的IP变更次数。
- 滞后特征:上一次交易金额、上一次登录时间距现在的小时数。
- 差分特征:当前交易金额与过去3笔交易平均金额的差值。
切分约束:在切分时,必须保证特征计算所依赖的历史数据不被切分到测试集。如果训练集和测试集在时间上重叠,或者测试集样本被用于计算训练集样本的特征,就会产生特征泄露。
常见陷阱:在构建特征时使用了全量数据(包括未来数据)计算滑动窗口均值,然后再进行切分。这会导致训练集和测试集共享了未来的统计信息,模型评估结果严重失真。
3.4 三种特性对切分策略的综合影响
在实际的实时风控系统中,三种特性往往同时存在,相互叠加。下表总结了它们对切分策略的具体影响:
| 特性 | 对切分的影响 | 必须避免的操作 | 推荐策略 |
|---|---|---|---|
| 时间依赖性 | 样本间存在时序因果链 | 随机打乱、K折交叉验证 | 按时间顺序切分,训练集在前,测试集在后 |
| 非平稳性 | 模型在旧分布上训练,在新分布上评估 | 使用过旧的数据训练,评估未来的数据 | 滑动窗口训练、时间衰减采样、周期性对齐 |
| 时序相关性 | 特征计算依赖历史窗口 | 跨切分边界计算滑动窗口特征 | 特征回溯隔离,确保特征只使用切分点之前的数据 |
3.5 实战案例:特征泄露的切分陷阱
以下代码展示了一个典型的特征泄露场景:在切分前计算了全局滑动窗口特征,导致训练集和测试集共享了未来信息。
import pandas as pd
import numpy as np
# 错误做法:先计算特征,再切分
df = pd.read_csv('transactions.csv')
df['avg_amount_30d'] = df.groupby('user_id')['amount'].transform(
lambda x: x.rolling(30, min_periods=1).mean()
)
# 此时 avg_amount_30d 已经包含了未来交易的信息
# 随机切分(错误)
train = df.sample(frac=0.7, random_state=42)
test = df.drop(train.index)
# 测试集中的交易可能被用于计算训练集中用户的 avg_amount_30d
# 正确做法:先按时间切分,再在各自窗口内计算特征
df = df.sort_values('timestamp')
cutoff = df['timestamp'].quantile(0.7)
train_df = df[df['timestamp'] <= cutoff].copy()
test_df = df[df['timestamp'] > cutoff].copy()
# 在训练集内计算特征(只使用训练集的历史数据)
train_df['avg_amount_30d'] = train_df.groupby('user_id')['amount'].transform(
lambda x: x.rolling(30, min_periods=1).mean()
)
# 在测试集内计算特征(只使用测试集的历史数据,且不跨越切分点)
test_df['avg_amount_30d'] = test_df.groupby('user_id')['amount'].transform(
lambda x: x.rolling(30, min_periods=1).mean()
)
最佳实践:在实时风控系统中,建议将特征计算逻辑封装为独立的函数,并传入时间窗口参数。切分时,先切分时间窗口,再对每个窗口独立执行特征计算,确保特征计算不跨越切分边界。
3.6 切分约束的工程落地检查清单
在实时风控系统的开发中,建议使用以下检查清单来验证切分策略是否正确:
- 时间戳检查:训练集的最大时间戳是否小于测试集的最小时间戳?
- 特征回溯检查:所有滑动窗口特征是否只使用了切分点之前的数据?
- 分布漂移检查:训练集和测试集的特征分布是否存在显著差异(使用KS检验或PSI指标)?
- 泄露检测:是否存在某个特征在训练集和测试集中取值完全相同(可能是全局计算导致的泄露)?
- 时间对齐检查:标签的时间是否晚于所有特征的时间?
核心结论:时间依赖性、非平稳性和时序相关性是实时风控数据的内在属性。它们共同决定了切分策略必须采用时间序列切分,并辅以滑动窗口和特征回溯隔离技术。忽略这些约束,将导致模型评估结果与线上表现严重不一致,造成业务决策失误。