2. 特征工程基础回顾:离线与实时特征生产流程
在深入偏差校验之前,必须清晰理解两种特征生产架构的本质差异。离线特征与实时特征在数据源、计算引擎、存储介质、时效性要求上存在根本性不同,这些差异直接决定了后续校验策略的设计。
2.1 离线特征生产流程(批处理 ETL)
离线特征生产以“批处理”为核心,通常以 T+1 或小时级为周期,处理全量历史数据。其典型流程如下:
- 数据源接入:从业务数据库(MySQL、PostgreSQL)、日志文件(HDFS、S3)、消息队列(Kafka 落盘)中抽取原始数据。
- 数据清洗与对齐:处理缺失值、异常值、时间戳对齐,确保数据质量。
- 特征计算:使用 Spark、Hive、MapReduce 等批处理引擎,执行聚合、窗口函数、Join 等复杂计算。
- 特征存储:将计算结果写入离线特征表(Hive 表、Parquet 文件)或特征存储系统(Feast、Tecton 的离线存储)。
- 数据分发:通过离线同步工具(DataX、Sqoop)将特征表推送到线上 KV 存储(Redis、HBase)供模型推理使用。
关键特征: 高吞吐、全量计算、数据一致性由事务保证、可回溯重算、延迟通常在分钟到小时级别。
2.2 实时特征生产流程(流处理)
实时特征生产以“流处理”为核心,每条数据到达后立即处理,延迟控制在秒级甚至毫秒级。典型流程如下:
- 实时数据源接入:通过 Kafka、Pulsar、AWS Kinesis 等消息队列接入点击流、交易流水、IoT 事件等实时数据。
- 流式特征计算:使用 Flink、Spark Streaming、Kafka Streams 等流处理引擎,在内存中维护状态,执行滑动窗口聚合、计数、去重等操作。
- 状态管理:利用 RocksDB、RocksDB + 远程存储或 Flink State Backend 管理中间状态,支持 Exactly-Once 语义。
- 实时特征输出:计算结果直接写入在线特征存储(Redis、HBase、DynamoDB)或通过消息队列推送给下游推理服务。
- 特征更新:每条新事件触发特征值更新,模型请求时读取最新值。
核心挑战: 状态一致性、乱序数据处理、窗口边界对齐、背压处理。实时特征通常无法回溯重算,一旦状态丢失需要从 checkpoint 恢复。
2.3 两种架构的核心差异
下表从多个维度对比离线与实时特征生产架构,帮助理解偏差产生的根源:
| 对比维度 | 离线特征(批处理) | 实时特征(流处理) |
|---|---|---|
| 计算引擎 | Spark、Hive、MapReduce | Flink、Spark Streaming、Kafka Streams |
| 数据范围 | 全量历史数据(T+1 或小时级) | 当前窗口内数据(秒级滑动) |
| 处理延迟 | 分钟 ~ 小时 | 毫秒 ~ 秒 |
| 计算语义 | Exactly-Once(通过事务保证) | At-Least-Once / Exactly-Once(依赖 checkpoint) |
| 状态管理 | 无状态(每次全量扫描) | 有状态(RocksDB、State Backend) |
| 数据一致性 | 强一致(ACID 事务) | 最终一致(乱序、延迟到达) |
| 回溯能力 | 强(可重跑任意历史分区) | 弱(依赖 checkpoint 和日志回放) |
| 存储介质 | HDFS、Hive 表、Parquet | Redis、HBase、内存状态 |
| 典型场景 | 用户画像、历史统计特征 | 实时点击率、滑动窗口计数 |
2.4 架构对比示意图
下图直观展示离线与实时特征生产流程的差异,重点突出数据流向、计算引擎和存储目标的不同:
2.5 偏差产生的根本原因
从上述对比可以看出,离线与实时特征之间的偏差主要源于以下三个层面:
- 时间窗口差异:离线使用固定日历窗口(如 00:00-23:59),实时使用滑动窗口(如过去 5 分钟),窗口边界不一致导致聚合结果不同。
- 数据完整性差异:离线处理全量已落盘数据,实时处理窗口内到达的数据,乱序、延迟、丢包都会导致实时特征值不完整。
- 计算语义差异:离线支持复杂 Join 和子查询,实时受限于状态大小和延迟,通常只能做简单聚合,导致特征逻辑不一致。