3. 特征工程入门:实时特征与离线特征的区别、滑动窗口特征、时间衰减特征

在实时风控系统中,特征工程是连接原始数据与模型决策的桥梁。与传统的离线风控不同,实时风控对特征的时效性、计算延迟和存储方式提出了严苛要求。本章将系统讲解实时特征与离线特征的核心区别,并深入剖析两种最常用的实时特征构造方法:滑动窗口特征与时间衰减特征。

3.1 实时特征 vs 离线特征:核心差异

离线特征通常基于全量历史数据批量计算,适用于模型训练和周期性评估;而实时特征必须在毫秒级延迟内完成计算,服务于在线推理。两者的差异贯穿数据源、计算模式、存储与更新机制。

对比维度 离线特征 实时特征
数据源 全量历史表(Hive / 离线数仓) 实时流(Kafka / 实时数仓)
计算模式 批量调度(T+1 / 小时级) 增量计算(毫秒级)
存储介质 HDFS / 离线表 Redis / 内存状态存储
更新频率 固定周期(每日/每小时) 事件驱动(每次请求/每笔交易)
延迟容忍 分钟级 ~ 小时级 < 100ms
典型用途 模型训练、离线回溯、报表 在线推理、实时决策
关键原则: 训练集切分时,必须保证离线特征与实时特征在时间戳上严格对齐。使用离线特征训练模型,再用实时特征上线推理,会导致特征分布偏移(Feature Drift),直接影响模型效果。

3.2 滑动窗口特征

滑动窗口特征是最基础的实时特征构造方法。它统计过去一段时间窗口内(如过去 1 小时、过去 24 小时)用户或设备的行为聚合值,如计数、求和、均值、标准差等。窗口可以是固定长度(如最近 30 分钟)或会话长度(如当前登录会话)。

3.2.1 滑动窗口的三种实现方式

  • 跳跃窗口(Tumbling Window): 窗口之间不重叠,按固定时间间隔切分。适合离线批处理,实时场景下容易丢失边界事件。
  • 滑动窗口(Sliding Window): 窗口之间部分重叠,每次滑动一个步长。实时风控中最常用,能捕捉连续行为模式。
  • 会话窗口(Session Window): 基于事件间隔动态划分窗口,超过静默阈值则关闭当前窗口。适合用户行为序列分析。
滑动窗口特征计算示意图 t 当前窗口(过去 1 小时) 上一个窗口(1 小时前) 现在 滑动窗口特征 = 窗口内所有事件聚合(计数/求和/均值),随当前时间推进不断更新

3.2.2 滑动窗口特征实战示例

以下代码展示在 Flink SQL 中定义 1 小时滑动窗口特征,统计用户过去 1 小时内的交易次数与金额总和:

-- 实时风控滑动窗口特征(Flink SQL)
CREATE VIEW user_1h_features AS
SELECT
    user_id,
    COUNT(*)                           AS trans_cnt_1h,
    SUM(amount)                        AS trans_amt_sum_1h,
    AVG(amount)                        AS trans_amt_avg_1h,
    STDDEV(amount)                     AS trans_amt_std_1h,
    COUNT(DISTINCT device_id)          AS distinct_device_cnt_1h
FROM transaction_stream
WHERE event_time BETWEEN CURRENT_TIMESTAMP - INTERVAL '1' HOUR AND CURRENT_TIMESTAMP
GROUP BY user_id;
工程注意: 滑动窗口特征在实时计算中需要维护窗口状态。建议将窗口大小控制在 24 小时以内,避免状态膨胀。对于超长窗口(如 7 天),考虑使用时间衰减特征替代。

3.3 时间衰减特征

时间衰减特征解决了滑动窗口的“硬边界”问题——窗口边界处的事件被突然丢弃,导致特征值跳变。衰减特征通过给每个事件赋予一个随时间递减的权重,使得越近的事件贡献越大,越远的事件逐渐被遗忘,形成平滑的衰减曲线。

3.3.1 常用衰减函数

衰减函数 公式 特点 适用场景
指数衰减 w(t) = e-λ·Δt 衰减速度由 λ 控制,λ 越大衰减越快 短期行为模式(如秒级/分钟级)
线性衰减 w(t) = max(0, 1 - α·Δt) 简单直观,权重线性下降至 0 固定时间窗口的平滑版本
对数衰减 w(t) = 1 / log(e + β·Δt) 衰减速度先快后慢,长尾保留 长期行为画像(如天级/周级)

3.3.2 指数衰减特征计算流程

以指数衰减为例,实时计算用户历史交易金额的衰减加权和:

  1. 初始化: 在 Redis 中存储用户当前衰减总值 decay_sum 和最后更新时间 last_update。
  2. 事件到达: 读取 decay_sum,先按时间差 Δt 进行衰减:decay_sum *= e-λ·Δt。
  3. 叠加新值: 将当前事件金额 amount 累加到衰减后的总值上。
  4. 更新状态: 写回 decay_sum 和当前时间戳 last_update。
# 指数衰减特征实时更新(Python 伪代码)
import time
import math

LAMBDA = 0.001  # 衰减系数,控制遗忘速度

def update_decay_feature(user_id, amount, redis_client):
    key = f"decay_sum:{user_id}"
    last_sum, last_time = redis_client.hmget(key, ["sum", "timestamp"])

    now = time.time()
    if last_sum is None:
        # 首次初始化
        decay_sum = amount
    else:
        delta_t = now - float(last_time)
        decay_sum = float(last_sum) * math.exp(-LAMBDA * delta_t) + amount

    redis_client.hset(key, mapping={"sum": decay_sum, "timestamp": now})
    return decay_sum
衰减系数调参: λ 的选择直接影响特征敏感性。λ 过大,历史信息被快速遗忘,特征波动剧烈;λ 过小,衰减接近无状态,失去时间敏感性。建议通过离线回溯实验确定最优 λ,通常取值范围为 0.0001 ~ 0.01。

3.4 滑动窗口 vs 时间衰减:选型对比

两种特征构造方法各有优劣,实际风控系统中常混合使用。以下对比帮助你在不同场景下做出选择:

对比维度 滑动窗口特征 时间衰减特征
边界效应 窗口边界处特征跳变 平滑连续,无跳变
计算复杂度 低(仅需窗口内聚合) 中(需维护衰减状态)
状态存储 窗口内所有事件(可能较大) 仅存储聚合值与时间戳(极小)
可解释性 高(“过去 1 小时交易次数”) 中(“衰减加权和”需额外说明)
长周期支持 窗口过长时状态膨胀 天然支持长周期,状态恒定
推荐场景 短周期行为(分钟~小时) 长周期画像(天~周)
实时风控特征工程架构 实时事件流 滑动窗口特征 计数 / 求和 / 均值 / 标准差 时间衰减特征 指数 / 线性 / 对数衰减 实时特征向量 → 模型推理

3.5 本章小结

  • 实时特征与离线特征在数据源、计算模式、存储和延迟要求上存在本质差异,训练集切分时必须保持特征对齐。
  • 滑动窗口特征适用于短周期行为统计,实现简单、可解释性强,但存在边界跳变和状态膨胀问题。
  • 时间衰减特征通过平滑权重解决边界效应,状态存储极小,适合长周期画像,但需要调参衰减系数。
  • 实际生产环境中,建议混合使用两种特征:短周期用滑动窗口,长周期用时间衰减,兼顾实时性与稳定性。