сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · ML System Design

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, остальные разбираются в тренажёре.

  1. #recsys_design1 / 5
    Поиск по каталогу: как совместить лексический поиск (BM25) и векторный (эмбеддинги), и зачем оба?
    A)Векторный поиск заменяет лексический BM25 в большинстве случаев
    B)BM25 быстрее векторного, поэтому векторный нужен разве что офлайн
    C)Гибрид: BM25 на артикулах, вектор на синонимах; слить RRF + реранкер
    D)Гибрид принципиально не выходит из-за фундаментально разных типов индексов
    показать ответ и разбор
    +C)Гибрид: BM25 на артикулах, вектор на синонимах; слить RRF + реранкер

    // разбор: Слабости зеркальны: эмбеддинги плывут на «iPhone 15 Pro 256» и опечатках-артикулах, BM25 слеп к «куртка для дождя» = «непромокаемая ветровка». Reciprocal Rank Fusion сливает выдачи без тюнинга шкал скоров. Тот же паттерн — в RAG-ретривере. Продвинутое продолжение разговора: где здесь место персонализации и бизнес-бустов.

  2. #recsys_design2 / 5
    Рекомендатель максимизирует релевантность и скатывается в одинаковые популярные айтемы. Чем это плохо и что делать?
    A)Это идеальный результат: чем более популярные и одинаковые айтемы показаны, тем выше итоговая выручка сервиса
    B)Проблема чисто визуальная и решается тем, что одинаковые карточки просто раскрашивают в интерфейсе в разные яркие цвета
    C)Нужно отключить учёт релевантности и показывать пользователю случайные товары из каталога
    D)Падает разнообразие и новизна (filter bubble, popularity bias); добавляют диверсификацию и exploration в ранжирование
    показать ответ и разбор
    +D)Падает разнообразие и новизна (filter bubble, popularity bias); добавляют диверсификацию и exploration в ранжирование

    // разбор: Оптимизация только релевантности схлопывает выдачу в узкий популярный набор: страдают разнообразие, новизна и открытие «длинного хвоста», а пользователь застревает в пузыре и быстрее выгорает. Поэтому в ранжирование добавляют диверсификацию (например, MMR), новизну и exploration, балансируя релевантность с разнообразием. Случайная выдача — другая крайность и тоже вредна.

  3. #recsys_design3 / 5
    Рекомендатель всегда показывает только то, в чём уверен по прошлым данным. Какая проблема и как её называют?
    A)Проблема в скорости инференса; чтобы её решить, достаточно закэшировать прежние рекомендации наперёд
    B)Никакой проблемы: показывать самые уверенные рекомендации — оптимальная стратегия выдачи для системы
    C)Нет exploration: система не собирает данные о неопоказанных айтемах и не улучшается по ним — нужен баланс exploration/exploitation
    D)Проблема в переобучении ранкера, которое лечится наращиванием объёма обучающей выборки для модели ранжирования
    показать ответ и разбор
    +C)Нет exploration: система не собирает данные о неопоказанных айтемах и не улучшается по ним — нужен баланс exploration/exploitation

    // разбор: Чистая эксплуатация (показывать только надёжно-релевантное по прошлым данным) не собирает обратную связь о неопоказанных айтемах: система не узнаёт, что новое или «хвост» могли бы зайти, и застревает в локальном оптимуме (усиливая петлю популярности). Нужен баланс exploration/exploitation — иногда показывать неопределённое ради сбора данных (ε-greedy, Thompson sampling, бандиты). Это не про скорость/кэш и не про переобучение.

  4. #recsys_design4 / 5
    Ранжирование ленты должно расти по кликам, но не проседать по выручке и времени в приложении. Как это устроить?
    A)Многокритериальное ранжирование: скор — взвешенная комбинация целей (клик, выручка, удержание) с guardrail'ами
    B)Оптимизировать одну метрику — клики; остальные бизнес-цели при этом подтянутся сами потом
    C)Обучить три независимые модели и на выдаче случайно выбирать один из их результатов на каждый запрос
    D)Ранжировать без модели, вручную задав фиксированный порядок айтемов под все цели
    показать ответ и разбор
    +A)Многокритериальное ранжирование: скор — взвешенная комбинация целей (клик, выручка, удержание) с guardrail'ами

    // разбор: Реальные системы почти всегда многокритериальны: оптимизация одной метрики (кликов) часто вредит другим (кликбейт роняет выручку и удержание). Итоговый скор строят как взвешенную комбинацию предсказаний нескольких целей (pCTR, ожидаемая выручка, вероятность досмотра) с настраиваемыми весами и guardrail'ами, а веса подбирают по онлайн-экспериментам. «Одна метрика подтянет остальные» — ложно, случайный выбор моделей и ручной порядок не масштабируются.

  5. #recsys_design5 / 5
    Зачем в онлайн-ранжирование добавляют признаки текущей сессии (что юзер смотрел минуту назад)?
    A)Сессионные признаки нужны для аналитики поведения пользователей и на само ранжирование выдачи не влияют
    B)Чтобы заменить собой долгосрочный профиль пользователя — его история за прошлое после этого больше не нужна
    C)Это технически сложно: онлайн-ранкер не умеет учитывать события внутри текущей сессии в реальном времени
    D)Свежий контекст сессии сильно меняет намерение здесь-и-сейчас; учёт последних действий заметно повышает релевантность выдачи
    показать ответ и разбор
    +D)Свежий контекст сессии сильно меняет намерение здесь-и-сейчас; учёт последних действий заметно повышает релевантность выдачи

    // разбор: Намерение пользователя «здесь и сейчас» часто определяется последними действиями (только что смотрел кроссовки — покажи сопутствующее), чего долгосрочный профиль не отражает. Поэтому в онлайн-ранкер подают real-time сессионные признаки (последние клики, запросы, время на айтемах), заметно повышая релевантность. Они дополняют, а не заменяют долгосрочный профиль; технически это стандартная практика, а не «невозможно».

дальше

Теорию прочитали. Навык ставится повторением

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