生产级AI/ML特征存储平台:Feast全面使用指南 — Architecture
Feast(Feature Store)是一个开源特征存储平台,通过帮助团队定义、管理、验证和提供生产级AI/ML特征,助力大规模生产机器学习系统的运营。
文中内容仅限技术学习与代码实践参考,市场存在不确定性,技术分析需谨慎验证,不构成任何投资建议。
概述
Feast的架构设计注重灵活性和可扩展性,由多个协同工作的组件构成,可提供用于训练和推理的特征存储服务。
-
Feast采用推送模型从不同数据源接收数据,并将特征值存储在在线存储(Online Store)中。这种机制使Feast能够以低延迟实时提供特征服务。
-
Feast支持特征转换功能(当前支持按需转换和流式数据源,未来将支持批处理转换)。对于流式(Streaming)和批处理(Batch)数据源,Feast需要独立的特征转换引擎(批处理场景下通常使用离线存储作为转换引擎)。我们正在探索为Feast添加默认的流式处理引擎。
-
在将数据源与Feast集成时,建议结合领域专业知识,充分理解不同写入模式对应用程序的权衡影响。
-
我们推荐使用Python作为特征存储微服务的开发语言。如文档所述,预计算特征是确保低延迟服务的最佳实践。将特征服务简化为轻量级数据库查询是理想模式,这意味着Python的边际性能开销应可接受。基于此,我们认为Python的优势(避免特征逻辑重复实现)大于其成本。同时我们也提供Java和Go客户端用于在线特征检索。
-
基于角色的访问控制(RBAC)是一种根据组织内用户角色限制资源访问的安全机制。在Feast场景中,RBAC确保只有授权用户/组能访问或修改特定资源,从而维护数据安全和操作完整性。
Python
使用 Python 提供特征服务。
为什么应该使用 Python 进行机器学习特征服务?
Python 已成为机器学习领域的主要语言,这一优势同样适用于特征服务领域。Feast 推荐使用 Python 编写的微服务主要基于以下五个原因:
1. Python 是机器学习的首选语言
应选择用户最熟悉的工具。Python 在机器学习社区的主导地位毋庸置疑。其简洁性和可读性使其成为编写和理解复杂算法的理想语言。Python 拥有丰富的库生态系统,如 TensorFlow、PyTorch、XGBoost 和 scikit-learn,这些库为机器学习模型的开发和部署提供了强大支持。Feast 也致力于融入这一生态系统。
2. 预计算是必经之路
预计算特征是确保低延迟性能的推荐方案。将特征服务简化为轻量级数据库查询是最佳实践模式,这意味着 Python 带来的边际开销是可接受的。预计算还能确保下游服务的产品体验保持高效。缓慢的用户体验等于糟糕的用户体验。请尽可能多地预计算和持久化数据。
3. 使用其他语言提供特征会导致偏差
确保模型训练(离线服务)和在线预测(在线服务)使用的特征在生产环境中可用至关重要。特征开发初期通常使用 Python 编写,这得益于 Python 数据操作库的便利性和高效性。然而在生产环境中,出于性能考虑,常常会有改用 Java、Go 或 C++ 等其他语言重写这些特征的需求。这种重实现会带来重大风险:训练与服务偏差(training and serving skew)。请注意,虽然总存在少数特例(例如 Time Since Last Event 类型的特征),但这不应成为常态。
当模型训练使用的特征与预测阶段使用的特征存在差异时,就会产生训练与服务偏差。这会导致模型性能下降、预测结果不可靠,以及新特征和新模型的发布速度降低。用其他语言重写特征的过程容易产生错误和不一致,从而加剧这一问题。
4. 重复实现代价过高
用其他语言重写特征不仅存在风险,而且资源消耗巨大。需要工程师投入大量时间和精力确保特征的正确转换。这个过程可能引入错误和不一致,进一步增加训练与服务偏差的风险。此外,维护两个版本的特征代码库会增加不必要的复杂性和开销。更重要的是,这项工作的机会成本极高,需要双倍的资源投入。只有当性能提升值得投入时,才应考虑代码重实现。由于大部分特征应该被预计算,因此延迟性能优化不应成为团队工作的最高优先级。
5. 善用现有 Python 优化手段
与其切换编程语言,不如在保持 Python 作为主要语言的同时优化特征存储性能。优化过程分为两个步骤:
步骤 1:量化特征计算的延迟瓶颈
使用 CProfile 等工具定位代码中的延迟瓶颈。这有助于优先解决最大的效率问题。当我们最初在 Python 中实现原生转换时,代码性能分析 帮助我们发现 Pandas 的类型转换会导致 10 倍的开销。
步骤 2:优化特征计算
如前所述,预计算是推荐方案。某些情况下可能需要数据生产者到在线特征存储的完全同步写入,这时就需要极快的特征计算和写入速度。我们建议首先优化特征计算代码。
应通过库、工具和缓存进行代码优化。例如:判断能否通过 NumPy 的矢量化计算优化特征计算;探索 Numba 等加速工具;使用 lru_cache 等工具缓存频繁访问的数据。
最后,Feast 将持续优化 Python 的服务性能并提升整体基础设施效率,这将更好地服务社区。因此我们建议:重点优化特征相关代码,向维护者报告延迟瓶颈,并通过贡献代码帮助提升基础设施性能。
通过保持 Python 特征实现并优化性能,可以确保训练与服务的一致性,降低错误风险,并专注于为客户提供更多产品体验。
拥抱 Python 进行特征服务,充分利用其优势来构建稳健可靠的机器学习系统。
Push 模型 vs Pull 模型
Push 模型 vs Pull 模型
Feast 采用推送模型(Push Model),即数据生产者将数据推送至特征存储(Feature Store),并由 Feast 将特征值存储于在线存储(online store)中,从而实现实时特征服务。
在拉取模型(Pull Model)中,Feast 会在请求时从数据生产者处拉取数据,并在提供服务前将特征值存储至在线存储(实际上这种存储并无必要)。该方法会引入额外的网络延迟,因为 Feast 需要协调对每个数据生产者的请求,这意味着延迟至少等同于最慢的调用耗时。因此,为了实现最低延迟的特征服务,我们选择将数据推送至 Feast 并存储特征值至在线存储。
采用推送模型的权衡在于无法直接保证强一致性(strong consistency),而必须通过显式设计更新 Feast 的协调机制和客户端使用方式来实现强一致性。
这种方法的显著优势在于 Feast 针对低延迟特征检索进行了读取优化。
推送实现方式
推送模型隐式包含了关于_如何_及_何时_将特征值推送至在线存储的决策。
从开发者视角来看,有三种推送特征值至在线存储的方式,各自具有不同的权衡取舍(详见写入模式章节)。
向 Feast 写入数据
Feast 采用推送模型将特征推送到在线存储。
这带来两个重要影响:(1) 数据生产者(即客户端)与 Feast(即服务端)之间的通信模式;(2) 特征计算和特征值写入 Feast 在线存储的模式。
数据生产者(即生成数据的服务)将数据发送至 Feast,以便 Feast 将特征值写入在线存储。这些数据可以是需要 Feast 计算特征值的原始数据,也可以是预计算好的特征值。
通信模式
客户端(或数据生产者)有两种方式向在线存储发送数据:
-
同步方式
- 使用同步 API 调用处理少量实体或单个实体(例如使用
push
或write_to_online_store
方法),或通过 Feature Server 的push
端点)
- 使用同步 API 调用处理少量实体或单个实体(例如使用
-
异步方式
- 使用异步 API 调用处理少量实体或单个实体(例如使用
push
或write_to_online_store
方法),或通过 Feature Server 的push
端点) - 使用"批处理作业"处理大量实体(例如使用批量物化引擎)
- 使用异步 API 调用处理少量实体或单个实体(例如使用
注意:在某些场景中,开发者可能将一组实体"批量"处理并通过单次 API 调用写入在线存储。这是减少写入负载的常见模式,但我们不将其归类为批处理作业。
特征值写入模式
向在线存储(即服务端)写入特征值有两种方式:客户端预计算转换 或 服务端按需计算转换。
组合方法
在某些场景中,结合使用预计算和按需转换可能是最优方案。
选择特征值写入模式时,必须考虑应用程序的具体需求、可接受的数据正确性、延迟容忍度以及可用计算资源。审慎选择有助于提升服务性能和可靠性。
客户端可通过三种方式向在线存储写入特征值:
- 预计算转换
- 按需计算转换
- 混合模式(预计算 + 按需)
1. 预计算转换
预计算转换可在 Feast 外部完成(例如通过批处理作业或流式应用),也可通过 push
或 write-to-online-store
API 在写入在线存储时由 Feast 特征服务端完成。
2. 按需计算转换
按需转换只能在 Feast 内部进行,时机可以是:(1) 客户端请求时 或 (2) 数据生产者写入在线存储时。通过 transform_on_write
参数可控制是否在写入操作时应用转换,允许对预处理数据跳过转换,同时在 API 调用时仍启用转换。
3. 混合模式(预计算 + 按需)
混合模式允许在 Feast 内外进行预计算转换,并在客户端请求时执行按需转换。这对于"时间自上次"类型的特征(例如上次购买时间)特别适用。
权衡考量
在同步与异步数据写入之间抉择时,需考虑以下权衡因素:
- 数据一致性:异步写入允许数据生产者不等待写入完成即发送数据,可能导致在线存储中的数据过时。这在数据实时性要求不高的场景中可以接受。但对于关键操作(如金融应用中的贷款金额计算),陈旧数据可能导致错误决策,此时必须使用同步写入。
- 正确性:必须权衡数据过时风险与操作需求。例如在借贷应用中,特征数据的新鲜度对正确性至关重要(取决于具体特征和原始数据),此时应优先同步写入。在敏感性较低的场景中,异步写入提供的最终一致性可能已足够。
- 服务耦合度:同步写入会导致服务间更紧密的耦合。若写入操作失败,可能导致依赖服务操作失败,这对需要高可靠性和服务独立性的系统是重大缺陷。
- 应用延迟:异步写入通常能降低客户端感知的延迟,因为客户端无需等待写入完成。这在操作不依赖即时数据新鲜度的环境中可提升用户体验和效率。
下表根据具体应用需求和数据敏感性,指导选择最合适的数据写入和特征计算策略:
数据写入类型 | 特征计算方式 | 应用场景 | 推荐方案 |
---|---|---|---|
异步 | 按需计算 | 容忍数据延迟的数据密集型应用 | 采用异步写入配合按需计算,平衡负载并高效管理资源 |
异步 | 预计算 | 高吞吐非关键数据处理 | 使用异步批处理作业配合预计算转换,提升效率和扩展性 |
同步 | 按需计算 | 高风险决策场景 | 采用同步写入配合按需特征计算,确保数据新鲜度和正确性 |
同步 | 预计算 | 需要快速反馈的用户界面应用 | 使用同步写入配合预计算特征,降低延迟并提升用户体验 |
同步 | 混合(预计算+按需) | 需在约束条件下优化延迟的高风险决策 | 在可能时使用同步写入配合预计算特征,辅以选择性按需计算来降低延迟提升体验 |
特征转换
特征转换(Feature Transformation) 是指接收输入数据集并返回输出数据集的函数。特征转换可作用于原始数据或衍生数据。
特征转换引擎
特征转换可通过以下三类「转换引擎」执行:
- Feast Feature Server
- 离线存储(如 Snowflake、BigQuery、DuckDB、Spark 等)
- 计算引擎
这三类转换引擎需与写入时使用的通信模式配合使用。
需特别注意,这意味着不同的特征转换代码可能在不同的转换引擎下使用。因此,理解各类转换引擎/通信模式的适用场景及其权衡取舍,对实施方案的成功至关重要。
总体而言,我们建议根据数据生产者、特征/模型使用场景及整体产品的适配性,选择合适的转换引擎和网络调用方式。
特征服务与模型推理
注意: 该ML基础架构图展示了一种由客户端应用驱动的编排模式。这并不是唯一可行的方法,不同模式会产生不同的权衡取舍。
生产机器学习系统可选择以下四种方式提供机器学习预测(模型推理的输出):
- 在线模型推理配合在线特征
- 离线模式推理不依赖在线特征
- 在线模型推理配合在线特征和缓存预测
- 不依赖特征的在线模型推理
注:在线特征可来源于批处理、流式处理或请求数据源
这三种方法各有不同的权衡取舍,但通常具有显著的实现差异。
1. 在线模型推理配合在线特征
该方法通过特征存储(feature store)提供在线特征,模型服务器(如 KServe)提供模型预测,是构建数据驱动型机器学习应用的强大方案。特别适用于需要实时请求数据运行推理的场景。
features = store.get_online_features(
feature_refs=[
"user_data:click_through_rate",
"user_data:number_of_clicks",
"user_data:average_page_duration",
],
entity_rows=[{"user_id": 1}],
)
model_predictions = model_server.predict(features)
2. 离线模式推理不依赖在线特征
机器学习团队通常认为提供预计算模型预测是最易实现的方案。该方法将模型预测视为特征,通过标准Feast SDK从特征存储中获取。这些预测通常通过批量处理预先生成(如本地运行模型推理生成CSV文件),再物化到在线存储。
model_predictions = store.get_online_features(
feature_refs=[
"user_data:model_predictions",
],
entity_rows=[{"user_id": 1}],
)
此方案无需模型服务器参与,但存在数据时效性问题,且只能服务批量计算时存在的用户/实体。某些场景下这种折衷是可接受的。
3. 在线模型推理配合在线特征和缓存预测
这是最复杂的方案,通过缓存预测结果和在特征写入时触发推理来实现低延迟优化。适用于多源特征输入、计算密集型模型或高延迟敏感场景。
# 客户端读取
features = store.get_online_features(
feature_refs=[
"user_data:click_through_rate",
"user_data:number_of_clicks",
"user_data:average_page_duration",
"user_data:model_predictions",
],
entity_rows=[{"user_id": 1}],
)
if features.to_dict().get('user_data:model_predictions') is None:
model_predictions = model_server.predict(features)
store.write_to_online_store(feature_view_name="user_data", df=pd.DataFrame(model_predictions))
当底层数据变更时需单独调用write_to_online_store
更新预测。
# 数据生产者的客户端写入
user_data = request.POST.get('user_data')
model_predictions = model_server.predict(user_data) # 假设DataFrame包含`user_data`
store.write_to_online_store(feature_view_name="user_data", df=pd.DataFrame(model_predictions))
虽然需要为每个数据生产者增加写入操作,但能实现最低推理延迟。
4. 不依赖特征的在线模型推理
此方案无需使用Feast。模型服务器直接提供预测,常见于LLM等无需特征的模型。但使用检索增强生成(RAG)的生成式模型需要特征(如文档嵌入),此时属于"在线模型推理配合在线特征"范畴。
客户端编排
上述代码示例隐含了客户端协调特征获取与模型推理的设计选择。示例采用以Feast为中心的编排模式,另一种方案可采用以推理服务为中心的编排模式:客户端调用推理端点,由推理服务负责协调。
基于角色的访问控制(RBAC)
简介
基于角色的访问控制(RBAC)是一种安全机制,它根据组织内用户的角色来限制对资源的访问。在 Feast 场景中,RBAC 确保只有经过授权的用户或组才能访问或修改特定资源,从而维护数据安全和操作完整性。
功能需求
Feast 中的 RBAC 实现旨在:
- 权限分配:允许管理员根据用户角色,为其分配各类操作和资源的访问权限
- 无缝集成:无需大幅修改即可与现有业务代码平滑集成
- 向后兼容:默认保持对非授权模型的支持以确保向后兼容性
业务目标
在 Feast 中实施 RBAC 的主要业务目标包括:
- 特征共享:实现多团队在受控访问下的特征存储共享,在保障数据安全的前提下支持协作开发
- 访问控制管理:防止对团队专属资源和空间的未授权访问,管控每个用户/组可执行的操作
参考架构
Feast 作为互联服务集合运行,每个服务都会执行授权检查。架构采用分布式微服务设计,包含以下核心组件:
- 服务端点:执行授权检查,确保仅处理合法请求
- 客户端集成:客户端通过在每个请求附加授权令牌与特征服务进行认证
- 服务间通信:始终授予通信权限
权限模型
Feast 的 RBAC 系统采用包含以下核心概念的权限模型:
- 资源(Resource):Feast 中需要实施安全防护的实体对象
- 操作(Action):对资源执行的逻辑操作(如 Create/Describe/Update/Delete/Read/write)
- 策略(Policy):强制执行资源授权决策的规则集合,默认实现采用基于角色的策略
授权架构
Feast 的授权架构包含以下核心组件:
- 令牌提取器(Token Extractor):从请求头中提取授权令牌
- 令牌解析器(Token Parser):解析令牌获取用户详情
- 策略执行器(Policy Enforcer):基于用户详情验证受保护端点
- 令牌注入器(Token Injector):为每个受保护请求头添加授权令牌
风险提示与免责声明
本文内容基于公开信息研究整理,不构成任何形式的投资建议。历史表现不应作为未来收益保证,市场存在不可预见的波动风险。投资者需结合自身财务状况及风险承受能力独立决策,并自行承担交易结果。作者及发布方不对任何依据本文操作导致的损失承担法律责任。市场有风险,投资须谨慎。