Москва: проверенные б/у ноутбуки с гарантией и проверкой перед оплатой
+7 906 722-94-45

Главная → Статьи → Feature store: единый слой признаков

Feature store: зачем нужен единый слой признаков и когда он оправдан

Feature store: онлайн и офлайн-хранилища, согласованность признаков между обучением и инференсом, материализация и типичные ошибки внедрения.

Прямой ответ

Feature store — слой, который хранит и отдаёт признаки: одинаково для обучения (офлайн, большие срезы истории) и для инференса (онлайн, одно значение по ключу с низкой задержкой). Главная решаемая проблема — рассогласование признаков между обучением и продакшеном (training-serving skew).

Как возникает skew без единого слоя:

  • обучение считает признак SQL-запросом по складу, а сервис — кодом на Python по данным запроса;
  • определения расходятся: разное окно агрегации, разная обработка пропусков, разный часовой пояс;
  • модель в продакшене получает не те признаки, на которых обучалась, и качество падает без видимых причин.

Состав feature store:

  1. Офлайн-хранилище. Исторические срезы признаков с меткой времени; используется для обучения и бэктестов. Обычно таблицы в складе или файлы в lakehouse.
  2. Онлайн-хранилище. Актуальные значения по ключу с задержкой единицы миллисекунд: Redis, DynamoDB, специализированные хранилища.
  3. Реестр признаков. Описание признака: тип, владелец, частота обновления, источник, версия.
  4. Точечно-корректная выборка (point-in-time correctness). При сборе обучающей выборки для события берутся только те значения признаков, которые были известны на момент события. Без этого возникает утечка будущего — модель показывает отличное качество офлайн и проваливается в проде.

Когда feature store оправдан:

  • несколько моделей используют общие признаки;
  • признаки требуют дорогих вычислений, и их нужно переиспользовать;
  • есть расхождения между офлайн и онлайн-логикой;
  • команда растёт и нужна документация признаков.

Когда избыточен: одна модель, десяток признаков, вычисляемых на лету из данных запроса. В этом случае достаточно аккуратного кода и тестов.

Практика: организация признаков

Описание признака как контракт:

features:
  - name: orders_last_30d
    entity: customer_id
    dtype: int
    description: "Число заказов за 30 дней до момента события"
    owner: data-team
    freshness: "1h"
    source: warehouse.orders
    window: 30d

Point-in-time join при сборе выборки:

SELECT e.event_id, e.event_ts, f.orders_last_30d, f.avg_order_value
FROM events e
LEFT JOIN features_daily f
  ON f.customer_id = e.customer_id
 AND f.feature_date = (e.event_ts::date - 1);   -- только данные, известные до события

Практические рекомендации:

  1. Разделяйте сырые данные и признаки. Признак — вычисленная величина с определённым окном и версией. Хранить «просто таблицу клиентов» и называть это feature store бесполезно.
  2. Версионируйте определения. Изменение окна с 30 на 60 дней — это новый признак или новая версия; старые модели не должны молча получить другое значение.
  3. Материализуйте тяжёлые признаки. Агрегации по большим таблицам удобно держать в материализованных представлениях с расписанием обновления.
  4. Проверяйте согласованность офлайн и онлайн. Регулярно сравнивайте значение признака из онлайн-хранилища со значением, вычисленным офлайн на ту же дату. Расхождение — индикатор skew.
  5. Следите за свежестью. Признак с задержкой обновления ведёт себя как другой признак; метрики свежести должны быть на дашборде — см. мониторинг моделей.

Онлайн-отдача признаков:

def get_online_features(customer_id: str) -> dict:
    key = f"feat:customer:{customer_id}"
    data = redis.hgetall(key)
    if not data:
        return DEFAULT_FEATURES           # явная обработка отсутствия
    return {k: decode(v) for k, v in data.items()}

Обработка отсутствия признаков должна быть явной и одинаковой при обучении и в сервисе: модель, обученная на заполненных значениях, при пустом входе ведёт себя непредсказуемо.

Практический сценарий: модель отлично работает офлайн и плохо в проде

Контекст: модель оттока с ROC-AUC 0.93 офлайн показывает 0.71 на реальном трафике.

  1. Проверка утечки. В обучающей выборке признак support_tickets_last_7d брался по состоянию на конец месяца, а не на момент события. Модель фактически «знала будущее»: обращения в поддержку часто следовали за решением об оттоке.
  2. Вторая причина. Онлайн-значение avg_order_value считалось сервисом по последним 10 заказам, а офлайн — по всем заказам клиента.
  3. Исправление. Пересобрали выборку с point-in-time join; унифицировали определения признаков и вынесли их в единый слой с описанием окна; добавили сверку офлайн/онлайн значений.
  4. Результат. Офлайн-метрика снизилась до 0.86, зато онлайн-качество совпало с офлайн-оценкой. Предсказуемость важнее красивой цифры на тесте.
  5. Профилактика. Тест на отсутствие признаков из будущего включён в пайплайн сборки выборки; автоматизация — в статье про MLOps-пайплайн.

Вывод: основная ценность feature store — не «хранилище признаков», а единое определение и point-in-time корректность.

Документация признаков как часть процесса. У каждого признака должен быть владелец и короткое описание: что считает, по какому окну, как обрабатывает пропуски, где используется. Без этого признаки дублируются под разными именами, а их изменения ломают чужие модели. Описание хранят рядом с определением признака, а не в отдельном документе: то, что не обновляется вместе с кодом, устаревает.

Миграция признака. Изменение определения (например, окно с 30 на 60 дней) безопаснее делать как новый признак с последующим переводом моделей, а не правкой существующего. Правка на месте молча меняет вход уже обученных моделей, и деградацию обнаруживают только по онлайн-метрикам.

FAQ

Можно ли заменить feature store материализованными представлениями? Частично: представления решают задачу хранения и вычисления, но не дают онлайн-отдачу с низкой задержкой, реестр признаков и версионирование. Для одной-двух моделей такого решения часто достаточно.

Как быть с признаками, которые нужны в реальном времени? Их считают в потоке и пишут в онлайн-хранилище; офлайн-версия того же признака должна вычисляться по той же логике. Расхождение в окнах или округлениях — типичный источник skew.

Что хранить дольше: признаки или сырые события? Сырые события хранят дольше, потому что определения признаков меняются, и историю нужно уметь пересчитать. Признаки — производный слой, который можно восстановить из событий.

Как оценить, что внедрение feature store оправдано? Посчитайте, сколько раз один признак переиспользуется и сколько инцидентов связано с расхождением определений. Если признаков меньше двух десятков и модель одна, затраты на внедрение не окупятся.

Источники: Feast docs, Wikipedia (feature engineering), PostgreSQL docs (materialized views) — ссылки в шапке.