生产级AI/ML特征存储平台:Feast全面使用指南 — Concepts

生产级AI/ML特征存储平台:Feast全面使用指南 — Concepts

Feast(Feature Store)是一个开源特征存储平台,通过帮助团队定义、管理、验证和提供生产级AI/ML特征,助力大规模生产机器学习系统的运营。

文中内容仅限技术学习与代码实践参考,市场存在不确定性,技术分析需谨慎验证,不构成任何投资建议。

在这里插入图片描述

概述

Feast项目结构

Feast中的顶层命名空间是一个项目。用户在一个项目中定义一个或多个特征视图(feature views)。每个特征视图包含一个或多个特征(features)。这些特征通常关联一个或多个实体(entities)。特征视图必须始终包含数据源(data source),该数据源在生成训练数据集(datasets)以及将特征值物化(materialize)到在线存储(online store)时使用。
您可以在项目页面中了解更多关于Feast项目的信息。

数据摄入

对于仅依赖批处理数据的_离线用例_,Feast无需摄入数据即可查询现有数据(通过数据仓库或(实验性)Spark/Trino等计算引擎)。Feast可帮助管理将流式特征推送至批处理源,使特征可用于训练。

对于在线用例,Feast支持:

  • 从批处理源摄入特征使其在线可用(通过物化过程)
  • 推送流式特征使其在离线/在线场景下均可用

我们将在后续概念页面(数据摄入)中深入探讨

特征注册与检索

特征通过代码形式注册在版本控制仓库中,并通过 实体(entities)特征视图(feature views)特征服务(feature services) 概念与数据源+模型版本绑定。我们将在后续概念页面中详细解释这些概念。这些特征随后存储在**注册表(registry)**中,可供不同用户和服务访问。特征可通过以下方式检索:

  • SDK API方法
  • 部署的特征服务器(feature server)(提供在线特征查询端点以支持实时模型)

Feast支持多种特征检索模式:

使用场景示例API
训练数据生成(Training data generation)训练生产推荐模型时获取(用户,商品)对相关的用户和商品特征get_historical_features
批量预测的离线特征检索(Offline feature retrieval for batch predictions)每日为所有用户预测用户流失率get_historical_features
实时模型预测的在线特征检索(Online feature retrieval for real-time model predictions)获取预计算特征以实时预测信用卡交易是否存在欺诈get_online_features

项目

项目通过基础设施层面的资源隔离实现了特征存储的完全隔离。这种隔离通过资源命名空间实现,例如将相关项目的名称作为表名前缀。每个项目都应被视为包含独立实体(entity)和特征(feature)的独立空间。单个请求中无法从多个项目获取特征。我们建议每个环境(devstagingprod)使用单一特征存储和单一项目。

Project
用户可在项目中定义一个或多个特征视图。每个特征视图包含一个或多个特征。这些特征通常关联一个或多个实体。特征视图必须始终配置数据源,该数据源将用于生成训练数据集以及将特征值物化(materialize)到在线存储(online store)。

"项目"概念提供以下优势:

逻辑分组:项目将相关特征集中管理,便于组织和追踪。

特征定义:在项目内可定义特征及其元数据、类型和数据源,有助于标准化特征的创建和使用流程。

环境隔离:项目为不同环境(开发、测试、生产)提供隔离机制,确保各环境的变更互不影响。

团队协作:通过项目组织特征,团队协作边界清晰,权责分明。

访问控制:项目可实施权限管理,不同用户或团队只能访问与其工作相关的特征。

数据摄取

数据源

在 Feast 中,数据源指用户拥有的原始底层数据(例如 BigQuery 中的表)。Feast 不管理任何原始底层数据,而是负责加载这些数据并执行不同操作以实现特征的检索或服务。

Feast 使用时序数据模型来表示数据。该数据模型用于解释数据源中的特征数据,以构建训练数据集或将特征物化到在线存储中。

以下是一个包含单实体列(driver)和两个特征列(trips_todayrating)的数据源示例:

网约车数据源
Feast 主要支持带时间戳的表格数据作为数据源。存在多种类型的数据源:

  • 批处理数据源:理想情况下存在于数据仓库(BigQuery、Snowflake、Redshift),但也可以存储在数据湖(S3、GCS 等)中。Feast 支持跨这两种存储进行数据摄取和查询
  • 流式数据源:Feast 提供原生流式集成,但支持在不同环境中使流式特征可用。包含两种类型:
    • 推送源 允许用户将特征推送到 Feast 中,使其可用于训练/批量评分(“离线”)和实时特征服务(“在线”)
    • [Alpha] 流式源 允许用户注册来自 Kafka 或 Kinesis 源的元数据。虽然 Feast 提供有限的辅助方法直接从 Kafka/Kinesis 主题摄取,但具体摄取仍需用户自行处理
  • (实验性) 请求数据源:仅在请求时可用(例如需要即时模型预测响应的用户操作)。这类数据主要作为按需特征视图的输入,支持轻量级特征工程和跨源特征组合

批处理数据摄取

从批处理源摄取数据主要用于支持实时模型,通过物化实现。Feast 底层管理着_离线存储_(用于从批处理源可扩展地生成训练数据)和_在线存储_(为实时模型提供低延迟特征访问)。

关键命令 materialize_incremental 可获取批处理源中所有实体的_最新_值,并将这些值摄取到在线存储中。

当使用 write_to_online_store=True 的按需特征视图时,可通过 transform_on_write 参数控制是否在摄取时应用转换。设置 transform_on_write=False 允许物化预转换特征而无需重新应用转换,这对已处理的大型批处理数据集特别有用。

物化可通过编程或 CLI 调用:

代码示例:编程方式调度物化

该代码片段创建指向注册表(包含所有已定义特征)和在线存储(本例为 DynamoDB)的特征存储对象:

# 定义Python可调用对象
def materialize():
  repo_config = RepoConfig(
    registry=RegistryConfig(path="s3://[YOUR BUCKET]/registry.pb"),
    project="feast_demo_aws",
    provider="aws",
    offline_store="file",
    online_store=DynamoDBOnlineStoreConfig(region="us-west-2")
  )
  store = FeatureStore(config=repo_config)
  store.materialize_incremental(datetime.datetime.now())

# (生产环境中)使用Airflow的PythonOperator
materialize_python = PythonOperator(
    task_id='materialize_python',
    python_callable=materialize,
)

代码示例:基于 CLI 的物化

在 CLI 中运行

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

在 Airflow 中运行

# 使用BashOperator
materialize_bash = BashOperator(
    task_id='materialize',
    bash_command=f'feast materialize-incremental {datetime.datetime.now().replace(microsecond=0).isoformat()}',
)

批处理数据模式推断

如果定义数据源时未指定 schema 参数,Feast 会在 feast apply 期间尝试推断数据源模式。具体推断方式取决于离线存储的实现,对于开箱即用的离线存储,Feast 通过检查云数据仓库中的表模式实现推断;如果数据源包含查询语句,则通过运行带 LIMIT 子句的查询并检查结果来推断。

流式数据摄取

流式数据摄取通过 Push API 或使用现有 Spark 上下文的 contrib processor 实现:

实体

实体(Entity)是语义相关特征的集合。用户通过定义实体来映射其用例的业务领域。例如,网约车服务可以将顾客(customer)和司机(driver)作为实体,这些实体将对应顾客和司机的相关特征进行分组。

driver = Entity(name='driver', join_keys=['driver_id'])
  • 实体名称entity name)用于唯一标识实体(例如在实验性 Web UI 中展示)
  • 连接键join key)用于标识物理主键,在特征检索过程中通过该键值进行特征值的连接

Feast 在多种场景中使用实体,具体如下:

用例 #1:定义和存储特征

Feast 定义特征的核心对象是 feature view(特征视图),即特征的集合。特征视图可映射到 0 个或多个实体,因为特征可以关联:

  • 零实体(例如全局特征 num_daily_global_transactions
  • 单一实体(例如用户特征 user_agelast_5_bought_items
  • 多个实体(即复合键,例如用户+商户类别特征 num_user_purchases_in_merchant_category

Feast 将特征视图关联的实体集合称为 entity key(实体键)

Entity
实体应在不同特征视图之间复用。这有助于特征发现,因为数据科学家可以借此了解其他团队如何为其关注的实体构建特征。

Feast 随后会使用特征视图概念来定义低延迟在线存储中的特征组模式。

用例 #2:特征检索

训练阶段,用户可控制需要查询的实体,例如对应训练/测试/验证集的划分。用户需指定 实体键 + 时间戳 列表来获取时间点准确的特征以生成训练数据集。

服务阶段,用户需指定 实体键 来获取最新特征值,以支持实时模型预测(例如需要获取最新交易用户特征来进行预测的欺诈检测模型)。

问:能否为所有实体检索特征?

理论上可行。

实际应用中,这主要适用于纯离线场景的 批量评分模型(例如预测所有现有用户的流失率)。对于这类用例,Feast 支持基于 SQL 实体列表生成特征。GitHub 开源议题正在征集贡献以优化该 API 的易用性。

对于 实时特征检索,系统不原生支持全量实体检索,因为这会导致高开销的扫描操作,可能影响数据源的其他操作性能。用户仍可传入大量实体列表进行检索,但该方式扩展性有限。

特征视图

特征视图

注意:特征视图不适用于不带时间戳的数据。解决方法是插入虚拟时间戳。

特征视图定义为特征集合

  • 在线场景中,这是调用get_online_features方法时读取的有状态特征集合
  • 离线场景中,这是调用get_historical_features方法时创建的无状态特征集合

特征视图是一个对象,代表在数据源中发现的时序特征数据的逻辑分组。根据特征视图类型,可能包含一些轻量级(实验性)特征转换(参见【Beta】按需特征视图)。

特征视图包含以下要素:

  • 一个数据源
  • 零个或多个实体
  • 用于在项目中唯一标识该特征视图的名称
  • (可选但推荐)指定一个或多个特征的模式(若不指定,Feast将通过读取数据源推断模式)
  • (可选但推荐)元数据(例如描述,或通过tags添加的自由格式元数据)
  • (可选)TTL(存活时间),限制Feast生成历史数据集时的回溯时间范围

特征视图使Feast能够在离线(训练)和在线(服务)环境中以一致的方式建模现有特征数据。特征视图通常包含特定对象属性的特征,此时该对象被定义为实体并包含在特征视图中。

from feast import BigQuerySource, Entity, FeatureView, Field
from feast.types import Float32, Int64

driver = Entity(name="driver", join_keys=["driver_id"])

driver_stats_fv = FeatureView(
    name="driver_activity",
    entities=[driver],
    schema=[
        Field(name="trips_today", dtype=Int64),
        Field(name="rating", dtype=Float32),
    ],
    source=BigQuerySource(
        table="feast-oss.demo_data.driver_activity"
    )
)

特征视图用于以下场景:

  • 通过查询特征视图的数据源生成训练数据集,以查找历史特征值。单个训练数据集可能包含来自多个特征视图的特征
  • 将特征值加载到在线存储。特征视图决定在线存储中的存储模式,特征值可从批处理源或流式源加载
  • 从在线存储检索特征。特征视图为Feast提供模式定义以查询在线存储中的特征

无实体的特征视图

如果特征视图包含的特征不与特定实体关联,可以定义无实体的特征视图(仅需要时间戳)。

from feast import BigQuerySource, FeatureView, Field
from feast.types import Int64

global_stats_fv = FeatureView(
    name="global_stats",
    entities=[],
    schema=[
        Field(name="total_trips_today_by_all_drivers", dtype=Int64),
    ],
    source=BigQuerySource(
        table="feast-oss.demo_data.global_stats"
    )
)

特征推断

如果在创建特征视图时未指定schema参数,Feast将在feast apply期间通过以下方式推断特征:为底层数据源中的每个列创建Field(实体列和时间戳列除外)。推断特征的名称和类型将使用源列的列名和数据类型。

实体别名

可以通过指定"实体别名"来连接entity_dataframe中与特征视图源表列名不匹配的列。例如:

  • "spammer"和"reporter"可作为"user"实体的别名
  • "origin"和"destination"可作为"location"实体的别名

建议使用.with_name动态指定新特征视图名称,使用.with_join_key_map覆盖join key映射,而无需注册每个新副本。

from feast import BigQuerySource, Entity, FeatureView, Field
from feast.types import Int32, Int64

location = Entity(name="location", join_keys=["location_id"])

location_stats_fv= FeatureView(
    name="location_stats",
    entities=[location],
    schema=[
        Field(name="temperature", dtype=Int32),
        Field(name="location_id", dtype=Int64),
    ],
    source=BigQuerySource(
        table="feast-oss.demo_data.location_stats"
    ),
)
from location_stats_feature_view import location_stats_fv

temperatures_fs = FeatureService(
    name="temperatures",
    features=[
        location_stats_fv
            .with_name("origin_stats")
            .with_join_key_map(
                {"location_id": "origin_id"}
            ),
        location_stats_fv
            .with_name("destination_stats")
            .with_join_key_map(
                {"location_id": "destination_id"}
            ),
    ],
)

字段

字段(特征)是一个独立的可测量属性。通常关联于特定实体,但也可以不关联实体。例如:

  • 客户实体的特征可以是月均交易次数
  • 非实体关联的特征可以是上月所有用户的总发帖数

支持的类型定义参见sdk/python/feast/types.py

字段作为特征视图的组成部分定义。由于Feast不转换数据,字段本质上只包含名称和类型的模式:

from feast import Field
from feast.types import Float32

trips_today = Field(
    name="trips_today",
    dtype=Float32
)

结合数据源,字段指示Feast查找特征值的位置(如特定Parquet文件或BigQuery表)。特征定义也用于通过特征引用从特征存储读取数据。

同一特征视图中的特征名称必须唯一。

每个字段可通过键值对标签添加元数据。

【Alpha】按需特征视图

按需特征视图允许数据科学家使用现有特征和请求时数据(仅在请求时可用的特征)来转换和创建新特征。用户定义Python转换逻辑,该逻辑将在历史检索和在线检索路径中执行。

当前这些转换在本地执行,适用于在线服务,但不适合大规模离线检索。

使用场景

使数据科学家能够轻松影响在线特征检索路径,典型工作流:

  1. 调用get_historical_features生成训练数据框
  2. 在Notebook中使用Pandas迭代特征工程
  3. 将转换逻辑复制到按需特征视图并提交到特性仓库开发分支
  4. 使用get_historical_features(小数据集)验证转换输出的历史数据
  5. 在开发分支使用get_online_features验证在线特征输出正确性
  6. 提交PR到生产分支以影响生产流量
from feast import Field, RequestSource
from feast.on_demand_feature_view import on_demand_feature_view
from feast.types import Float64

# 定义请求数据源(包含仅在请求时可用的特征)
input_request = RequestSource(
    name="vals_to_add",
    schema=[
        Field(name="val_to_add", dtype=PrimitiveFeastType.INT64),
        Field(name="val_to_add_2": dtype=PrimitiveFeastType.INT64),
    ]
)

# 使用输入数据和特征视图特征创建新特征
@on_demand_feature_view(
   sources=[
       driver_hourly_stats_view,
       input_request
   ],
   schema=[
     Field(name='conv_rate_plus_val1', dtype=Float64),
     Field(name='conv_rate_plus_val2', dtype=Float64)
   ]
)
def transformed_conv_rate(features_df: pd.DataFrame) -> pd.DataFrame:
    df = pd.DataFrame()
    df['conv_rate_plus_val1'] = (features_df['conv_rate'] + features_df['val_to_add'])
    df['conv_rate_plus_val2'] = (features_df['conv_rate'] + features_df['val_to_add_2'])
    return df

【Alpha】流式特征视图

流式特征视图是标准特征视图的扩展,主要区别在于:

  • 流式特征视图同时包含流式和批处理数据源
  • 标准特征视图只有批处理数据源

当存在流式数据源(如Kafka、Kinesis)可提供在线场景下的新鲜特征时,应使用流式特征视图。示例定义:

from datetime import timedelta

from feast import Field, FileSource, KafkaSource, stream_feature_view
from feast.data_format import JsonFormat
from feast.types import Float32

driver_stats_batch_source = FileSource(
    name="driver_stats_source",
    path="data/driver_stats.parquet",
    timestamp_field="event_timestamp",
)

driver_stats_stream_source = KafkaSource(
    name="driver_stats_stream",
    kafka_bootstrap_servers="localhost:9092",
    topic="drivers",
    timestamp_field="event_timestamp",
    batch_source=driver_stats_batch_source,
    message_format=JsonFormat(
        schema_json="driver_id integer, event_timestamp timestamp, conv_rate double, acc_rate double, created timestamp"
    ),
    watermark_delay_threshold=timedelta(minutes=5),
)

@stream_feature_view(
    entities=[driver],
    ttl=timedelta(seconds=8640000000),
    mode="spark",
    schema=[
        Field(name="conv_percentage", dtype=Float32),
        Field(name="acc_percentage", dtype=Float32),
    ],
    timestamp_field="event_timestamp",
    online=True,
    source=driver_stats_stream_source,
)
def driver_hourly_stats_stream(df: DataFrame):
    from pyspark.sql.functions import col

    return (
        df.withColumn("conv_percentage", col("conv_rate") * 100.0)
        .withColumn("acc_percentage", col("acc_rate") * 100.0)
        .drop("conv_rate", "acc_rate")
    )

参考此处了解如何使用流式特征视图注册自定义流式数据管道。

特征检索

概述

Feast 支持以下几种特征检索模式:

  1. 训练数据生成(通过 feature_store.get_historical_features(...)
  2. 批量评分的离线特征检索(通过 feature_store.get_historical_features(...)
  3. 实时模型预测的在线特征检索
    • 通过 SDK:feature_store.get_online_features(...)
    • 通过部署的特征服务端点:requests.post('http://localhost:6566/get-online-features', data=json.dumps(online_request))

所有检索机制都接受以下参数:

开始前需要实例化本地 FeatureStore 对象(需了解如何解析注册表,详见注册表说明

完整代码示例可查看通过 feast init -t [YOUR TEMPLATE] 生成的模板仓库(推荐使用 gcpsnowflakeaws 模板)

核心概念

特征服务(Feature Services)

特征服务表示来自一个或多个特征视图的逻辑特征组。通过特征服务可以将特征视图中的特征按模型需求组合使用。建议每个模型版本对应一个特征服务,便于跟踪模型使用的特征。

from driver_ratings_feature_view import driver_ratings_fv
from driver_trips_feature_view import driver_stats_fv

driver_stats_fs = FeatureService(
    name="driver_activity",
    features=[driver_stats_fv, driver_ratings_fv[["lifetime_rating"]]]
)

特征服务应用于以下场景:

  • 生成训练数据集时查询多个特征视图的历史特征值
  • 从离线存储检索批量评分所需的特征
  • 从在线存储检索实时推理所需的特征

特征服务的应用不会实际部署服务实例

示例:从在线存储检索特征服务

from feast import FeatureStore
feature_store = FeatureStore('.')  # 初始化特征存储

feature_service = feature_store.get_feature_service("driver_activity")
features = feature_store.get_online_features(
    features=feature_service, entity_rows=[entity_dict]
)

示例:从离线存储检索特征服务

from feast import FeatureStore
feature_store = FeatureStore('.')  # 初始化特征存储

feature_service = feature_store.get_feature_service("driver_activity")
feature_store.get_historical_features(features=feature_service, entity_df=entity_df)

特征引用(Feature References)

(建议仅在实验阶段使用该方式,生产环境推荐使用特征服务)

特征引用通过字符串格式 <feature_view>:<feature> 唯一标识特征值。使用示例:

online_features = fs.get_online_features(
    features=[
        'driver_locations:lon',
        'drivers_activity:trips_today'
    ],
    entity_rows=[
        # {join_key: entity_value}
        {'driver': 'driver_1001'}
    ]
)

注意:

  • 支持单次请求从多个特征视图检索特征
  • 不支持跨项目特征引用

使用无实体特征视图时,无需在 entity_rows 中添加实体值

事件时间戳(Event timestamp)

表示特征在数据源中被观测到的时间点,用于:

  • 时间点连接(point-in-time joins)时确保获取最新特征值
  • 在线服务时避免提供过时特征值

数据集(Dataset)

通过历史检索生成的训练数据集合,包含:

  • 一个或多个特征视图与实体数据框的连接结果
  • 来自多个特征视图的特征组合

与特征视图的区别:特征视图定义数据模式和数据源位置,数据集是实际查询结果

与数据源的区别:数据集是历史检索的输出,数据源是输入

历史特征检索(训练数据/批量评分)

通过 get_historical_features API 处理时间点连接的复杂性,主要步骤如下:

完整示例:生成训练数据

entity_df = pd.DataFrame.from_dict(
    {
        "driver_id": [1001, 1002, 1003, 1004, 1001],
        "event_timestamp": [
            datetime(2021, 4, 12, 10, 59, 42),
            datetime(2021, 4, 12, 8, 12, 10),
            datetime(2021, 4, 12, 16, 40, 26),
            datetime(2021, 4, 12, 15, 1, 12),
            datetime.now()
        ]
    }
)
training_df = store.get_historical_features(
    entity_df=entity_df,
    features=store.get_feature_service("model_v1"),
).to_df()
print(training_df.head())

完整示例:批量评分离线特征检索

(与训练数据生成的主要区别在于使用当前时间戳获取最新特征值)

from feast import FeatureStore

store = FeatureStore(repo_path=".")

# 获取最新特征值
entity_sql = f"""
    SELECT
        driver_id,
        CURRENT_TIMESTAMP() as event_timestamp
    FROM {store.get_data_source("driver_hourly_stats_source").get_table_query_string()}
    WHERE event_timestamp BETWEEN '2021-01-01' and '2021-12-31'
    GROUP BY driver_id
"""
batch_scoring_features = store.get_historical_features(
    entity_df=entity_sql,
    features=store.get_feature_service("model_v2"),
).to_df()
# predictions = model.predict(batch_scoring_features)

步骤 1:指定特征

支持两种方式:

示例:查询特征服务

training_df = store.get_historical_features(
    entity_df=entity_df,
    features=store.get_feature_service("model_v1"),
).to_df()

示例:查询特征引用列表

training_df = store.get_historical_features(
    entity_df=entity_df,
    features=[
        "driver_hourly_stats:conv_rate",
        "driver_hourly_stats:acc_rate",
        "driver_daily_features:daily_miles_driven"
    ],
).to_df()

步骤 2:指定实体

支持两种实体指定方式:

  • Pandas 数据框(包含实体键和时间戳)
  • SQL 查询语句

示例:训练数据生成的实体数据框

entity_df = pd.DataFrame.from_dict(
    {
        "driver_id": [1001, 1002, 1003, 1004, 1001],
        "event_timestamp": [
            datetime(2021, 4, 12, 10, 59, 42),
            datetime(2021, 4, 12, 8, 12, 10),
            datetime(2021, 4, 12, 16, 40, 26),
            datetime(2021, 4, 12, 15, 1, 12),
            datetime.now()
        ]
    }
)

示例:训练数据生成的实体 SQL 查询

entity_sql = f"""
    SELECT
        driver_id,
        event_timestamp
    FROM {store.get_data_source("driver_hourly_stats_source").get_table_query_string()}
    WHERE event_timestamp BETWEEN '2021-01-01' and '2021-12-31'
"""

在线特征检索(模型推理)

确保获取注册特征的最新值,需提供:

  • 实体列表
  • 待检索特征列表(推荐使用特征服务)

注意:entity_rows 不需要时间戳

检索方式:

  1. Python SDK
  2. 特征服务端点

完整示例:Python SDK 实时推理

from feast import RepoConfig, FeatureStore
from feast.repo_config import RegistryConfig

repo_config = RepoConfig(
    registry=RegistryConfig(path="gs://feast-test-gcs-bucket/registry.pb"),
    project="feast_demo_gcp",
    provider="gcp",
)
store = FeatureStore(config=repo_config)

features = store.get_online_features(
    features=[
        "driver_hourly_stats:conv_rate",
        "driver_hourly_stats:acc_rate",
        "driver_daily_features:daily_miles_driven",
    ],
    entity_rows=[
        {
            "driver_id": 1001,
        }
    ],
).to_dict()

完整示例:特征服务端点实时推理

(需部署特征服务器,参见Python 特征服务器

import requests
import json

online_request = {
    "features": [
        "driver_hourly_stats:conv_rate",
    ],
    "entities": {"driver_id": [1001, 1002]},
}
r = requests.post('http://localhost:6566/get-online-features', data=json.dumps(online_request))
print(json.dumps(r.json(), indent=4, sort_keys=True))

时间点连接

Feast 中的特征值被建模为时间序列记录。以下是一个包含两个特征列(trips_todayearnings_today)的驾驶员特征视图示例:

point-in-time-joins-1
可以通过以下特征视图在 Feast 中注册上述表格:

from feast import Entity, FeatureView, Field, FileSource
from feast.types import Float32, Int64
from datetime import timedelta

driver = Entity(name="driver", join_keys=["driver_id"])

driver_stats_fv = FeatureView(
    name="driver_hourly_stats",
    entities=[driver],
    schema=[
        Field(name="trips_today", dtype=Int64),
        Field(name="earnings_today", dtype=Float32),
    ],
    ttl=timedelta(hours=2),
    source=FileSource(
        path="driver_hourly_stats.parquet"
    )
)

Feast 能够以时间点正确的方式将一个或多个特征视图中的特征联接到实体数据框。这意味着 Feast 可以复现过去特定时间点的特征状态。

给定以下实体数据框,假设用户希望将上述 driver_hourly_stats 特征视图联接到该数据框,同时保留 trip_success 列:

point-in-time-joins-2

实体数据框中的时间戳代表我们希望复现特征状态的时刻(即这些特定时间点的特征值)。要进行时间点连接,用户需要加载实体数据框并运行历史特征检索:

# 读取实体数据框
entity_df = pd.read_csv("entity_df.csv")

training_df = store.get_historical_features(
    entity_df=entity_df,
    features = [
        'driver_hourly_stats:trips_today',
        'driver_hourly_stats:earnings_today'
    ],
)

对于实体数据框中的每一行,Feast 会从相应特征视图数据源查询并连接所选特征。Feast 将从实体数据框的时间戳开始向后扫描,最大扫描范围为指定的 TTL 时间。

point-in-time-joins-3
请注意 TTL 时间是相对于实体数据框中的每个时间戳计算的,而不是相对于当前查询时间。

以下是最终生成的联接训练数据框,包含原始实体行和联接后的特征值:

point-in-time-joins-4
三个特征行成功联接到实体数据框行。实体数据框的第一行时间早于特征视图中最早的特征行,因此未能联接。实体数据框的最后一行超出 TTL 窗口(特征行生成 11 小时后发生的事件),同样未能联接。

[Alpha] 保存数据集

Feast 数据集可便捷地保存包含特征和实体的数据框,供后续数据分析和模型训练使用。数据质量监控是创建数据集概念的主要动机。

数据集元数据存储在 Feast 注册表中,原始数据(特征、实体、附加输入键和时间戳)存储在离线存储中。

数据集可以从以下来源创建:

  1. 历史特征检索的结果
  2. [计划中] 特征服务期间记录请求(包括按需转换的输入)和响应
  3. [计划中] 写入在线存储时(从批处理源或流)记录特征

从历史检索创建保存数据集

要通过历史特征创建保存数据集以供后续检索或分析,用户需要先调用 get_historical_features 方法,然后将返回的检索作业传递给 create_saved_dataset 方法。create_saved_dataset 将触发提供的检索作业(通过调用 .persist())在后台使用指定的 storage 存储数据。存储类型必须与全局配置的离线存储类型一致(例如无法将数据持久化到不同的离线源)。create_saved_dataset 还会创建包含所有相关元数据的 SavedDataset 对象,并将该对象写入注册表。

from feast import FeatureStore
from feast.infra.offline_stores.bigquery_source import SavedDatasetBigQueryStorage

store = FeatureStore()

historical_job = store.get_historical_features(
    features=["driver:avg_trip"],
    entity_df=...,
)

dataset = store.create_saved_dataset(
    from_=historical_job,
    name='my_training_dataset',
    storage=SavedDatasetBigQueryStorage(table_ref='<gcp-project>.<gcp-dataset>.my_training_dataset'),
    tags={'author': 'oleksii'}
)

dataset.to_df()

后续可以通过特征存储中的 get_saved_dataset 方法检索已保存的数据集:

dataset = store.get_saved_dataset('my_training_dataset')
dataset.to_df()

查看我们的历史特征验证教程,了解该概念在实际用例中的应用。

权限管理

概述

Feast 权限模型允许为特征存储中定义的所有资源配置细粒度权限策略。

配置的权限信息存储在 Feast 注册表中,可通过 CLI 和注册表 API 进行访问。

权限授权检查在执行请求时通过以下 Feast (Python) 服务器实施:

  • 在线特征服务(REST)
  • 离线特征服务(Arrow Flight)
  • 注册表服务(gRPC)

注意:通过本地 provider 访问 Feast API 时不进行权限校验。

核心概念

权限模型基于以下组件构建:

  • 资源(resource):需要安全保护的 Feast 对象
    • 假定资源具有 name 属性及可选的键值对 tags 字典
  • 操作(action):在受保护资源上执行的逻辑操作,包括:
    • create:创建实例
    • describe:访问实例状态
    • update:更新实例状态
    • delete:删除实例
    • read:读取在线和离线存储
    • read_online:读取在线存储
    • read_offline:读取离线存储
    • write:写入任何存储
    • write_online:写入在线存储
    • write_offline:写入离线存储
  • 策略(policy):基于当前用户身份实施授权决策的规则
    • 默认提供基于角色的策略实现,通过用户角色授予/拒绝资源操作权限

Permission 类表示特征存储中配置的单个权限,包含以下属性:

  • name:权限名称
  • types:受保护资源类型列表(默认包含所有管理类型,即 ALL_RESOURCE_TYPES 别名,包含所有子类)
  • name_patterns:资源名称匹配的正则表达式列表(任一匹配即应用策略,默认空列表表示无名称过滤)
  • required_tags:必须匹配的资源标签键值对(默认 None 表示无标签过滤)
  • actions:该权限授权的操作列表(默认 ALL_VALUES 别名表示所有操作)
  • policy:验证客户端请求的策略

为简化配置,预定义了多个常量:

  • feast.feast_object 模块:
    • ALL_RESOURCE_TYPES:所有 FeastObject 类型列表
    • ALL_FEATURE_VIEW_TYPES:所有特征视图类型列表(包含不从 FeatureView 继承的类型如 OnDemandFeatureView
  • feast.permissions.action 模块:
    • ALL_ACTIONS:所有管理操作列表
    • READ:包含在线和离线存储的所有读操作
    • WRITE:包含所有存储的写操作
    • CRUD:包含资源创建、描述、更新、删除等状态管理操作

通过以上定义,特征存储可实现细粒度的资源访问控制,支持按团队划分访问权限,满足组织级服务/数据共享需求及敏感信息保护要求。

feast CLI 新增 permissions 命令用于列出已注册权限,支持识别各权限匹配的资源及未被任何权限覆盖的现存资源。

注意:未匹配任何配置权限的 Feast 资源将不受任何授权策略保护,意味着任何用户均可执行任意操作。

定义示例

此权限定义允许 super-reader 角色用户访问所有特征视图/特征服务的资源状态,并读取所有存储:

Permission(
    name="feature-reader",
    types=[FeatureView, FeatureService],
    policy=RoleBasedPolicy(roles=["super-reader"]),
    actions=[AuthzedAction.DESCRIBE, *READ],
)

此示例仅允许 admindata_team 角色用户写入带 risk_level: high 标签的数据源:

Permission(
    name="ds-writer",
    types=[DataSource],
    required_tags={"risk_level": "high"},
    policy=RoleBasedPolicy(roles=["admin", "data_team"]),
    actions=[AuthzedAction.WRITE],
)

注意:在基于角色的策略中使用多个角色时,用户只需拥有任一指定角色即可获得授权。

以下权限允许 trusted 角色用户读取名称包含 risky 的所有特征视图的离线存储:

Permission(
    name="reader",
    types=[FeatureView],
    name_patterns=".*risky.*", # 接受 `str` 或 `list[str]` 类型
    policy=RoleBasedPolicy(roles=["trusted"]),
    actions=[AuthzedAction.READ_OFFLINE],
)

授权配置

要启用权限功能,需在 feature_store.yaml 配置文件中添加 auth 节。当前 Feast 支持 OIDC 和 Kubernetes RBAC 授权协议。

未指定 auth 配置节时,默认使用 no_auth 模式,表示不实施权限校验。

auth 节包含:

  • type 字段:指定实际授权协议类型
  • 协议专属字段:详见授权管理器文档

标签

概述

Feast 中的标签允许在 UI、CLI 中列出对象或直接查询注册表时进行高效过滤。

为 Feast 对象定义标签的方式是通过定义文件,或直接应用在将注册到特征存储库的对象上。

示例

本例展示在定义文件中为 Feature View 添加标签:

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"),
    ],
    online=True,
    source=driver_stats_source,
    # 标签是附加到每个特征视图的用户自定义键/值对
    tags={"team": "driver_performance"},
)

本例展示在代码中为 Stream Feature View 添加标签:

    sfv = StreamFeatureView(
        name="test kafka stream feature view",
        entities=[entity],
        schema=[],
        description="desc",
        timestamp_field="event_timestamp",
        source=stream_source,
        tags={"team": "driver_performance"},

使用标签 team:driver_performance 过滤特征视图的示例:

$ feast feature-views list --tags team:driver_performance              
NAME                       ENTITIES    TYPE
driver_hourly_stats        {'driver'}  FeatureView
driver_hourly_stats_fresh  {'driver'}  FeatureView

未使用标签过滤时列出特征视图的示例:

$ feast feature-views list
NAME                       ENTITIES    TYPE
driver_hourly_stats        {'driver'}  FeatureView
driver_hourly_stats_fresh  {'driver'}  FeatureView
transformed_conv_rate_fresh  {'driver'}  OnDemandFeatureView
transformed_conv_rate        {'driver'}  OnDemandFeatureView

风险提示与免责声明
本文内容基于公开信息研究整理,不构成任何形式的投资建议。历史表现不应作为未来收益保证,市场存在不可预见的波动风险。投资者需结合自身财务状况及风险承受能力独立决策,并自行承担交易结果。作者及发布方不对任何依据本文操作导致的损失承担法律责任。市场有风险,投资须谨慎。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

船长Q

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值