Фичи и ранжирование
~15 мин

Feature Store и фичи для RecSys

Пользовательские, айтемные, контекстные фичи. Feast, онлайн/оффлайн фичи.

Feature Store — единый источник правды для ML-фичей

Представь: в компании пять ML-команд. Антифрод считает фичу «количество транзакций за 7 дней» в Spark-джобе. Рекомендации — ту же фичу, но в другом пайплайне, чуть по-другому (включая текущий день или нет?). Скоринг — третий вариант. Поиск — четвёртый. Маркетинг — пятый. Пять реализаций одной фичи — пять источников багов, пять мест для поддержки, и ни одна команда не уверена, что считает правильно.

Feature Store — это централизованное хранилище, где фичи определяются один раз, вычисляются одним пайплайном и отдаются всем потребителям. Аналогия: вместо того чтобы каждый отдел компании готовил свой отчёт о продажах, есть один бухгалтерский отдел, который считает цифры и публикует единый отчёт. Все пользуются одним источником правды.

Но Feature Store — это не просто общая таблица. Его главная суперсила: гарантия, что модель на проде получает точно такие же фичи, как при обучении. Без этой гарантии даже идеальная модель может работать плохо — и ты даже не поймёшь почему.

Большая картина: define once → compute → store → serve

Feature Store работает в четыре шага:

1. Define — описываешь фичу декларативно: «средний чек пользователя за 30 дней». Указываешь entity (user_id), источник данных, тип агрегации. Определение хранится как код — версионируется в Git. 2. Compute — пайплайн (Spark, Airflow, стриминг) вычисляет фичи по расписанию или в реальном времени. Одна реализация — все потребители получают одинаковый результат. 3. Store — вычисленные значения попадают в два хранилища: offline store (S3, BigQuery) для обучения и online store (Redis, DynamoDB) для инференса. Offline хранит историю, online — только последние значения. 4. Serve — модель запрашивает фичи по entity_id. При обучении идёт в offline store (исторические данные). При инференсе — в online store (актуальные значения за миллисекунды).

Архитектура Feature Store: источники → вычисление → офлайн/онлайн хранилище → потребители
Feature Store: фичи определяются один раз, вычисляются единым пайплайном и хранятся в двух режимах — offline для обучения, online для инференса.

Ключевой инсайт: offline и online store хранят одни и те же фичи, вычисленные одним кодом. Это то, что устраняет рассинхронизацию между обучением и инференсом.

Training-serving skew: проблема №1 без Feature Store

Training-serving skew — это когда фичи при обучении и на инференсе вычисляются по-разному. Это одна из самых коварных ошибок в ML: модель не падает, не выдаёт NaN, просто тихо работает хуже.

Как это происходит на практике: • Разный код предобработки. DS обучал модель в ноутбуке и писал df["amount"].rolling(7).mean(). Backend-разработчик реализовал ту же логику на Java — но посчитал окно чуть иначе (включая текущий день или нет, включая нули или нет). • Разные источники данных. При обучении фичи считали из Hive-таблицы. На проде — из PostgreSQL. Данные немного отличаются: разная гранулярность, разные задержки обновления. • Time leakage. При обучении случайно использовали «будущие» данные: средний чек *включая* целевое событие. На проде этой информации ещё нет — модель получает другие значения.

Как ловить skew

Сравни распределения фичей в обучающей выборке и на проде. Если они расходятся, но входные данные не изменились — скорее всего skew в коде вычисления. Feature Store устраняет проблему на корню: один код → одни фичи.

Feature Store решает training-serving skew архитектурно: определение фичи задаётся один раз, а compute-пайплайн заполняет и offline, и online store. DS достаёт фичи из offline store для обучения, а inference-сервис — из online store. Код вычисления общий → skew невозможен.

Online vs Offline store: два режима для разных задач

Offline store — для обучения и batch-аналитики. Хранит полную историю значений фичей: «какой был средний чек пользователя 42 на 15 марта 2024?». Реализация: файлы в S3 (Parquet), таблицы в BigQuery, Hive, Snowflake. Latency не важна — запрос может выполняться секунды или минуты. Важен объём: миллиарды строк за месяцы и годы.

Online store — для real-time инференса. Хранит только последние значения фичей: «какой средний чек пользователя 42 прямо сейчас?». Реализация: Redis, DynamoDB, Bigtable. Latency критична — ответ за 1-10ms. Объём меньше: по одной строке на entity.

Сравнение:

                   Offline Store          Online Store
 :          ,     real-time inference
:                       
Latency:           /         110 ms
:         S3, BigQuery, Hive     Redis, DynamoDB
:                                
 :   "все пользователи       "user_id=42
                      X"             прямо сейчас"

Как они связаны: compute-пайплайн записывает фичи в оба хранилища. Batch-джоба (Spark, Airflow) заполняет offline store раз в час/день. Отдельный процесс (materialization) берёт последние значения из offline store и загружает в online store. Некоторые фичи вычисляются стримингом (Kafka, Flink) и попадают в online store напрямую — это «свежие» real-time фичи.

Point-in-time correctness: путешествие назад во времени

Допустим, ты обучаешь модель оттока. Нужна обучающая выборка: для каждого пользователя — фичи на момент целевого события. Пользователь ушёл 10 марта — значит, нужны его фичи на 9 марта (до события), а не на сегодня.

Если просто взять текущие значения из базы — произойдёт time leakage: в фичах окажется информация из будущего относительно момента предсказания. Модель выучит паттерны, которых не будет на проде, и метрики в проде будут хуже.

Point-in-time correctness (point-in-time join) — это способность Feature Store выдавать значения фичей строго на указанный момент времени. Ты передаёшь список (entity_id, timestamp) — Feature Store возвращает значения фичей, которые были актуальны именно на этот timestamp, игнорируя всё, что появилось позже.

# Point-in-time join: правильная обучающая выборка
import pandas as pd
from feast import FeatureStore

store = FeatureStore("feature_repo/")

# Целевые события: user_id + дата события
entity_df = pd.DataFrame({
    "user_id": [101, 102, 103],
    "event_timestamp": [
        "2024-03-09",  # фичи НА ЭТУ дату
        "2024-03-12",  # без утечки из будущего
        "2024-03-15",
    ],
})

# Feast вернёт значения фичей СТРОГО на указанные даты
training_df = store.get_historical_features(
    entity_df=entity_df,
    features=[
        "user_stats:avg_order_30d",
        "user_stats:num_sessions_7d",
        "user_stats:days_since_last_purchase",
    ],
).to_df()

# training_df: user_id | event_timestamp | avg_order_30d | ...
# Каждая строка — фичи БЕЗ утечки из будущего

Без Feature Store point-in-time join приходится реализовывать вручную через window-функции в SQL — это сложно, хрупко и часто содержит баги. Feature Store делает это из коробки: каждая запись фичи хранит entity timestamp, и join учитывает его автоматически.

Feast: open-source, быстрый старт

Feast (Feature Store) — самый популярный open-source Feature Store. Изначально создан в Gojek, сейчас развивается как независимый проект. Feast легковесный: можно запустить локально за 10 минут без Kubernetes и облака.

Как устроен Feast: • Feature definitions — Python-код, описывающий сущности (Entity), источники данных (FileSource, BigQuerySource) и представления фичей (FeatureView). Хранится в Git. • Offline store — по умолчанию файлы (Parquet), можно подключить BigQuery, Snowflake, Redshift. Используется для get_historical_features() — point-in-time join. • Online store — по умолчанию SQLite (для разработки), в проде — Redis, DynamoDB, PostgreSQL. Используется для get_online_features() — real-time запросы. • Materialization — команда feast materialize, которая переносит данные из offline store в online store. Запускается по cron.

# feature_repo/features.py — определение фичей в Feast
from feast import Entity, FeatureView, Field, FileSource
from feast.types import Float32, Int64
from datetime import timedelta

# Сущность — ключ для запроса
user = Entity(name="user_id", join_keys=["user_id"])

# Источник данных — откуда брать значения
user_stats_source = FileSource(
    path="data/user_stats.parquet",
    timestamp_field="event_timestamp",
)

# FeatureView — набор фичей с привязкой к сущности
user_stats_view = FeatureView(
    name="user_stats",
    entities=[user],
    schema=[
        Field(name="avg_order_30d", dtype=Float32),
        Field(name="num_sessions_7d", dtype=Int64),
        Field(name="days_since_last_purchase", dtype=Int64),
    ],
    source=user_stats_source,
    ttl=timedelta(days=1),  # фичи устаревают через 1 день
)
# Использование Feast: online inference
from feast import FeatureStore

store = FeatureStore("feature_repo/")

# Real-time: получить фичи для конкретных пользователей
features = store.get_online_features(
    features=[
        "user_stats:avg_order_30d",
        "user_stats:num_sessions_7d",
    ],
    entity_rows=[
        {"user_id": 101},
        {"user_id": 102},
    ],
).to_dict()

# features = {
#   "user_id": [101, 102],
#   "avg_order_30d": [4250.0, 1800.0],
#   "num_sessions_7d": [12, 3],
# }

Типичный workflow с Feast: описал фичи в Python → feast apply (создаёт/обновляет инфраструктуру) → заполнил offline store данными → feast materialize (загрузил в online store) → используешь get_historical_features() для обучения и get_online_features() для инференса.

Альтернативы: Tecton, Hopsworks, in-house

Tecton — managed Feature Store от создателей Feast. Полностью serverless: стриминг-фичи, автоматическая materialization, monitoring фичей. Дорого, но снимает всю инфраструктурную сложность. Подходит для компаний, где ML — основной бизнес.

Hopsworks — open-source ML-платформа, Feature Store — одна из ключевых компонент. Сильная сторона: встроенная интеграция с PySpark и Kafka для стриминг-фичей. Есть managed-версия и self-hosted.

Vertex AI Feature Store (GCP), SageMaker Feature Store (AWS) — облачные решения, тесно интегрированные с экосистемами своих облаков. Удобно, если ты уже в GCP/AWS, но создают vendor lock-in.

In-house решения. Многие крупные компании строят свои Feature Store: это Redis + cron-джоба + пайплайн вычисления фичей. Плюс: полный контроль, точное соответствие инфраструктуре. Минус: месяцы разработки и поддержка. Для небольших команд (1-3 ML-инженера) in-house — оверинжиниринг; достаточно Feast или даже просто Redis + Parquet-файлы.

Когда Feature Store НЕ нужен

Если у тебя одна модель, один пайплайн и нет real-time инференса — Feature Store избыточен. Достаточно правильно организованных Parquet-файлов и скрипта предобработки. Feature Store окупается, когда: несколько моделей делят одни фичи, есть online serving, или training-serving skew стал реальной проблемой.

🎯 На собеседовании

Junior

Что такое Feature Store? Централизованное хранилище ML-фичей: фичи определяются один раз, вычисляются одним пайплайном и отдаются всем моделям. Гарантирует, что при обучении и на проде фичи одинаковые. • Зачем нужен Feature Store? Устраняет дублирование кода фичей между командами и борется с training-serving skew — рассинхронизацией фичей между обучением и инференсом. • Что такое training-serving skew? Когда фичи при обучении и на проде вычисляются по-разному (разный код, разные источники данных). Модель не падает, но тихо работает хуже.

Middle

Online vs offline store — в чём разница? Offline: полная история фичей в S3/BigQuery, latency секунды, для обучения. Online: последние значения в Redis/DynamoDB, latency 1-10ms, для real-time инференса. Оба заполняются одним пайплайном. • Что такое point-in-time correctness? Feature Store возвращает значения фичей строго на указанный момент времени, исключая утечку будущих данных. Необходимо для корректной обучающей выборки. • Feast — как устроен? Entity → FeatureView → Source. Offline store (Parquet/BigQuery) для get_historical_features, online store (Redis) для get_online_features. Materialization синхронизирует offline→online.

Senior

Спроектируй Feature Store для рекомендаций. Batch-фичи (user profile, item stats) → Spark/Airflow ежечасно → Parquet + Redis. Streaming-фичи (последний клик, текущая сессия) → Kafka/Flink → Redis напрямую. Point-in-time join для обучающей выборки. Monitoring: freshness, coverage, drift. • Как обеспечить freshness real-time фичей? Dual-write: batch-пайплайн обновляет агрегаты раз в час, стриминг-пайплайн обновляет real-time фичи (последний клик, текущий CTR) с задержкой в секунды. TTL в online store для автоматической инвалидации. • Feast vs in-house — когда что? Feast: быстрый старт, community, но ограничен в streaming и monitoring. In-house: месяцы разработки, зато полный контроль над latency, backfill, versioning фичей. Компромисс: начать с Feast, мигрировать на in-house при росте.

Собираем всё вместе

Feature Store решает три проблемы: дублирование кода фичей между командами (define once), training-serving skew (единый пайплайн для offline и online), и time leakage при обучении (point-in-time correctness). Архитектура — четыре шага: define → compute → store (offline + online) → serve.

Если запомнить одну вещь: Feature Store — это не про хранение, а про consistency. Главная ценность — гарантия, что модель на проде видит точно такие же фичи, как при обучении. Без этой гарантии даже лучшая модель деградирует на проде.

Дальше на роадмапе: Model Serving — как отдавать предсказания в batch и online, Monitoring — как ловить data drift и деградацию модели на проде.