Pular para o conteúdo principal

Feature stores

Definição

Um feature store é um sistema de dados especificamente projetado para gerenciar o ciclo de vida de features de ML — da transformação de dados brutos, passando pelo armazenamento, até o serviço de baixa latência — de forma consistente entre o treinamento do modelo e a inferência em produção. Sem um feature store, as equipes frequentemente encontram o viés treinamento-serviço: a lógica de computação de features executada offline durante o treinamento difere sutilmente da lógica usada no momento do serviço, fazendo com que os modelos em produção tenham desempenho inferior em relação à avaliação offline.

Os feature stores resolvem isso armazenando definições de features como código e executando a mesma lógica de transformação em ambos os contextos. Eles mantêm duas camadas de armazenamento complementares: um store offline (um data warehouse ou data lake, por exemplo BigQuery, Redshift, arquivos Parquet no S3) que contém grandes conjuntos de dados históricos usados para treinamento e pontuação em lote, e um store online (um banco de dados chave-valor de baixa latência, por exemplo Redis, DynamoDB, Cassandra) que serve valores de features pré-computados para modelos no momento da inferência com latência de submilissegundo.

O problema do viés treinamento-serviço e a necessidade de reutilização de features tornam-se agudos em escala. Uma grande organização pode ter dezenas de equipes calculando features similares (gastos do cliente nos últimos 7 dias, duração da sessão, tipo de dispositivo) de forma independente, com diferenças sutis na lógica de negócios. Um feature store fornece um catálogo governado onde as features são definidas uma vez, validadas e reutilizadas entre equipes e modelos, reduzindo significativamente o esforço de engenharia duplicado e o risco de lógica de features inconsistente.

Como funciona

Definição de features e pipelines de transformação

As features são definidas como código — classes Python ou manifestos YAML — que especificam a fonte de dados, a lógica de transformação e a chave de entidade (o identificador usado para buscar features, por exemplo user_id, product_id). Pipelines de transformação em lote são executados de acordo com um cronograma para materializar features no store offline. Pipelines de transformação por stream (por exemplo usando Flink ou Spark Structured Streaming) mantêm o store online atualizado para features sensíveis ao tempo, como sinais de fraude em tempo real.

Store offline: recuperação de dados de treinamento

Ao treinar um modelo, um conjunto de dados é gerado fornecendo uma lista de chaves de entidade e um conjunto de timestamps (uma "junção point-in-time"). O feature store recupera os valores de features que eram corretos em cada timestamp, evitando assim vazamento de dados futuros. Essa correção point-in-time é uma das coisas mais difíceis de implementar corretamente sem um feature store e uma das garantias mais valiosas que ele fornece.

Store online: serviço de baixa latência

Antes que um modelo sirva uma previsão, ele precisa dos valores de features para a entidade sendo pontuada (por exemplo o usuário fazendo uma requisição). O cliente do feature store consulta o store online pela chave de entidade e retorna um vetor de features em milissegundos. Como as mesmas definições de features sustentam tanto os stores offline quanto online, os valores são garantidamente calculados de forma idêntica.

Registro de features e governança

Um catálogo de features documenta cada feature: sua definição, proprietário, tipo de dado, garantia de frescor e quais modelos a consomem. Essa camada de governança permite a descoberta — uma nova equipe pode navegar pelas features existentes antes de escrever as suas — e análise de impacto — entender quais modelos são afetados se a fonte de dados upstream de uma feature mudar.

Trabalhos de materialização

A materialização é o processo de executar os pipelines de transformação e gravar os resultados nos stores. A materialização offline é executada como um trabalho em lote agendado. A materialização online copia um subconjunto de dados offline para o store online para recuperação rápida, ou é impulsionada por pipelines de streaming quando frescor em tempo real é necessário. Feast, Tecton e Hopsworks fornecem todos comandos CLI ou integrações de orquestração para acionar e monitorar a materialização.

Quando usar / Quando NÃO usar

Usar quandoEvitar quando
Múltiplas equipes ou modelos compartilham a mesma lógica de features e a consistência é críticaHá apenas um modelo com um pequeno conjunto de features estável que nunca muda
O viés treinamento-serviço causou incidentes em produção ou discrepâncias de acuráciaOs requisitos de latência de inferência são flexíveis e a pontuação em lote é suficiente
Conjuntos de dados de treinamento corretos point-in-time são necessários para evitar vazamento de dadosA sobrecarga de engenharia de operar um feature store supera a escala do projeto
As features precisam ser servidas com latência de submilissegundo para previsões em tempo realVocê está em exploração precoce e as features ainda não são estáveis o suficiente para formalizar
Requisitos regulatórios exigem um catálogo de features governado e auditávelA equipe de ciência de dados é pequena e carece de suporte de engenharia de ML para gerenciar a infraestrutura

Comparações

CritérioFeastTectonHopsworks
Código abertoSim (Apache 2.0)Não (SaaS / gerenciado)Sim núcleo; empresa paga
Oferta gerenciadaNão (apenas auto-hospedado)Sim (totalmente gerenciado)Sim (nuvem ou on-prem)
Features de streamingLimitado (via fonte Kafka)Nativo, nível produçãoNativo com integração Flink
Monitoramento de featuresBásicoAvançado (drift integrado)Avançado
Melhor paraEquipes querendo controle OSSEmpresas precisando de features em tempo real gerenciadasEquipes querendo open source full-stack

Vantagens e desvantagens

VantagensDesvantagens
Elimina o viés treinamento-serviço compartilhando a lógica de transformaçãoInvestimento significativo de engenharia para configurar e operar
Permite a reutilização de features entre equipes, reduzindo esforço duplicadoAdiciona dependência operacional no caminho de serviço (disponibilidade do store online)
Junções point-in-time evitam vazamento de dados nos dados de treinamentoDefinições de features podem se tornar um gargalo se a governança for muito rígida
Centraliza a governança e a documentação de featuresCurva de aprendizado para cientistas de dados não familiarizados com a abstração
Suporta serviço de features em lote e em tempo realExcessivo para equipes com poucos modelos e features estáveis

Exemplos de código

# feast_feature_store_example.py
# Demonstrates defining, materializing, and retrieving features with Feast.
# Prerequisites:
# pip install feast pandas scikit-learn
# feast init my_feature_repo && cd my_feature_repo
# (Adjust the data source path below to match your environment.)

# ── feature_repo/features.py ──────────────────────────────────────────────────
# This file defines the feature views and entities in your Feast registry.

from datetime import timedelta
import pandas as pd
from feast import (
Entity,
FeatureStore,
FeatureView,
Field,
FileSource,
)
from feast.types import Float32, Int64

# 1. Define the entity — the primary key used to look up features
driver = Entity(
name="driver",
description="A taxi driver identified by driver_id",
)

# 2. Define the data source (parquet file for local demo; swap for BigQuery etc.)
driver_stats_source = FileSource(
path="data/driver_stats.parquet", # generated below
timestamp_field="event_timestamp",
created_timestamp_column="created",
)

# 3. Define a FeatureView — the transformation and storage spec
driver_stats_fv = FeatureView(
name="driver_hourly_stats",
entities=[driver],
ttl=timedelta(days=7), # how long features stay valid
schema=[
Field(name="conv_rate", dtype=Float32),
Field(name="acc_rate", dtype=Float32),
Field(name="avg_daily_trips", dtype=Int64),
],
online=True, # materialize to online store
source=driver_stats_source,
)


# ── generate_sample_data.py ───────────────────────────────────────────────────
# Run this once to create sample data before materializing.
def generate_driver_stats(path: str = "data/driver_stats.parquet") -> None:
import os
os.makedirs("data", exist_ok=True)

rng = pd.date_range(end=pd.Timestamp.now(tz="UTC"), periods=48, freq="h")
df = pd.DataFrame({
"driver_id": [1001, 1002, 1003] * 16,
"event_timestamp": list(rng[:48]),
"created": pd.Timestamp.now(tz="UTC"),
"conv_rate": [0.8, 0.6, 0.9] * 16,
"acc_rate": [0.95, 0.88, 0.92] * 16,
"avg_daily_trips": [150, 200, 175] * 16,
})
df.to_parquet(path, index=False)
print(f"Sample data written to {path}")


# ── training_data_retrieval.py ────────────────────────────────────────────────
# Retrieve a point-in-time correct training dataset.
def get_training_data(repo_path: str = ".") -> pd.DataFrame:
store = FeatureStore(repo_path=repo_path)

# Entity DataFrame: the entities and timestamps we want features for
entity_df = pd.DataFrame({
"driver_id": [1001, 1002, 1003],
"event_timestamp": [
pd.Timestamp("2024-01-15 10:00:00", tz="UTC"),
pd.Timestamp("2024-01-15 11:00:00", tz="UTC"),
pd.Timestamp("2024-01-15 12:00:00", tz="UTC"),
],
"label": [1, 0, 1], # target variable for supervised training
})

# Point-in-time join: retrieves feature values as-of each row's timestamp
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",
],
).to_df()

print("Training dataset:")
print(training_df.to_string())
return training_df


# ── online_serving.py ─────────────────────────────────────────────────────────
# Retrieve features for real-time inference after materialization.
def get_online_features(driver_ids: list, repo_path: str = ".") -> dict:
store = FeatureStore(repo_path=repo_path)

# Materialize features to the online store first:
# store.materialize_incremental(end_date=pd.Timestamp.now(tz="UTC"))

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=[{"driver_id": did} for did in driver_ids],
).to_dict()

print("Online feature vector:")
for key, values in feature_vector.items():
print(f" {key}: {values}")
return feature_vector


# ── main ──────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
generate_driver_stats()
# After running `feast apply` to register the feature views:
# training_df = get_training_data()
# online_fv = get_online_features([1001, 1002])
print("Feature definitions ready. Run `feast apply` to register them.")

Recursos práticos

Veja também