Feast Local 模板快速入门:从零搭建本地 Feature Store 并跑通全链路
【免费下载链接】feastThe Open Source Feature Store for AI/ML项目地址: https://gitcode.com/GitHub_Trending/fe/feast
本文基于 Feast 官方
local模板(README.md),面向初次接触 Feast 的开发者,完整讲解如何通过feast init -t local生成一个可运行的本地 Feature Store 项目,逐行剖析模板中的 feature 定义与配置文件,跑通「定义特征 → 生成训练数据 → 物化在线特征 → 在线检索 → 流式推数」的完整闭环,并给出从本地开发走向生产部署的升级路线。
一、模板概览:local 模板里有什么
local模板位于仓库 sdk/python/feast/templates/local/ 目录,feast init -t local会把它复制到用户指定的目录。生成后的 feature 仓库包含四类内容:
| 文件/目录 | 作用 |
|---|---|
data/ | 存放原始演示用的 parquet 数据 |
feature_repo/feature_definitions.py | 演示用的特征定义(Entity、FeatureView、FeatureService 等) |
feature_repo/feature_store.yaml | 演示配置,声明数据源、Registry、Provider 等指向哪里 |
feature_repo/test_workflow.py | 展示 Feast 全部关键命令的脚本:定义、检索、推送特征 |
整个流程可以一条命令跑完:
python test_workflow.pytest_workflow.py内部依次执行了feast apply、离线特征获取(训练与批量打分两种模式)、materialize_incremental物化、在线特征获取(三种途径:直接取特征、经 FeatureService、经 PushSource 驱动的 FeatureView)、store.push模拟流式推数,最后执行feast teardown清理资源。这正是 Feast 从注册到离在线检索的完整生命周期。
二、模板机制:feast init是如何生成项目的
local模板之所以能直接运行,背后依赖 Feast CLI 的模板机制,相关实现见 sdk/python/feast/cli/cli.py 与 sdk/python/feast/repo_operations.py:
feast init [目录名] -t local会先校验项目名(只能由字母、数字、下划线、连字符组成,且不能以下划线或连字符开头,见is_valid_name);- 随后把
sdk/python/feast/templates/local/整目录拷贝到目标路径; - 如果模板目录里有
bootstrap.py(sdk/python/feast/templates/local/bootstrap.py),CLI 会动态导入并执行其中的bootstrap()函数,然后用完即删; - 最后将
feature_store.yaml里的project: my_project替换为用户传入的项目名。
bootstrap.py完成了三件关键的事(对应实现见 bootstrap.py):
- 调用
create_driver_hourly_stats_df(定义于 sdk/python/feast/driver_test_data.py)为司机 1001~1005 生成最近 15 天、按小时采样的模拟特征数据,并写成data/driver_stats.parquet; - 用 pyarrow 显式 schema 生成一个空的
data/driver_quality_labels.parquet,作为 Label View 的批数据源; - 把
feature_definitions.py中的%PROJECT_NAME%、%PARQUET_PATH%、%LOGGING_PATH%、%LABEL_DATA_PATH%四个占位符替换为真实路径。
其他可用模板可通过feast init --help查看,模板名与sdk/python/feast/templates/下的目录一一对应(gcp、aws、snowflake、spark、postgres、minimal等)。
三、配置文件解析:feature_store.yaml
local 模板生成的 feature_store.yaml 是理解 Feast 配置的起点:
project: my_project # 默认 Registry 是文件,可换成更可扩展的 SQL 后端 registry: data/registry.db # provider 主要指定默认的离线/在线存储,以及在给定云上存储 Registry 的方式 provider: local online_store: type: sqlite path: data/online_store.db entity_key_serialization_version: 3 # 默认 no_auth(不做认证授权),可选 kubernetes、oidc,详见文档 auth: type: no_auth各配置项含义如下:
project:Feature Store 的命名空间,模板中以Project对象在代码中对应(见下文 feature_definitions.py 第 25 行)。所有 FeatureView、FeatureService 都归属于某个 project;registry:特征注册表(元数据存储)。local 模板使用本地 SQLite 文件data/registry.db,仅适合本地开发;生产环境应换成 S3/GCS 上的远程文件或 SQL 数据库,详见 Registry 概念;provider:local表示使用本地离线存储(文件)与本地在线存储(SQLite)。切换 provider 即切换整套默认存储,例如feast init -t gcp会默认接入 BigQuery;online_store:在线存储类型为sqlite,路径为data/online_store.db,用于支撑毫秒级的在线特征检索;entity_key_serialization_version:实体键序列化版本,3是当前推荐版本(仓库内还提供 v2 到 v3 的重序列化指南,见 entity-reserialization-of-from-v2-to-v3.md);auth:默认no_auth,生产可切换为kubernetes或oidc,参见 认证文档。
四、特征定义逐行解析:feature_definitions.py
模板的核心是 feature_definitions.py,它演示了 Feast 特征建模的几乎所有核心抽象。
4.1 Project 与 Entity
project = Project(name="%PROJECT_NAME%", description="A project for driver statistics") driver = Entity(name="driver", join_keys=["driver_id"])Project声明了该仓库所属的命名空间;Entity定义了特征的主体——司机(driver),其join_keys=["driver_id"]相当于查询特征时使用的"主键"。
4.2 数据源 FileSource
driver_stats_source = FileSource( name="driver_hourly_stats_source", path="data/driver_stats.parquet", timestamp_field="event_timestamp", created_timestamp_column="created", )parquet 文件是本地开发最便捷的数据源;生产环境可换成 BigQuery、Snowflake、Redshift 等数仓。timestamp_field用于 point-in-time join,created_timestamp_column用于去重。
4.3 FeatureView:特征的核心容器
driver_stats_fv = FeatureView( name="driver_hourly_stats", entities=[driver], ttl=timedelta(days=1), schema=[ Field(name="conv_rate", dtype=Float32), Field(name="acc_rate", dtype=Float32), Field(name="avg_daily_trips", dtype=Int64, description="Average daily trips"), Field(name="driver_metadata", dtype=Map, description="Driver metadata as key-value pairs"), Field(name="driver_config", dtype=Json, description="Driver configuration as JSON"), Field(name="driver_profile", dtype=Struct({"name": String, "age": String}), description="Driver profile as a typed struct"), ], online=True, source=driver_stats_source, tags={"team": "driver_performance"}, enable_validation=True, version="latest", )模板刻意展示了 Feast 的类型系统(对应 docs/reference/type-system.md 与 feast/types):
Float32/Int64:基础数值类型;Map:键值对类型,driver_metadata用它承载{"vehicle_type": "truck", "rating": "5.0"}这类数据;Json:任意 JSON 文档;Struct:结构化嵌套类型,driver_profile声明了{"name": String, "age": String}的字段结构。
schema既用于特征物化入库,也用于检索时构建训练数据集或在线服务特征,是"离线训练/在线服务同构"的关键。ttl控制特征在在线存储中的过期时间;online=True表示该特征需物化到在线存储。
4.4 RequestSource 与 On-demand 变换
input_request = RequestSource( name="vals_to_add", schema=[Field(name="val_to_add", dtype=Int64), Field(name="val_to_add_2", dtype=Int64)], ) @on_demand_feature_view( sources=[driver_stats_fv, input_request], schema=[ Field(name="conv_rate_plus_val1", dtype=Float64), Field(name="conv_rate_plus_val2", dtype=Float64), ], ) def transformed_conv_rate(inputs: pd.DataFrame) -> pd.DataFrame: df = pd.DataFrame() df["conv_rate_plus_val1"] = inputs["conv_rate"] + inputs["val_to_add"] df["conv_rate_plus_val2"] = inputs["conv_rate"] + inputs["val_to_add_2"] return dfRequestSource描述请求时刻才有的特征(如 HTTP 请求携带的实时数值);@on_demand_feature_view定义"按需变换":把已有 FeatureView 特征与请求参数组合计算出新特征,属于对 ADR-0003-on-demand-transformations 的落地示范。
4.5 FeatureService:面向模型版本的特征分组
driver_activity_v1 = FeatureService( name="driver_activity_v1", features=[ driver_stats_fv[["conv_rate"]], # 子选择某个特征 transformed_conv_rate, # 选择全部特征 ], logging_config=LoggingConfig(destination=FileLoggingDestination(path="data/")), ) driver_activity_v2 = FeatureService( name="driver_activity_v2", features=[driver_stats_fv, transformed_conv_rate] )FeatureService把特征打包成"模型版本"粒度,支持特征子选择(如driver_stats_fv[["conv_rate"]]),并可为服务挂载特征日志(写入FileLoggingDestination)。
4.6 PushSource:流式新鲜特征
driver_stats_push_source = PushSource( name="driver_stats_push_source", batch_source=driver_stats_source, ) driver_stats_fresh_fv = FeatureView( name="driver_hourly_stats_fresh", entities=[driver], ttl=timedelta(days=1), schema=[...], online=True, source=driver_stats_push_source, # 与上面的 FV 区别仅在此 tags={"team": "driver_performance"}, version="latest", )PushSource在批数据源之上叠加"推入通道":流数据可直接store.push()到在线存储,让特征秒级变新。注意driver_stats_fresh_fv与driver_stats_fv的 schema、TTL 完全一致,唯一区别是 source 换成了 PushSource——这说明了流式特征与批特征建模的统一方式。对应流式变换的设计见 ADR-0005-stream-transformations。
4.7 LabelView:可变的训练标签
driver_quality_labels_source = PushSource( name="driver_quality_labels_push", batch_source=FileSource( name="driver_quality_labels_batch", path="data/driver_quality_labels.parquet", timestamp_field="event_timestamp", ), ) driver_quality_labels = LabelView( name="driver_quality_labels", entities=[driver], schema=[ Field(name="is_reliable", dtype=Int64), Field(name="quality_score", dtype=Float32), Field(name="reviewer_notes", dtype=String), Field(name="labeler", dtype=String), ], source=driver_quality_labels_source, labeler_field="labeler", conflict_policy=ConflictPolicy.LAST_WRITE_WINS, description="Human quality labels for drivers - used for model training and evaluation", tags={ "feast.io/labeling-method": "table", "feast.io/field-role:is_reliable": "label", "feast.io/label-values:is_reliable": "1 0", "feast.io/label-widget:is_reliable": "binary", "feast.io/label-widget:quality_score": "number", "feast.io/label-widget:reviewer_notes": "text", }, )LabelView用于管理训练数据、RLHF、评估所需的可变人类标签,标签数据通过 PushSource 从 UI 或外部工具提交;conflict_policy=ConflictPolicy.LAST_WRITE_WINS定义并发写入的冲突策略(后写覆盖);labeler_field标记标注者字段;tags用feast.io/field-role:*、feast.io/label-widget:*等约定描述字段角色与 UI 渲染方式。这是 ADR-0012-label-view 的工程落地。
五、端到端工作流:test_workflow.py 全流程拆解
test_workflow.py 是理解 Feast 常用 API 的最佳样例,python test_workflow.py一次性演示了六大环节:
5.1 注册:feast apply
store = FeatureStore(repo_path=".") subprocess.run(["feast", "apply"])apply把 feature_definitions.py 中的定义注册到 Registry,是后续一切操作的前提。
5.2 离线特征:训练与批量打分
entity_df = pd.DataFrame.from_dict({ "driver_id": [1001, 1002, 1003], # 实体 join key "event_timestamp": [datetime(2021, 4, 12, 10, 59, 42), ...], # 保留关键字 "label_driver_reported_satisfaction": [1, 5, 3], # 可选标签,Feast 不处理 "val_to_add": [1, 2, 3], # 供 on-demand 变换使用 "val_to_add_2": [10, 20, 30], }) training_df = store.get_historical_features( entity_df=entity_df, features=[ "driver_hourly_stats:conv_rate", "driver_hourly_stats:acc_rate", "driver_hourly_stats:avg_daily_trips", "transformed_conv_rate:conv_rate_plus_val1", "transformed_conv_rate:conv_rate_plus_val2", ], ).to_df()get_historical_features基于 entity_df 执行point-in-time join(原理见 docs/getting-started/concepts/point-in-time-joins.md),返回可用于训练或批量打分的 DataFrame。批量打分模式只需把event_timestamp统一设为当前时间(pd.to_datetime("now", utc=True)),即取每个实体最新时刻的特征。
5.3 物化:materialize_incremental
store.materialize_incremental(end_date=datetime.now())把离线数据源中的特征按 TTL 窗口增量写入在线存储,支撑低延迟检索。定时物化(如借助 Airflow)正是生产环境的标准做法,参见 数据接入概念。
5.4 在线特征检索:三种途径
# 途径一:直接列出特征引用 returned_features = store.get_online_features( features=[ "driver_hourly_stats:acc_rate", "driver_hourly_stats:driver_metadata", "driver_hourly_stats:driver_config", "driver_hourly_stats:driver_profile", "transformed_conv_rate:conv_rate_plus_val1", "transformed_conv_rate:conv_rate_plus_val2", ], entity_rows=[{"driver_id": 1001, "val_to_add": 1000, "val_to_add_2": 2000}, ...], ).to_dict() # 途径二:经 FeatureService features_to_fetch = store.get_feature_service("driver_activity_v1") # 途径三:经 PushSource 驱动的新鲜特征服务 features_to_fetch = store.get_feature_service("driver_activity_v3")其中 entity_rows 需同时携带 on-demand 变换所需的val_to_add请求字段;途径三取的是以driver_hourly_stats_fresh(PushSource 版)为基础的driver_activity_v3服务。
5.5 流式推数:store.push
event_df = pd.DataFrame.from_dict({ "driver_id": [1001], "event_timestamp": [datetime.now()], "created": [datetime.now()], "conv_rate": [1.0], "acc_rate": [1.0], "avg_daily_trips": [1000], "driver_metadata": [{"vehicle_type": "truck", "rating": "5.0"}], "driver_config": [json.dumps({"max_distance_km": 500, "preferred_zones": ["north"]})], "driver_profile": [{"name": "driver_1001_updated", "age": "30"}], }) store.push("driver_stats_push_source", event_df, to=PushMode.ONLINE_AND_OFFLINE)store.push模拟一次流事件:to=PushMode.ONLINE_AND_OFFLINE表示同时写入在线存储与离线存储(其他模式见feast.data_source.PushMode)。推数后再次查询driver_activity_v3,即可看到 driver 1001 的conv_rate已更新为 1.0——这就是"流式特征实时可用"的直观演示。随后feast teardown清理 Registry 与存储资源。
六、从本地走向生产:升级路线
local 模板的目标是本地快速验证,生产部署需要按以下顺序演进(详见 Running Feast in production):
- 更换模板/离线存储:
local使用文件离线存储,不可扩展。改用可扩展离线存储的模板,例如feast init -t gcp、feast init -t aws、feast init -t snowflake;具体选项运行feast init --help查看(当前仓库 templates 目录还提供spark、postgres、athena等模板,见 sdk/python/feast/templates/)。推荐用 BigQuery、Snowflake、Redshift 等数仓,Spark 目前为实验性支持; - Registry 上云/上库:
feature_store.yaml默认指向本地文件data/registry.db,生产应改用远程文件(S3/GCS)或 SQL Registry,参见 Registry 概念 及 SQL Registry 参考; - 搭建 CI/CD 与多环境:按 dev / staging / prod 分离环境,在特征定义变更时自动更新 Registry;
- (可选)定时物化:通过 Airflow 等调度器定期执行批量物化,保证在线特征低延迟可用(见 数据接入 中的批式物化);
- (可选)部署特征服务:
feast serve启动 Python 特征服务暴露 HTTP 接口供在线检索,详见 Python Feature Server;业务方也可以直接在代码中调用 Feast 客户端取特征(特征检索)。
七、小结
local模板虽然体量小,却完整覆盖了 Feast 的核心心智模型:Entity 定义主体、FeatureView 组织特征、FeatureService 面向模型版本、PushSource/LabelView 支撑流式与标签场景,外加apply → get_historical_features → materialize → get_online_features → push → teardown的全链路 API。先跑通python test_workflow.py,再按上文路线逐级替换离线存储、Registry 与部署形态,即可平滑地从本地原型演进为生产级 Feature Store。
【免费下载链接】feastThe Open Source Feature Store for AI/ML项目地址: https://gitcode.com/GitHub_Trending/fe/feast
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考