Feature Store 特征平台落地:从 Feast 架构到实践
从 offline/online store、registry 与 point-in-time join 讲透 Feast 架构,给出与实时/离线数据链路结合的落地路径
特征平台要解决的是两个具体问题:特征重复开发,以及训练与线上推理的不一致(train-serve skew)。前者是工程浪费,同一批特征在每个模型项目里各写一遍特征工程代码;后者是质量事故,训练时用的特征口径和线上服务时取到的特征对不上,模型上线后效果立刻衰减。Feast 是这类开源特征平台的代表(项目文档见 docs.feast.dev,由 LF AI & Data 基金会托管),它的架构可以用三个核心组件讲透:offline store 负责历史特征与训练数据、online store 负责在线低延迟特征获取、registry 负责特征元数据注册,三者之上还有 point-in-time join 保证训练样本不泄漏未来信息。本文按这三个组件拆解架构,再给出与实时/离线数据链路结合的落地路径。
先讲 offline store。离线特征的本质是"按时间组织的历史特征表",用于模型训练时生成训练样本。Feast 的 offline store 不自己存数据,而是把特征数据源映射到已有的存储上——比如数仓表、数据湖表、对象存储上的文件——训练时通过 SQL 把特征表与标签表 join 成训练数据集。offline store 承担的关键职责是 point-in-time join:每个训练样本都对应一个事件时间(比如"用户在第 5 天下单"),join 时只能使用"在那个时间点之前已经产生"的特征值,绝不能把未来的特征(比如用户第 6 天的行为)混进第 5 天的样本里。这就是标签泄漏(label leakage)的经典来源:如果只是简单地把特征表和标签表按主键 join,时间上"未来"的特征会被当成"当时已知"的信息,模型在训练集上表现很好,上线后立刻崩。point-in-time join 保证每个样本只看到当时可见的特征快照,这是特征平台区别于普通特征表 join 的核心能力。
再讲 online store。在线推理(推荐、风控等实时场景)需要在毫秒级延迟内取到用户当前的特征,不可能去查询离线大表。online store 是专为低延迟设计的特征存储,常见实现包括 Redis、DynamoDB 等 KV 存储,key 是实体 ID(如 user_id),value 是特征的最新值。特征从离线链路计算出来后,通过在线同步写入 online store;线上服务通过 Feast 的在线获取 API(GetOnlineFeatures)一次批量取回多个特征。offline store 与 online store 的对应关系是"同一份特征定义,两种物理存储":训练用离线版本(历史值),推理用在线版本(最新值)。两者是否一致,正是 train-serve skew 的源头——如果离线特征和在线特征由两套代码分别计算,口径迟早分叉。
第三是 registry。registry 是特征元数据的注册中心,记录"有哪些特征、特征怎么算出来的、属于哪个 Feature View、实体是什么、存在哪个 store 里"。Feast 中特征定义围绕三个概念组织。Entity 是特征的业务主键,比如 user_id、item_id,它定义特征"属于谁"。Feature View 是一组特征的定义单元,包含特征来源(source,通常是某张表或某个流)、关联的 Entity、以及特征的 TTL(特征有效期,超过 TTL 的特征值视为过期);一个 Feature View 对应"由同一份逻辑计算出来的一组特征"。Feature Service 则是面向线上服务的特征组合打包,一个推荐服务需要"用户侧特征 20 个 + 商品侧特征 10 个",可以打包成一个 Feature Service,线上调用一次 API 全部取回。registry 还承担版本管理:特征定义变更(比如口径调整)要有版本记录,否则训练与线上各自引用了不同版本的定义而不自知。Google Cloud 的Feast 介绍与 AWS 的 Feast on AWS 实践都遵循"离线训练与在线服务共用同一份特征定义"的模式。
与实时/离线数据链路结合是落地的关键。典型的结合方式是三层:批特征走离线链路——数仓或湖仓的批任务(如日调度)计算历史特征写入 offline store,同时把最新值同步到 online store;流特征走实时链路——Kafka 等消息流经流处理任务实时计算特征,直接更新 online store,并按需落一份到离线存储供训练使用;在线获取走 API——线上服务调用 Feast 的在线获取接口,一次拿到批特征与流特征的最新值。关键在于特征定义只写一次,offline/online 两套物理存储由平台根据定义自动维护,而不是各写各的。
落地步骤按四步推进。第一步盘点特征:把现有模型项目里重复出现的特征梳理出来,按实体归类,优先选取"多个项目共用、口径稳定"的特征进入平台,第一批不要贪多。第二步定义与接入:为选定的特征写 Entity、Feature View、Feature Service 定义,接入离线数据源(先跑通离线部分,训练数据集通过 point-in-time join 生成,验证样本与手工逻辑一致)。第三步上线在线部分:配置 online store,跑通批特征同步与在线获取 API,用线上请求抽样对比在线特征值与离线特征值,这一步是防 train-serve skew 的关键验证。第四步引入流特征:接入实时链路,完善监控(同步延迟、在线 store 命中率、特征 TTL 过期率)。Feast 官方文档也强调,从离线起步、逐步加在线和流,是降低落地风险的最稳路径。
反模式比成功路径更值得记住。第一是"把 Feature Store 当数据库用":特征平台的职责是特征定义、版本与双 store 一致性,不是任意数据的存储,把业务明细数据也塞进来会让 TTL、同步、血缘全部失控。第二是"特征全部实时化":不是所有特征都需要流式计算,口径稳定的日级特征走批链路更省钱更稳定,实时化只给真正影响在线效果的场景。第三是"跳过 point-in-time join":为了省事直接用当前值 join 历史样本,等于人为制造标签泄漏,训练效果虚高,上线即翻车。第四是"定义版本裸奔":特征口径变更不留版本、不通知下游,线上与训练悄然分叉,出问题时难以定位。第五是"在线 store 无监控":同步任务挂了一整天没人发现,线上默默用着旧特征,建议把同步延迟与命中率纳入告警。
多环境管理与特征监控是平台长期运行的保障。特征定义会经历开发、测试、上线三个阶段,建议 registry 按环境隔离:开发环境允许随意改定义,生产环境的定义变更要走评审并记录版本,避免开发中的特征被线上服务意外引用。特征上线后要监控三件事:在线 store 的同步延迟(批特征多久更新一次)、在线获取 API 的命中率(请求的特征是否都能取到值)、以及特征值的分布漂移(线上分布与训练分布明显偏离时,模型效果可能已经在退化)。特征血缘也要从定义阶段就记录:一个特征来自哪张源表、经过什么计算、被哪些模型使用,出了问题才能从模型反查到源头。这些能力在起步阶段可以很简陋,但架构上要留出位置,否则规模上来之后再补,成本会高出一个量级。
给你的下一步是:挑一个现有模型项目做试点,只做离线部分——用 Feast 定义它的特征、生成训练数据,与原来的手工特征工程产出的训练集做一致性对比;对上了,再谈在线与流。先验证"平台不会改变我的模型结果",是让算法团队接受特征平台最有效的方式。


