← Todo el material
Curso Intermedio 23 de agosto de 2026

MLOps — Módulo 6: feature stores — features consistentes entre train y prod

Sexto módulo del Curso de MLOps: el train-serve skew, ese bug silencioso donde las features se calculan distinto al entrenar y al servir. Qué es un feature store, sus dos caras offline y online, un ejemplo real de Feast con get_historical_features y get_online_features, y cuándo NO necesitas uno todavía.

☰ Índice del contenido

En el Módulo 5 el paso construir_features vivía dentro del pipeline de entrenamiento. Funciona perfecto — hasta que el modelo llega a producción y alguien tiene que calcular esas mismas features en vivo, para cada cliente que consulta, en milisegundos. Y ahí, casi siempre, el cálculo se hace otra vez, en otro lugar, con otro código. Dos implementaciones de la misma feature: una en el notebook de entrenamiento, otra en el servicio de producción. Parecen iguales. Casi nunca lo son. Este módulo es sobre ese desajuste — el que degrada modelos sin que ningún test se ponga rojo.

La tesis del módulo

El modelo aprende de las features que le diste al entrenar; en producción responde según las features que le das al servir. Si esas dos no son idénticas, el modelo está resolviendo un problema distinto al que aprendió. Un feature store existe para que la feature se defina UNA vez y se sirva igual en los dos mundos.

El bug silencioso: train-serve skew

Train-serve skew (desajuste entre entrenamiento y servicio) es cuando una feature se calcula de forma distinta al entrenar que al servir en producción. El modelo entrenó viendo un valor; en producción recibe otro con el mismo nombre; y como nada crashea, el modelo simplemente predice peor, en silencio, para siempre.

Los ejemplos son deprimentemente comunes:

  • Al entrenar, gasto_promedio_30d se calcula con un groupby de pandas sobre el histórico completo. En producción, el backend lo calcula con una consulta SQL que redondea distinto o cuenta un día menos.
  • Al entrenar, un faltante se rellena con la mediana del dataset. En producción, se rellena con 0 porque “era más fácil”.
  • Al entrenar, la feature usa datos que en el momento real de la predicción todavía no existían (data leakage, el fantasma de ml-08).

  ENTRENAMIENTO              PRODUCCIÓN
 ┌─────────────┐           ┌─────────────┐
 │ notebook:   │           │ backend:    │
 │ groupby +   │           │ SQL a mano  │
 │ mediana     │           │ + relleno 0 │
 └─────────────┘           └─────────────┘
        │                         │
        ▼                         ▼
 ┌─────────────┐           ┌─────────────┐
 │ feature = X │  ¿iguales?│ feature = Y │
 └─────────────┘    NO ──► └─────────────┘
        │                         │
        ▼                         ▼
   modelo aprende            modelo predice
   con X                     con Y  ──►  skew

La causa raíz es una sola: la lógica de la feature está escrita dos veces. Cualquier cosa escrita dos veces, tarde o temprano, deja de coincidir.

El feature store: definir una vez, servir en dos mundos

Un feature store (repositorio de features) es una capa central donde defines cada feature una sola vez y desde donde la sirves consistente a los dos consumidores: el entrenamiento y la inferencia. Su idea clave es que tiene dos caras sobre la misma definición:

  • Offline store — el almacén histórico, en batch. Guarda meses o años de valores de features y sirve para entrenar: “dame el valor que tenía esta feature para este cliente en esta fecha pasada”. Vive en algo tipo un data warehouse o Parquet; prioriza volumen, no velocidad.
  • Online store — el almacén de baja latencia. Guarda solo el valor más reciente de cada feature y sirve para inferir en vivo: “dame el valor actual de esta feature para este cliente, ya”. Vive en algo tipo Redis; prioriza velocidad, no historia.

La misma definición alimenta las dos. Entrenas leyendo del offline, sirves leyendo del online, y como ambos salen de la misma fuente de verdad, el skew desaparece por construcción.


              ┌──────────────────┐
  fuentes ──► │  FEATURE STORE   │
  de datos    │  (definición     │
              │   única)         │
              └──────────────────┘
                 │            │
        offline  │            │  online
        (batch)  ▼            ▼  (baja latencia)
          ┌──────────┐   ┌──────────┐
          │ ENTRENAR │   │  SERVIR  │
          └──────────┘   └──────────┘

Feast en la práctica

Feast (feature store open source) es la implementación más común para aterrizar esto sin comprar una plataforma entera. Defines las features en código y Feast se encarga de materializarlas al offline y al online. Primero, la definición:

# features.py — definición de features en Feast
from datetime import timedelta
from feast import Entity, FeatureView, Field, FileSource
from feast.types import Float32, Int64

# La entidad: sobre QUÉ se calculan las features (aquí, el cliente).
cliente = Entity(name="cliente", join_keys=["cliente_id"])

# De dónde salen los valores históricos (offline).
fuente = FileSource(
    path="s3://datos/features_cliente.parquet",
    timestamp_field="event_timestamp",
)

# La feature view: el grupo de features y su definición ÚNICA.
features_cliente = FeatureView(
    name="features_cliente",
    entities=[cliente],
    ttl=timedelta(days=90),          # cuánto vale un valor antes de expirar
    schema=[
        Field(name="gasto_promedio_30d", dtype=Float32),
        Field(name="antiguedad_meses", dtype=Int64),
        Field(name="usa_app", dtype=Int64),
    ],
    source=fuente,
    online=True,                     # también disponible para servir en vivo
)

Al entrenar, pides los valores tal como eran en un momento del pasado — Feast hace el point-in-time join que evita el data leakage por ti:

# Entrenamiento: leer del OFFLINE store con corte temporal correcto.
from feast import FeatureStore
import pandas as pd

store = FeatureStore(repo_path=".")

# entity_df trae los cliente_id y el instante de cada etiqueta.
entity_df = pd.read_parquet("s3://datos/etiquetas_churn.parquet")

training_df = store.get_historical_features(
    entity_df=entity_df,
    features=[
        "features_cliente:gasto_promedio_30d",
        "features_cliente:antiguedad_meses",
        "features_cliente:usa_app",
    ],
).to_df()
# training_df tiene el valor que la feature TENÍA en la fecha de cada fila.

Al servir, pides el valor actual desde el online store, en milisegundos:

# Producción: leer del ONLINE store para una predicción en vivo.
features = store.get_online_features(
    features=[
        "features_cliente:gasto_promedio_30d",
        "features_cliente:antiguedad_meses",
        "features_cliente:usa_app",
    ],
    entity_rows=[{"cliente_id": 12345}],
).to_dict()
# features son los valores ACTUALES, listos para modelo.predict(...).

Misma definición, mismos nombres, misma fuente. get_historical_features para el train, get_online_features para el serve. El skew no se “corrige”: deja de poder ocurrir.

Offline vs online: la tabla que hay que tener clara

Offline storeOnline store
Para quéEntrenar y backtestingServir predicciones en vivo
Qué guardaTodo el históricoSolo el valor más reciente
LatenciaSegundos–minutos, no importaMilisegundos, crítica
VolumenEnorme (meses/años)Chico (una fila por entidad)
Tecnología típicaWarehouse, Parquet, BigQueryRedis, DynamoDB
Método de Feastget_historical_featuresget_online_features

¿Lo necesitas ya? Casi seguro que no

Acá va la advertencia más importante del módulo, y va a contramano del entusiasmo:

Cuidado: un feature store es infraestructura pesada. Es un servicio más que montar, materializar, monitorear y mantener. Si tienes un modelo, un puñado de features y las calculas con una función de Python que llamas tanto al entrenar como al servir, ya resolviste el skew sin comprar nada. El feature store recién paga su costo cuando tienes muchos modelos compartiendo features, muchos equipos reusándolas, y features que llegan de fuentes en tiempo real. Empieza simple: una función compartida importada desde un módulo común. Sube a feature store cuando el dolor de coordinar features entre equipos sea real, no antes.

El antídoto barato del skew, para la mayoría de los proyectos, es esta disciplina: la transformación de features es una función, se escribe una vez, y tanto el pipeline de entrenamiento como el servicio de producción la importan del mismo lugar. El feature store es esa misma idea, industrializada — útil cuando la escala lo pide.

Errores comunes

  • Montar Feast en el proyecto 1. Con un modelo y diez features, el feature store es puro costo. Una función compartida ya mata el skew. Guarda la artillería para cuando haya escala.
  • Creer que el feature store calcula features. No las inventa: sirve consistentes las que tú definiste. Si la definición estaba mala, la sirve mal en los dos mundos, con más disciplina.
  • Confundir online store con base de datos de la app. El online store guarda el valor más reciente de features para ML, no tus datos transaccionales. Son cosas distintas con dueños distintos.
  • Olvidar el corte temporal al entrenar. Leer del offline sin point-in-time join reintroduce data leakage: usas el valor de hoy para predecir el pasado. get_historical_features existe justamente para eso.
  • Materializar y nunca refrescar. El online store guarda el último valor; si no lo actualizas (feast materialize), sirves features rancias y el modelo decide con información vieja.

La frase para llevarse

“El skew no lo causa un mal modelo: lo causa escribir la misma feature dos veces. La cura, con o sin feature store, es la misma — que la feature exista una sola vez.”

Qué viene

Módulo 7: CI/CD/CT para ML. Ya sabes armar pipelines (M5) y features consistentes (M6). Ahora hay que blindar el proceso: extender el CI/CD del curso de Deploy con validación de datos y de modelos, y con el CT —continuous training— que dispara el reentrenamiento automático.

Material relacionado

MLOpsMachine LearningFeature storeFeastPipelines

Preguntas sobre este curso

Regístrate gratis para hacer preguntas y guardar tu progreso.

✉ Entérate de lo nuevo

Recibe un aviso cuando publique nuevos cursos, workshops o investigaciones. Sin spam.