Feast on RHOAI 快速上手:在 Red Hat OpenShift AI 上端到端跑通本地与远程拓扑的 Feature Store
2026/9/17 5:17:40 网站建设 项目流程

Feast on RHOAI 快速上手:在 Red Hat OpenShift AI 上端到端跑通本地与远程拓扑的 Feature Store

【免费下载链接】feastThe Open Source Feature Store for AI/ML项目地址: https://gitcode.com/GitHub_Trending/fe/feast

本篇技术指南基于仓库中的 RHOAI 快速入门示例(examples/rhoai-quickstart/README.md 与配套笔记本 feast-demo-quickstart.ipynb),演示如何把 Feast 作为特征存储运行在 Red Hat OpenShift AI(RHOAI)平台上。读完本文,你将能够:在 RHOAI 沙箱中创建 Workbench 并运行 Feast 示例笔记本;用 Driver 样例数据完成特征仓库初始化、历史特征生成、批量推理、在线物化与在线特征读取;并进一步搭建 remote online server 与 remote registry server 两种客户端-服务端拓扑,理解各端口、配置项与 Feast SDK 的实际行为。

环境准备:两种运行方式与适用前提

README 给出的前提是:示例以 Jupyter Notebook 展示 Feast 能力,你需要一个可以执行 Jupyter 的环境,二选一:

  1. 在本地机器运行 Jupyter:适合希望脱离平台独立验证的读者,按 Jupyter 官方文档安装即可。
  2. 在 RHOAI 平台运行 Jupyter:本快速入门的目标场景,直接在 RHOAI 的 Workbench(Notebook 环境)里执行示例。

如果还没有 RHOAI 集群,README 建议使用 Red Hat OpenShift AI 的开发者沙箱(developer sandbox)来体验本示例,并建议新用户在动手前先用官方渠道的入门视频和产品文档(Red Hat OpenShift AI 文档中的 Data Science Projects 章节)熟悉平台。

在 RHOAI 沙箱上创建 Workbench

README 给出的操作步骤:

  1. 进入你的项目(namespace)下的Dev页面;
  2. 打开Workbenches标签页;
  3. 点击Create Workbench,填写所需信息;
  4. 点击Create

Workbench 就绪后,README 给出两种方式把示例导入并运行:

  • 克隆仓库:把 Feast 仓库克隆进 Workbench,然后运行examples/rhoai-quickstart/下的笔记本;
  • 上传文件:把必要文件上传到已有 Workbench,直接执行笔记本。

笔记本整体流程:围绕 Driver 实体的三段式演示

feast-demo-quickstart.ipynb 以Driver(司机)实体为载体演示 Feast 的核心能力。README 将笔记本归纳为三部分:

  • 设置 Feast 仓库:加载样例 driver 数据、生成训练数据集、执行离线推理(批量打分)、把批量特征摄入在线存储,并学习如何用FeatureViewFeatureService拉取推理特征;
  • 配置 Remote Online 拓扑:启动 remote online server 与客户端,用 remote online client 实时取特征;
  • 配置 Remote Registry 拓扑:启动 remote registry server 与客户端,用 remote registry client 读取 Feast 元数据(FeatureView、FeatureService 等)。

README 同时说明:该笔记本也可以在独立环境(standalone)中完整执行,并不强依赖 RHOAI。

第 1 步:安装 Feast 与版本一致性要求

笔记本的第一个代码单元:

# WE MUST ENSURE PYTHON CONSISTENCY BETWEEN NOTEBOOK AND FEAST SERVERS # LAUNCH THIS NOTEBOOK FROM A CLEAN PYTHON ENVIRONMENT >3.9 %pip install -q feast==0.40.1 # grpcio is needed as a later section to run the feast registry server. %pip install -q grpcio

两个要点值得注意:

  • Python 版本与一致性:注释明确要求从干净的 Python 环境(>3.9)启动笔记本,且笔记本端与 Feast 服务端之间的 Python 版本必须保持一致。这是远程拓扑能正常工作的隐含前提;
  • grpcio 依赖:笔记本后段要用到feast serve_registry启动 gRPC 协议的 Registry 服务,因此提前安装grpcio

示例固定的 Feast 版本为 0.40.1,且从输出可见 RHOAI Workbench 的默认内核为 Python 3.9.16,路径位于容器内/opt/app-root/(如/opt/app-root/src/feast)。如果你在本机复现,需把笔记本中出现的绝对路径替换为自己环境下的实际路径。

第 2 步:feast init创建项目并解读 feature_store.yaml

!rm -rf my_feast_project !feast init my_feast_project %cd my_feast_project/feature_repo

feast init会打印Creating a new Feast repository in .../my_feast_project。从源码看,init命令定义在 cli.py,支持--minimal--template选项(模板可选localawsgcpsnowflakepostgres等,默认local),实际建库逻辑在 repo_operations.py 的init_repo。本示例未传模板参数,即使用默认 local 模板,生成带示例数据的完整 feature store 骨架。

生成的仓库结构(笔记本中的find输出):

. +-- data +-- |-- driver_stats.parquet +-- __init__.py +-- feature_store.yaml +-- example_repo.py +-- test_workflow.py

各文件职责(README/笔记本的说明):

  • data/:存放本示例的 parquet 样例数据;
  • example_repo.py:创建 Feast 对象(FeatureView、FeatureService、OnDemandFeatureView)的代码;
  • feature_store.yaml:Feast 的全部仓库级配置;
  • test_workflow.py:演示 Feast 关键命令(定义、读取、推送特征)的 Python 代码。

feature_store.yaml实际内容:

project: my_feast_project # By default, the registry is a file (but can be turned into a more scalable SQL-backed registry) registry: data/registry.db # The provider primarily specifies default offline / online stores & storing the registry in a given cloud provider: local online_store: type: sqlite path: data/online_store.db entity_key_serialization_version: 2

参数解读:

配置项示例值含义
projectmy_feast_project项目名,用于命名空间隔离,在线存储的表名会带此前缀(后文可见my_feast_project_driver_hourly_stats
registrydata/registry.db注册表默认是文件型(SQLite);可换成 SQL 支持的注册表以获得更好的可扩展性
providerlocalprovider 主要决定默认的离线/在线存储与注册表落盘位置,参见 local provider 文档
online_store.typesqlite本地在线存储,见 sqlite online store 参考
entity_key_serialization_version2实体键序列化版本,决定在线存储中实体键的编码方式

feature_store.yaml的完整字段说明可参考 feature-store-yaml 参考文档。

第 3 步:样例数据与 Feast 对象定义

driver_stats.parquet:1807 行的司机小时级统计

data/driver_stats.parquetfeast init生成,是本示例的“离线数据源”。用 pandas 读取后可见 1807 行 × 6 列,schema 如下:

类型说明
event_timestampdatetime64[ns, UTC]事件时间(司机统计的小时时间戳)
driver_idint64实体 join key(1001–1005 等司机 ID)
conv_ratefloat32接单转化率
acc_ratefloat32事故率
avg_daily_tripsint32日均行程数
createddatetime行写入时间(created timestamp 列)

FileSource 定义

笔记本展示该数据源在特征定义文件中的写法(路径为 RHOAI 容器内的绝对路径,本机复现时请替换):

driver_stats_source = FileSource( name="driver_hourly_stats_source", path="/opt/app-root/src/feast/examples/rhoai-quickstart/my_feast_project/feature_repo/data/driver_stats.parquet", timestamp_field="event_timestamp", created_timestamp_column="created", )

timestamp_fieldcreated_timestamp_column分别指定事件时间与物化时间列,是点-in-time join 与增量物化(后文materialize-incremental)的依据。

feast apply:把代码定义落成注册表对象

在存在feature_store.yaml的目录下执行feast apply。注意笔记本先执行了rm -rf .ipynb_checkpoints/——Jupyter 的 checkpoints 目录会干扰feast apply的仓库扫描。

feast apply的实际输出创建了:

  • 实体driver
  • 批量特征视图driver_hourly_statsdriver_hourly_stats_fresh(后者同时挂了 push source,支持在线推送);
  • 按需特征视图(OnDemand FeatureView)transformed_conv_ratetransformed_conv_rate_fresh(对conv_rate做加值变换,输出conv_rate_plus_val1/conv_rate_plus_val2);
  • 特征服务driver_activity_v2driver_activity_v1driver_activity_v3
  • SQLite 在线表my_feast_project_driver_hourly_statsmy_feast_project_driver_hourly_stats_fresh

输出中还有一条提示:On demand feature view is an experimental feature... the functionality does not scale well for offline retrieval,即按需特征视图目前是实验性能力,离线批量读取场景的扩展性有限。

第 4 步:生成训练数据集(get_historical_features)

训练数据需要“实体 + 时间戳 + 标签”。笔记本先构造 entity dataframe:

entity_df = pd.DataFrame.from_dict( { # entity's join key -> entity values "driver_id": [1001, 1002, 1003], # "event_timestamp" (reserved key) -> timestamps "event_timestamp": [ datetime(2021, 4, 12, 10, 59, 42), datetime(2021, 4, 12, 8, 12, 10), datetime(2021, 4, 12, 16, 40, 26), ], # (optional) label name -> label values. Feast does not process these "label_driver_reported_satisfaction": [1, 5, 3], # values we're using for an on-demand transformation "val_to_add": [1, 2, 3], "val_to_add_2": [10, 20, 30], } )

要点:event_timestamp是保留键;label_*列原样透传,Feast 不处理;val_to_add/val_to_add_2是喂给 OnDemand 变换的输入列。然后:

store = FeatureStore(repo_path=".") 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()

返回的training_df共 10 列:3 个司机 ID 行,包含原始特征(conv_rate/acc_rate/avg_daily_trips,float32/int32)、按需变换特征(conv_rate_plus_val1/conv_rate_plus_val2,float64)以及透传的 label 列。注意按需特征的值等于conv_rate + val_to_add(如 0.058821 + 1 = 1.058821),验证了变换逻辑生效。README 强调带时间戳的原因:我们希望同一司机在不同时间点的特征分别入模(点-in-time join)。

第 5 步:离线推理(批量打分)

批量打分的做法与训练数据生成相同,只是把event_timestamp换成当前时间:

entity_df["event_timestamp"] = pd.to_datetime("now", utc=True) 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()

输出显示三行司机在“now”时刻拿到的特征向量(如 driver 1002 的conv_rate_plus_val1 = 2.311688),说明历史特征接口在推理场景下的用法。

第 6 步:把批量特征物化进在线存储

!feast materialize-incremental $(date -u +"%Y-%m-%dT%H:%M:%S")

该命令从离线存储取特征写入在线存储。实际输出:

Materializing 2 feature views to 2024-09-24 18:02:06+00:00 into the sqlite online store. driver_hourly_stats from 2024-09-23 18:02:09+00:00 to 2024-09-24 18:02:06+00:00: 100%|████████...| 5/5 driver_hourly_stats_fresh from 2024-09-23 18:02:09+00:00 to 2024-09-24 18:02:06+00:00: 100%|████████...| 5/5

解读:物化窗口为“上次物化时间(2024-09-23 18:02)→ 传入的截止时间(2024-09-24 18:02)”,即增量模式;apply时记录的materialization_intervals决定了起点,这也解释了为什么driver_hourly_stats输出里先打印了一个 0/5 的进度条——首次物化需要分片扫描离线 parquet。OnDemand 特征视图不参与物化(feast apply时也未为它们建表)。

第 7 步:在线特征读取与 FeatureService

直接按特征字符串读取

feature_vector = store.get_online_features( features=[ "driver_hourly_stats:conv_rate", "driver_hourly_stats:acc_rate", "driver_hourly_stats:avg_daily_trips", ], entity_rows=[ # {join_key: entity_value} {"driver_id": 1004}, {"driver_id": 1005}, ], ).to_dict()

输出:

{'acc_rate': [0.49898454546928406, 0.2943153381347656], 'avg_daily_trips': [178, 74], 'conv_rate': [0.19129787385463715, 0.5790505409240723], 'driver_id': [1004, 1005]}

entity_rows{join_key: entity_value}字典列表,每个字典对应一个实体的“最新值”读取。

用 FeatureService 解耦消费端

FeatureService用于把“特征视图定义”和“应用需要的特征集合”解耦。笔记本额外定义并注册了driver_activity_v4(指向driver_stats_fresh_fv):

driver_activity_v4 = FeatureService( name="driver_activity_v4", features=[example_repo.driver_stats_fresh_fv], ) feature_store = FeatureStore(".") feature_store.apply([driver_activity_v4])

随后可以用 FeatureService 对象(而不仅是字符串列表)调用同一套 API:

feature_vector = feature_store.get_online_features( features=driver_activity_v4, entity_rows=[{"driver_id": 1004}, {"driver_id": 1005}], ).to_dict()

结果与直接按字符串读取完全一致,证明两种引用方式等价。

第 8 步:Remote Online 拓扑——feast serve 与 remote 客户端

启动在线服务

在 feature_repo 目录下后台启动在线服务:

import subprocess # Run feast serve in the background feast_online_server_process = subprocess.Popen(["feast", "serve"])

输出显示 gunicorn(uvicorn worker)在http://127.0.0.1:6566上监听。随后用ps -ef | grep 'feast serve'确认进程存在。

默认端口6566并非魔法数字:serve命令在 serve.py 中定义,--port默认值即 6566,--host默认127.0.0.1。该命令还有若干对生产环境有价值的参数,默认值如下(摘自同一源码):

参数默认值说明
--host127.0.0.1监听地址
--port6566监听端口
--typehttp服务类型,httpgrpc
--workers1worker 进程数,-1表示按 CPU 核数自动计算(2*cores+1
--worker-connections1000每个 worker 的最大并发连接
--max-requests1000worker 处理多少请求后重启(防内存泄漏)
--max-requests-jitter50防 worker 同时重启的抖动量
--keep-alive-timeout30keep-alive 连接超时(秒)
--registry_ttl_sec60注册表元数据刷新周期(秒)
--cert/--key两者必须成对提供,以启动 TLS 模式(见 TLS 启动指南)
--metrics关闭启用 Metrics Server

完整服务参考见 feature servers 文档。

构造 remote online 客户端配置

笔记本在remote-online/目录动态生成feature_store.yaml

project: my_feast_project registry: './../my_feast_project/feature_repo/data/registry.db' # 复用本地注册表,简化演示 provider: local online_store: type: remote path: http://127.0.0.1:6566 entity_key_serialization_version: 3

两个关键细节:

  • online_store.type: remote时,path就是在线服务地址。从源码看,RemoteOnlineStoreConfig 的path默认值正是http://localhost:6566,且还提供cert(TLS 模式下的客户端证书路径)、connection_pool_size(默认 50)、connection_idle_timeout(默认 300 秒)、connection_retries(默认 3 次,指数退避)等连接池参数——笔记本示例未显式配置,全部走默认值;
  • entity_key_serialization_version从主仓库的2变成3:客户端与服务端序列化版本必须对齐,否则实体键无法正确解码。这是 remote 拓扑最容易被忽略的坑。

初始化并读取

online_feature_store_client = FeatureStore('.') online_feature_store_client.apply([]) online_features_stores_client = online_feature_store_client.get_online_features( features=driver_activity_v4, entity_rows=[{"driver_id": 1004}, {"driver_id": 1005}], ).to_dict()

输出与本地直读完全一致,同时在线服务日志打印POST /get-online-features HTTP/1.1 200——证明请求确实经由 6566 端口的 HTTP 接口完成,而不是走了本地 SQLite。

笔记本的说明指出:为了演示简洁,此处客户端仍指向my_feast_project的本地注册表,而生产环境可以进一步把 registry、online、offline 三类服务全部远程化,用 feature store client 统一访问(相关拓扑见 生产部署拓扑指南)。

第 9 步:Remote Registry 拓扑——feast serve_registry 与元数据读取

Registry 保存 FeatureService、FeatureView 等全部 Feast 元数据。除直接读本地注册表外,还可以以客户端-服务端模式访问:启动 registry server,客户端通过 remote registry 读取。默认端口为6570

启动 registry server

先切回my_feast_project/feature_repo目录(registry server 需要在含feature_store.yaml的仓库目录下启动),然后:

import subprocess feast_remote_registry_server_process = subprocess.Popen(["feast", "serve_registry"]) # 用 ps -ef | grep 'feast serve_registry' 确认进程

serve_registry命令定义见 serve.py:--port默认DEFAULT_REGISTRY_SERVER_PORT(即 constants.py 中的6570),--grpc/--no-grpc默认开启 gRPC(本例使用的就是 gRPC 通道,这也是第 1 步要装grpcio的原因),--rest-api可额外启用 REST 注册表服务,--cert/--key同样支持 TLS。

构造 remote registry 客户端配置

remote-registry/目录生成feature_store.yaml

project: my_feast_project registry: registry_type: remote path: localhost:6570 provider: local online_store: type: remote path: http://127.0.0.1:6566 entity_key_serialization_version: 3

注意 registry 的pathhost:port形式(gRPC 地址),与 online 的http://...URL 形式不同。

通过远程注册表读取元数据

registry_feature_store_client = FeatureStore('.') registry_feature_store_client.apply([]) # Listing all feature views using remote registry client registry_feature_store_client.list_all_feature_views(allow_cache=False) # Listing all feature services using remote registry client registry_feature_store_client.list_feature_services()

list_all_feature_views返回的元数据对象信息量很大,可核对第 3 步feast apply的产物:driver_hourly_stats(entities=['driver']、ttl=1 天、batch_source 为 parquet FileSource、features 为conv_rate-Float32/acc_rate-Float32/avg_daily_trips-Int64materialization_intervals记录了物化窗口);driver_hourly_stats_fresh(额外带 PUSH_SOURCE 流数据源);两个 OnDemand 特征视图(mode = pandas,source 指向对应 feature view 投影与RequestSource)。list_feature_services则返回driver_activity_v1/v2/v3/v4及其feature_view_projections组成。

收尾:停止服务并核对进程

笔记本最后把两个后台进程终止:

feast_online_server_process.terminate() # Stop the remote Feast online server feast_remote_registry_server_process.terminate() # stops the remote registry server

随后ps -ef | grep 'feast serve'应只剩 grep 自身。从输出日志看,feast serve收到 SIGTERM 后 gunicorn 会走Handling signal: term → Shutting down → Application shutdown complete的优雅关闭流程(其中Error while closing socket [Errno 9] Bad file descriptor是容器内常见噪声,可忽略)。

小结与延伸阅读

本示例完整覆盖了 Feast “本地仓库 → 批量摄取 → 在线服务 → 客户端-服务端拓扑” 的闭环,且每个环节都给出了可直接复制的配置与 API 调用。示例目录只有两个文件(README 与 笔记本),其余所有能力(FileSource、FeatureView/OnDemandFeatureView、FeatureService、get_historical_features/get_online_features、materialize-incremental、feast serve/serve_registry)都来自当前仓库的 Python SDK(sdk/python/feast)。若继续深入,建议按以下路径:

  • 概念:实体、点-in-time join、特征拉取、feature_store.yaml 全字段参考;
  • 服务与部署:feature servers 参考、远程在线存储、生产部署拓扑、TLS 模式启动服务器;
  • 同类示例:仓库 examples 目录下的 quickstart、operator-quickstart 等,可对照本例的 local 拓扑理解 Kubernetes Operator 方式部署的差异。

【免费下载链接】feastThe Open Source Feature Store for AI/ML项目地址: https://gitcode.com/GitHub_Trending/fe/feast

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询