ML System Design: дизайн рекомендаций
«Спроектируй рекомендации» - самый частый ML-system-design кейс. Проверяют не знание моделей, а порядок мышления: цель → данные → каскад → холодный старт → петля. Прыжок в «возьмём нейронку» - главный минус.
// Держи скелет ответа в голове - интервьюер ведёт по нему, даже если спрашивает вразброс.
Скелет: от цели до петли
Порядок ответа: бизнес-цель и метрика → какие есть сигналы и данные → каскад retrieval-ranking → холодный старт → сервинг и свежесть → петля обратной связи и A/B.
Цель первична: CTR (click-through rate), время, выручка или retention - она диктует лосс ранкера, состав guardrail'ов и даже дизайн выдачи. «Релевантность вообще» - не цель.
// Первые пять минут кейса - вопросы интервьюеру: масштаб каталога, тип контента, доля новых юзеров. Это не потеря времени, это и есть оцениваемый навык.
Каскад и фичи
Каскад обязателен по масштабу: retrieval дёшево сужает миллионы до сотен (ANN по эмбеддингам, co-visitation, популярное по сегменту), ранкер тяжёлой моделью упорядочивает сотни, бизнес-слой накладывает правила.
Фичи трёх семейств: юзер (история, сегмент), айтем (контент, статистики), контекст (время, устройство, точка входа). Кросс-фичи юзер×айтем - хлеб ранкера.
// Свежесть проговори явно: как быстро новый айтем попадает в выдачу, как быстро клик влияет на следующую сессию это выбор между realtime-фичами и ночным пересчётом.
- каскад
- retrieval (миллионы→сотни) → ranking → бизнес-правила
- ANN
- approximate nearest neighbors
Холодный старт и петля
Холодный старт - обязательная глава: новый юзер - онбординг и популярное по сегменту; новый айтем - контентные эмбеддинги плюс exploration-буст показов, чтобы набрать сигнал.
Петля обратной связи: система учится на собственных показах. Без exploration (epsilon, бандиты) и позиционного дебиасинга выдача схлопывается в самоподтверждение.
// Закрывается ответ оценкой: офлайн @K-метрики как фильтр, онлайн A/B с guardrail'ами как решение.
- exploration
- доля показов на новое/неуверенное - разрыв петли
Как отвечать: «Спроектируй ленту рекомендаций для маркетплейса»
Начну с вопросов: цель - конверсия или GMV, размер каталога, доля новых юзеров и товаров. Дальше по скелету. Данные: клики, покупки, просмотры - implicit-сигналы плюс контент товаров. Архитектура - каскад: retrieval из нескольких источников (ANN по two-tower, co-visitation, популярное в сегменте) даёт сотни кандидатов, бустинг-ранкер на кросс-фичах юзер×айтем их сортирует, бизнес-слой фильтрует купленное и держит разнообразие. Холодный старт: онбординг и популярное - юзерам, контентные эмбеддинги с exploration-бустом - товарам. Обучение на логах - с позиционным дебиасингом, в выдаче - доля exploration против самоподтверждения. Оценка: офлайн NDCG@K на time-сплите как фильтр, решение - A/B по конверсии с guardrail на разнообразие и жалобы.
Ответ идёт по скелету целиком и начинается с вопросов - ровно так выглядит сильное прохождение кейса.
На чём валят
- −Прыгнуть в «возьмём нейронку» до вопросов о цели, данных и масштабе.
- −Одна модель на весь каталог без каскада - не влезет в латентность.
- −Забыть холодный старт - первый же уточняющий вопрос интервьюера.
- −Не упомянуть петлю обратной связи и позиционное смещение логов.
Проверьте себя
Пять вопросов из банка по этой подтеме. Всего их 9, остальные разбираются в тренажёре.
- Поиск по каталогу: как совместить лексический поиск (BM25) и векторный (эмбеддинги), и зачем оба?A)Векторный поиск заменяет лексический BM25 в большинстве случаевB)BM25 быстрее векторного, поэтому векторный нужен разве что офлайнC)Гибрид: BM25 на артикулах, вектор на синонимах; слить RRF + реранкерD)Гибрид принципиально не выходит из-за фундаментально разных типов индексов
показать ответ и разбор
+C)Гибрид: BM25 на артикулах, вектор на синонимах; слить RRF + реранкер// разбор: Слабости зеркальны: эмбеддинги плывут на «iPhone 15 Pro 256» и опечатках-артикулах, BM25 слеп к «куртка для дождя» = «непромокаемая ветровка». Reciprocal Rank Fusion сливает выдачи без тюнинга шкал скоров. Тот же паттерн — в RAG-ретривере. Продвинутое продолжение разговора: где здесь место персонализации и бизнес-бустов.
- Рекомендатель максимизирует релевантность и скатывается в одинаковые популярные айтемы. Чем это плохо и что делать?A)Это идеальный результат: чем более популярные и одинаковые айтемы показаны, тем выше итоговая выручка сервисаB)Проблема чисто визуальная и решается тем, что одинаковые карточки просто раскрашивают в интерфейсе в разные яркие цветаC)Нужно отключить учёт релевантности и показывать пользователю случайные товары из каталогаD)Падает разнообразие и новизна (filter bubble, popularity bias); добавляют диверсификацию и exploration в ранжирование
показать ответ и разбор
+D)Падает разнообразие и новизна (filter bubble, popularity bias); добавляют диверсификацию и exploration в ранжирование// разбор: Оптимизация только релевантности схлопывает выдачу в узкий популярный набор: страдают разнообразие, новизна и открытие «длинного хвоста», а пользователь застревает в пузыре и быстрее выгорает. Поэтому в ранжирование добавляют диверсификацию (например, MMR), новизну и exploration, балансируя релевантность с разнообразием. Случайная выдача — другая крайность и тоже вредна.
- Рекомендатель всегда показывает только то, в чём уверен по прошлым данным. Какая проблема и как её называют?A)Проблема в скорости инференса; чтобы её решить, достаточно закэшировать прежние рекомендации наперёдB)Никакой проблемы: показывать самые уверенные рекомендации — оптимальная стратегия выдачи для системыC)Нет exploration: система не собирает данные о неопоказанных айтемах и не улучшается по ним — нужен баланс exploration/exploitationD)Проблема в переобучении ранкера, которое лечится наращиванием объёма обучающей выборки для модели ранжирования
показать ответ и разбор
+C)Нет exploration: система не собирает данные о неопоказанных айтемах и не улучшается по ним — нужен баланс exploration/exploitation// разбор: Чистая эксплуатация (показывать только надёжно-релевантное по прошлым данным) не собирает обратную связь о неопоказанных айтемах: система не узнаёт, что новое или «хвост» могли бы зайти, и застревает в локальном оптимуме (усиливая петлю популярности). Нужен баланс exploration/exploitation — иногда показывать неопределённое ради сбора данных (ε-greedy, Thompson sampling, бандиты). Это не про скорость/кэш и не про переобучение.
- Ранжирование ленты должно расти по кликам, но не проседать по выручке и времени в приложении. Как это устроить?A)Многокритериальное ранжирование: скор — взвешенная комбинация целей (клик, выручка, удержание) с guardrail'амиB)Оптимизировать одну метрику — клики; остальные бизнес-цели при этом подтянутся сами потомC)Обучить три независимые модели и на выдаче случайно выбирать один из их результатов на каждый запросD)Ранжировать без модели, вручную задав фиксированный порядок айтемов под все цели
показать ответ и разбор
+A)Многокритериальное ранжирование: скор — взвешенная комбинация целей (клик, выручка, удержание) с guardrail'ами// разбор: Реальные системы почти всегда многокритериальны: оптимизация одной метрики (кликов) часто вредит другим (кликбейт роняет выручку и удержание). Итоговый скор строят как взвешенную комбинацию предсказаний нескольких целей (pCTR, ожидаемая выручка, вероятность досмотра) с настраиваемыми весами и guardrail'ами, а веса подбирают по онлайн-экспериментам. «Одна метрика подтянет остальные» — ложно, случайный выбор моделей и ручной порядок не масштабируются.
- Зачем в онлайн-ранжирование добавляют признаки текущей сессии (что юзер смотрел минуту назад)?A)Сессионные признаки нужны для аналитики поведения пользователей и на само ранжирование выдачи не влияютB)Чтобы заменить собой долгосрочный профиль пользователя — его история за прошлое после этого больше не нужнаC)Это технически сложно: онлайн-ранкер не умеет учитывать события внутри текущей сессии в реальном времениD)Свежий контекст сессии сильно меняет намерение здесь-и-сейчас; учёт последних действий заметно повышает релевантность выдачи
показать ответ и разбор
+D)Свежий контекст сессии сильно меняет намерение здесь-и-сейчас; учёт последних действий заметно повышает релевантность выдачи// разбор: Намерение пользователя «здесь и сейчас» часто определяется последними действиями (только что смотрел кроссовки — покажи сопутствующее), чего долгосрочный профиль не отражает. Поэтому в онлайн-ранкер подают real-time сессионные признаки (последние клики, запросы, время на айтемах), заметно повышая релевантность. Они дополняют, а не заменяют долгосрочный профиль; технически это стандартная практика, а не «невозможно».
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.