← к модулям

Feature store и train/serve skew (B4)

Фаза 8 улучшала модель. Здесь — вопрос, от которого зависит, доедет ли улучшение до пользователя: откуда ранкер берёт фичи и те ли это фичи, на которых он учился. Утечка через время, протухание, расхождение обучения и сервинга — измерено на том же логе и том же протоколе, что D2b.

⚠ Секции 2–4 — реальный замер на Brightkite (готовый офлайн-артефакт, VPS видит только JSON). Секции 1 и 5 — живая синтетическая механика: джойн по времени и симулятор расхождения, считаются на лету, без весов и без сырых данных.

загрузка артефакта feature store…

Теория простым языком

Feature store — единственный модуль учебника, где инфраструктура важнее модели. Причина простая: почти все истории «офлайн было +5%, онлайн ноль» заканчиваются не в алгоритме, а в том, что фича на обучении и фича на сервинге — разные величины с одним именем.

Зачем отдельный сервис фичей, если есть SQL и таблица
БылоДо сих пор фичи в проекте считались скриптом: агрегаты по логу, джойн к меткам, обучение (D2b, Фаза 8).
ПроблемаСкрипт не знает про время и про сервинг. Ему всё равно, что агрегат посчитан по всему логу (утечка), что онлайн ту же фичу считает другой код на другом языке (skew), что джоб пересчёта не отработал третьи сутки (staleness), и что фичу переименовали в апстриме (сломанный контракт).
ИдеяFeature store делает фичу версионированным объектом с временем: одно определение на офлайн и онлайн, as-of join по времени события, гарантии свежести, реестр и контракты. Тогда «те же фичи» — это проверяемое утверждение, а не надежда.
Стало лучшеЗамер на Brightkite: дырявая таблица фичей рисует +2.7 п.п. в отчёте и отдаёт ровно 0 в проде; протухание на 30 дней стоит −4.0 п.п.; поломка контракта колонок — до −4.6 п.п. Все три проблемы существуют без единой строчки про модель.
Осталось слабымЭто дорогая инфраструктура: два хранилища (офлайн для обучения, онлайн key-value для низкой латентности), backfill истории, мониторинг, дежурство. На раннем этапе продукта аккуратный скрипт с честным as-of join решает 80% задачи, и начинать надо с него.
ДальшеB6 — re-rank поверх ранкера (квоты, eligibility, разнообразие), потом сборка полного capstone. В индустрии дальше: streaming-фичи, on-demand feature views, embedding store.

Четыре обязанности feature store

1. Point-in-time correctness. К каждому событию присоединяется значение фичи, известное на момент события, а не последнее известное. Это as-of join, и это не оптимизация — это определение корректности обучающей выборки.
2. Train/serve consistency. Одно определение фичи, исполняемое обоими путями. Если офлайн считает pandas, а онлайн — руками написанный Java-код, расхождение вопрос времени, а не вероятности.
3. Freshness и SLA. У каждой фичи есть горизонт устаревания: у «визитов за полгода» он дни, у «товаров в корзине» — секунды. SLA задаётся замером деградации, а не вкусом.
4. Backfill и версионирование. Новая фича должна быть пересчитана вглубь истории по тем же правилам, иначе обучение увидит её только на свежем куске данных. Определение меняется — версия меняется: старая модель продолжает получать старую версию.

Утечка (leakage) — точное определениеФича содержит информацию, недоступную в момент предсказания. Три источника: временная (агрегат посчитан по будущим событиям — наш случай), таргетная (в фиче сидит сам лейбл или его производная: «сумма покупки» для предсказания покупки), групповая (статистика посчитана по всей выборке, включая валидацию — так течёт target encoding без внутреннего фолда). Общее у всех трёх: офлайн-метрика растёт, онлайн — нет. Проверять стоит не «есть ли утечка вообще», а «мог ли этот признак быть вычислен в момент запроса».
Training-serving skewСистематическое расхождение между фичами обучения и сервинга. Виды: разные единицы или нормализация, разные окна агрегации, разные значения по умолчанию для пропусков, разный порядок/имена колонок, разное время отсечки (батч vs запрос). Ключевое свойство, которое мы измерили: ранжирующая метрика к монотонным искажениям почти слепа — она инвариантна к монотонному преобразованию внутри фичи и ловит только сдвиг баланса между фичами. Поэтому skew ищут не метрикой качества, а мониторингом распределений фичей, доли дефолтов, доли значений вне диапазона и калибровки предсказаний.
Offline store / online storeOffline store — исторический слой (колоночное хранилище, партиционирование по времени): нужен для обучения и backfill, оптимизирован под большие сканы и as-of join. Online store — key-value (Redis/DynamoDB и подобные) с последним значением фичи на ключ: нужен для сервинга за единицы миллисекунд, читается по одному ключу. Одно определение фичи материализуется в оба. Отсюда и берутся две главные проблемы модуля: если материализация отстала — staleness; если реализации разошлись — skew. Отдельный класс — on-demand фичи (считаются в момент запроса из контекста: время суток, устройство, текущая сессия): их в online store нет, и именно они чаще всего расходятся с офлайном.

Как это связано с остальным учебником

Утечку через время мы уже ловили дважды: в валидации (random split против temporal) и в D2b (метаданные мест выводятся только из train). Разница в уровне: там протокол оценки, здесь — хранилище фичей, из-за чего одна и та же ошибка попадает сразу во все модели компании. Мониторинг расхождения фичей — прямое продолжение дашборда B7, а вывод «офлайн-дельта не заменяет онлайн-замер» — это Фаза 14. Это ступень 2 из 3 к полному capstone: дальше B6 re-rank constraints, потом сборка.

⚠️ Что может пойти не так

  • Джойнить фичи к меткам по ключу без условия на время — самая дорогая строчка SQL в индустрии: метрика вырастет, прод не заметит улучшения.
  • Считать «фича квазистатична, можно агрегировать по всему логу» — координата места и его типичный час выглядят статичными, но в наших замерах они тоже часть утечки.
  • Проверять утечку только метрикой: подозрительно высокое качество — сигнал, но слабый. Дешевле контракт на фичу и range-check (у нас 85.9% дырявых строк recency вышли за собственный диапазон).
  • Считать train/serve skew поймавшимся, если офлайн-метрика не просела: монотонные искажения её почти не двигают, а выдачу переписывают на десятки процентов.
  • Заполнять пропуск нулём «по умолчанию»: ноль — это значение, и модель прочитает его как утверждение о мире, а не как «неизвестно». Дефолт — часть контракта фичи, а не деталь реализации.
  • Строить стриминг фичей до того, как измерена кривая freshness: у наших фич неделя простоя стоит полпроцента recall — стриминг здесь купил бы копейки за большие деньги.
  • Добавлять фичу без backfill: модель увидит её только на последнем куске истории и научится тому, что это «признак недавних событий».

🧠 Проверь себя: Ранкер выкатили, офлайн-метрики стабильны, жалоб нет. Через неделю выясняется, что онлайн-конверсия просела. Логи показывают: сервис персональных фичей отдаёт по таймауту 12% запросов, в этих случаях фичи заполняются нулями. Что здесь главная системная проблема?

Где это в дорожной карте

B4 — Feature Store & Training-Serving Skew, ступень 2 из 3 на пути к полному capstone (Фаза 8 → B4 → B6 → сборка). Измеренная часть — на реальном Brightkite по протоколу D2b; механика джойна и симулятор скью — синтетические и живые. Главный урок модуля: модель бывает хороша офлайн и плоха онлайн из-за фичей, а не из-за алгоритма.