AI特征仓库:重复不够用了之后
我们团队当时在做几个独立项目:
表面上看是三个系统,但底层特征大量重复:用户活跃度、购买力、设备稳定性等等。
我们团队当时在做几个独立项目:
表面上看是三个系统,但底层特征大量重复:用户活跃度、购买力、设备稳定性等等。
为什么需要特征仓库
先说清楚遇到了什么问题。我们团队当时在做几个独立项目:
- 商品推荐系统:需要用户最近 30 天点击、购买、收藏行为特征
- 风控系统:需要用户最近 7 天登录、交易、设备变更特征
- 营销系统:需要用户最近 90 天活动参与、优惠券使用特征
表面上看是三个系统,但底层特征大量重复:用户活跃度、购买力、设备稳定性等等。每个项目都要自己算一遍,结果就是:
- 特征定义不一致:同样是"最近 30 天购买次数",有人算的是自然月,有人是滚动窗口,还有人忽略了退款订单
- 特征漂移:离线训练时用的特征工程逻辑,线上推理时重新写一遍,两边对不上
- 重复计算资源浪费:同一个聚合特征在多个系统里独立算,存储和计算成本翻倍
- 新模型上线慢:每次要重新对接特征管道,特征从算到用至少要两周
这些问题的本质是:特征工程缺少一个中心化的管理机制。大家各自造轮子,轮子质量参差不齐。
什么是特征仓库
特征仓库的概念其实很简单,就像代码仓库管理代码一样,特征仓库负责统一管理、存储和提供特征。
核心解决三个问题:
- 特征定义统一:一个特征的计算逻辑只定义一次,离线训练和在线推理共用
- 特征共享复用:不同模型、不同系统可以复用同一套特征
- 特征版本可追溯:特征定义变更可追踪,方便回滚和问题排查
业界开源方案里,Feast 算是使用最广的之一。它支持离线和在线特征存储,提供统一的特征服务接口,还能和常见的训练框架直接集成。
搭建 Feast 特征仓库
环境准备
先说环境:当时用的是 Feast 0.40 版本,存储层用了 Redis(在线)和 Parquet(离线),数据源是 PostgreSQL 业务数据库。
安装比较简单,直接用 pip:
pip install feast[redis,postgres]
初始化项目
创建一个 Feast 项目:
feast init my_feature_repo
cd my_feature_repo
生成的目录结构里,feature_store.yaml 是核心配置文件。里面要指定存储类型、数据源和缓存策略。
定义特征
特征的定义在 features/ 目录下,用 Python 写。以用户特征为例:
from datetime import timedelta
from feast import Entity, Feature, FeatureView, Field
from feast.types import Float32, Int64, String
from feast.data_source import PostgreSQLSource
# 定义实体:用户
user = Entity(
name="user_id",
join_keys=["user_id"],
value_type=String,
)
# 数据源配置
user_events_source = PostgreSQLSource(
name="user_events_source",
query="""
SELECT
user_id,
event_time,
event_type,
event_value
FROM user_events
WHERE event_time >= '{{ start_date }}'
AND event_time < '{{ end_date }}'
""",
timestamp_field="event_time",
created_timestamp_column="created_at",
)
# 定义特征视图
user_features = FeatureView(
name="user_features",
entities=[user],
ttl=timedelta(days=30),
schema=[
Field(name="click_count_last_7d", dtype=Int64),
Field(name="click_count_last_30d", dtype=Int64),
Field(name="purchase_count_last_7d", dtype=Int64),
Field(name="purchase_amount_last_30d", dtype=Float32),
],
source=user_events_source,
)
这里有几个关键点要说明:
Entity是特征的唯一标识,通常是用户、商品这类主键FeatureView把多个特征组合在一起,定义存储策略和数据源ttl是特征的有效期,超过这个时间窗口的数据不提供- 数据源用 SQL 查询定义,Feast 会自动处理增量刷新
特征注册
定义完特征后,需要注册到 Feast 仓库:
feast apply
这步会把特征定义写进存储层,初始化相应的索引和缓存结构。
物化特征
特征定义好只是逻辑上的,还需要把数据物理化到存储层:
feast materialize 2026-06-01T00:00:00 2026-07-17T00:00:00
这会执行数据源查询,把结果写到离线存储(Parquet)和在线存储(Redis)。时间窗口可以按天或小时切,支持增量更新。
离线训练特征获取
训练时直接用 Feast 获取特征:
from feast import FeatureStore
store = FeatureStore(repo_path=".")
entity_df = pd.DataFrame({
"user_id": ["user_001", "user_002", "user_003"],
"event_timestamp": [datetime(2026, 7, 15), datetime(2026, 7, 15), datetime(2026, 7, 15)]
})
training_data = store.get_historical_features(
entity_df=entity_df,
feature_views=["user_features"],
).to_df()
Feast 会自动按时间点获取当时有效的特征,避免穿越问题。
在线推理特征获取
在线推理时,通过特征服务获取实时特征:
from feast import FeatureStore
store = FeatureStore(repo_path=".")
features = store.get_online_features(
feature_views=["user_features"],
entity_rows=[{"user_id": "user_001"}],
)
print(features)
# 特征值会直接从 Redis 读取,延迟通常在毫秒级
数据流向大致是这样:
这张图想说明的是:特征从数据源出来后,分别写入离线和在线存储。离线侧用于模型训练,在线侧用于实时推理。两边共用同一套特征定义,确保逻辑一致。
踩坑记录
搭建过程不顺利,踩了不少坑。这里说几个典型的。
时间窗口问题
最开始做的时候,特征值总是对不上。查了很久,发现是 ttl 和数据源查询的时间窗口不一致。
特征视图的 ttl 决定了特征从 Redis 过期的时间,但数据源查询里的时间过滤是单独控制的。如果两边逻辑没对齐,就会出现:请求时间在特征有效期外,但数据源里其实有数据。
解决办法是在数据源查询里明确时间过滤条件,让 ttl 只作为缓存控制,不做逻辑过滤。
特征漂移仍然存在
用了 Feast 之后,特征定义统一了,但特征漂移问题没有完全消失。
原因在于:离线训练用的是历史快照,而在线推理用的是实时特征。如果特征计算逻辑依赖实时数据(比如最近 1 小时的行为),两边天然存在时间差。
这个问题的解决方案是:训练时显式标注特征的时间点,推理时用相同逻辑对齐。另外就是避免过度依赖实时特征,适当加长特征的时间窗口。
存储成本增加
一开始以为集中管理特征会节省存储,结果发现存储成本反而上升了。
原因主要是:离线和在线存储各存一份,而且每个特征版本都会保留历史副本。如果特征更新频繁,磁盘占用增长很快。
后来加了几个优化:
- 冷特征从在线存储降级到离线存储,减少 Redis 占用
- 特征版本只保留最近 7 天的历史数据,更早的通过归档处理
- 对重复度高的特征做压缩,Parquet 本身对列式存储压缩效果不错
性能调优
在线推理时,特征获取的延迟一度成为瓶颈。问题出在:
- 单次请求获取的特征太多,网络往返次数增加
- Redis 连接池配置不当,高并发时连接不够用
- 特征值没有做序列化优化,数据体积偏大
优化措施:
- 特征按使用频率分组,高频特征单独缓存
- 调整 Redis 连接池大小和超时时间
- 把特征值从 JSON 改成 Protobuf 序列化,体积减少约 40%
效果和边界
折腾了几个月后,效果是有的,但也看清了一些边界。
好的方面:
- 新模型上线时间缩短:特征直接从仓库取,不用再重新对接数据管道,时间从两周降到三天左右
- 特征质量提升:统一的特征定义减少了因计算逻辑不一致导致的特征漂移
- 复用性增强:不同项目可以共用特征,开发效率明显提高
但同时也发现:
- 特征仓库不是万能药:如果特征本身就设计得不好,统一管理只会放大问题
- 维护成本不低:特征定义、数据源配置、存储优化都需要持续投入
- 过度集中有风险:所有模型都依赖同一个特征仓库,单点故障影响面很大
结语
特征仓库这件事,本质上是在解决重复造轮子的问题。它让特征从"一次性资源"变成了"可复用资产",这当然是好的。
但技术方案从来不是万能的。特征仓库能解决特征工程里的重复和一致性问题,但解决不了特征本身的业务价值问题。如果特征设计本身有问题,再好的管理机制也只是让问题更集中。
实际使用中,更像是在集中和分散之间找平衡:核心特征集中管理,边缘特征允许一定程度的冗余。完全统一会失去灵活性,完全分散又会陷入重复造轮子的泥潭。
技术的边界往往不是工具本身,而是我们在工具和业务之间做出的选择。
版权声明: 本文首发于 指尖魔法屋-AI特征仓库:重复不够用了之后(https://blog.thinkmoon.cn/post/286-ai-feature-store-duplicate-shared-practice/) 转载或引用必须申明原指尖魔法屋来源及源地址!
评论
使用 GitHub 账号登录后即可留言,支持 Markdown。