特征服务架构设计

特征服务是连接原始数据与机器学习模型的中间层,负责将业务数据转化为模型可消费的特征向量。在行情规则灰度发布场景中,特征服务需要同时支撑离线训练和在线推理,保证特征口径一致、延迟可控、可回溯。

特征服务的核心职责

特征服务并非简单的数据搬运工具,它承担以下四项关键职责:

  • 特征计算与转换:对原始行情数据进行聚合、滑动窗口、比率计算等操作,生成高维特征。
  • 特征存储与索引:按实体ID(如股票代码)和时间戳组织特征,支持快速点查和范围查询。
  • 特征一致性保障:确保离线训练使用的特征与在线推理时生成的特征在逻辑上完全一致,避免训练/推理偏差。
  • 特征版本与回溯:支持特征逻辑的版本管理,允许回放历史数据生成指定时间点的特征快照,用于回测和灰度对比。
灰度发布中的特殊要求:特征服务必须支持按规则ID、实验组ID进行特征路由,使得灰度组和对照组可以同时使用不同版本的特征逻辑,而互不影响。

在线特征与离线特征

在线特征与离线特征在计算方式、存储介质、延迟要求上存在本质差异。下表从多个维度进行对比:

对比维度 在线特征 离线特征
计算时机 实时计算,请求到达时触发 批量预计算,T+1或小时级
延迟要求 毫秒级(通常 < 10ms) 分钟级到小时级
数据源 实时行情流(Kafka/Pulsar) 历史行情数仓(Hive/Parquet)
存储选型 Redis / Memcached / 本地缓存 HDFS / 对象存储 / 特征仓库
特征口径 必须与离线严格对齐 作为基准,定义特征逻辑
使用场景 实时风控、实时推荐、实时定价 模型训练、回测、离线分析

在行情规则灰度发布中,离线特征用于训练灰度规则模型和生成回测数据集;在线特征则用于实时推理,判断当前行情是否触发灰度规则。两者必须共享同一套特征定义代码,避免出现“离线效果好、在线效果差”的割裂问题。

最佳实践:将特征计算逻辑封装为独立的函数或算子库,在离线批处理作业和在线流处理作业中引用同一份代码,从源头保证口径一致。

特征存储选型:Redis vs. Feast

特征存储的选择直接影响在线服务的延迟、离线回溯的便利性以及整体架构的复杂度。以下重点对比两种主流方案:Redis(纯内存KV存储)和 Feast(开源特征仓库)。

Redis 方案

Redis 作为在线特征存储的核心优势在于极致的低延迟和简单部署。在行情场景中,通常将特征以 实体ID:特征名 为 key,特征值为 value 存储在 Redis 中。

# 特征写入示例(Python + redis-py)
import redis
import json

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

# 存储股票 600519 的实时特征
feature_key = "stock:600519:features"
feature_value = {
    "ma5": 185.20,
    "ma10": 182.35,
    "volume_ratio": 1.25,
    "turnover_rate": 0.0032
}
r.hset(feature_key, mapping=feature_value)

# 在线推理时读取
features = r.hgetall(feature_key)
print(features)

Redis 方案的优缺点:

  • 优点:延迟极低(1-3ms)、运维成熟、支持多种数据结构(Hash/Sorted Set/Stream)。
  • 缺点:无内置特征版本管理、离线回溯需自行实现、数据一致性依赖外部机制。

Feast 方案

Feast 是专为ML设计的特征仓库,统一管理离线特征表和在线特征存储。它通过 Feature View 定义特征逻辑,自动同步到在线存储(如Redis)中。

# Feast 特征定义示例(feature_view.yaml)
name: stock_technical_features
entities:
  - stock_code
features:
  - name: ma5
    dtype: float32
  - name: ma10
    dtype: float32
  - name: volume_ratio
    dtype: float32
  - name: turnover_rate
    dtype: float32
batch_source:
  type: bigquery
  table_ref: "project.dataset.stock_daily_features"
online_store:
  type: redis
  connection_string: "localhost:6379"

Feast 方案的优缺点:

  • 优点:内置特征版本管理、离线/在线自动同步、支持时间旅行查询(Time Travel)、与主流ML框架集成。
  • 缺点:引入额外组件(Feast Server、Registry)、运维复杂度增加、在线延迟略高于纯Redis。

选型对比表

评估维度 Redis Feast
在线延迟 1-3ms 3-8ms(含SDK开销)
特征版本管理 需自研 原生支持
离线回溯能力 弱,需额外开发 强,支持时间旅行
运维复杂度 低 中高
灰度发布支持 需通过key命名空间隔离 通过Feature View版本隔离
适用场景 高吞吐、低延迟、特征逻辑稳定 特征频繁迭代、需要严格一致性
灰度发布中的选型建议:如果团队特征迭代频率低(每月少于2次),且在线延迟要求极高(< 5ms),优先选择 Redis + 自研版本管理;如果特征逻辑每周都在调整,且需要频繁回测历史数据,Feast 是更合适的选择。

特征服务架构总览

下图展示了结合 Redis 和 Feast 的特征服务架构,覆盖从数据源到在线推理的完整链路:

特征服务架构图 特征服务架构总览 实时行情流 (Kafka) 历史行情数仓 (Hive) 特征计算引擎 在线特征存储 (Redis) 离线特征仓库 (Feast) 模型训练 / 回测 在线推理服务 灰度规则引擎 灰度结果输出 特征一致性保障 离线/在线共享同一份特征定义

架构图中,特征计算引擎同时消费实时流和离线数据,将计算结果分别写入 Redis(在线)和 Feast(离线)。在线推理服务从 Redis 读取特征,灰度规则引擎根据特征值判断是否命中灰度规则;模型训练和回测则从 Feast 拉取历史特征数据。

灰度发布中的特征隔离策略

在行情规则灰度发布期间,不同实验组可能使用不同版本的特征逻辑。特征服务需要支持以下隔离策略:

  1. Key 命名空间隔离(Redis 方案):在特征 key 中加入实验组标识,例如 stock:600519:features:v2,不同组读取不同 key。
  2. Feature View 版本隔离(Feast 方案):为每个灰度版本创建独立的 Feature View,通过 Feast SDK 指定版本号获取特征。
  3. 双写双读:灰度期间同时写入新旧两套特征,在线服务根据请求中的实验组标识路由到对应的特征版本。
推荐做法:在灰度初期使用 Key 命名空间隔离,快速验证特征逻辑;灰度稳定后,将新特征逻辑合并为主版本,清理旧 key,避免存储膨胀。

特征服务的架构设计直接决定了灰度发布的灵活性和稳定性。选择适合团队技术栈和迭代节奏的存储方案,并提前规划好特征隔离策略,是灰度发布成功的关键基础。