Часть VI · Проектирование · глава 19 из 19
Проектирование системы
Последняя глава собирает предыдущие восемнадцать в одну процедуру. На собеседовании «спроектируйте рекомендательную систему для X» — вопрос не про знание архитектур, а про порядок мышления: с чего вы начнёте, что зафиксируете до того, как назовёте хоть одну модель, и в какой момент признаете, что первое решение придётся выбросить.
- Сначала простое работающее решение, потом критика, потом полное. Сложная система, спроектированная с нуля, не работает и не чинится заплатками.
- Целеполагание нельзя пропускать. Грубую ошибку здесь не вытащит никакая модель ниже — а пропустить его хочется всегда.
- Размер каталога определяет архитектуру. До 75 000 кандидатов воронка не нужна вовсе, и отказ от неё — правильное решение, а не упрощение.
- Редкое целевое событие меряют прокси не из упрямства. Поймать тот же относительный эффект на конверсии 0.5% вместо 40% стоит в 133 раза больше трафика.
1. Закон Голла
«Работающая сложная система неизменно оказывается развившейся из простой системы, которая работала. Сложная система, спроектированная с нуля, никогда не работает, и её нельзя залатать так, чтобы она заработала».
Отсюда общая схема ответа: сначала строим простое, но работающее решение → критикуем его → строим более полное.
И тонкость, ради которой это не пустой лозунг: критика опирается на те особенности задачи, которые мы доосознали, пока строили простое решение. То есть простое решение нужно не только как запасной вариант — оно нужно как инструмент понимания.
Они — часть жизни любого инженера и любого рантайма. Принимать их лучше всего, понимая, какое решение долгосрочно правильное, — чтобы не сделать чего-то, что в корне ему противоречит.
Оговорка честная: иногда просто осознать правильное решение крайне дорого. Тогда лучше довериться опыту — какое-то работающее решение в любом случае лучше, чем никакого.
2. Пять шагов
Шаг 1: целеполагание
- Находим ключевую бизнес-метрику, которую хотим вырастить.
- Связываем её с офлайн-метрикой, которую может растить модель.
- Определяем ключевой таргет модели.
Грубую ошибку здесь не вытащит никакая сложная модель ниже. Если вы оптимизируете не то, качество реализации не имеет значения.
А пропустить его хочется всегда: многим не терпится начать делать уже что-нибудь, и часто это полезное свойство — но именно поэтому целеполагание выглядит самым логичным кандидатом на быстрый пропуск.
Отчасти ради компенсации этого соблазна мы и строим сначала простое решение: чтобы быстро дойти до работающего и вернуться к цели с пониманием задачи.
Шаг 2: ограничения
Этап чисто брейнштормовый — он готовит материальную базу для всех последующих решений. Что собирать:
- размеры каталога и множества пользователей;
- требования к доступности, фильтрации, прочие продуктовые ограничения;
- отдельно — требования к скорости ответа;
- устройство интента пользователя;
- основные смещения, которым система подвержена сильнее всего.
Размер каталога определяет архитектуру больше, чем что-либо ещё. Пусть ранжирующая модель тратит 4 мкс на кандидата, а бюджет ответа — 300 мс:
| Каталог | Полный перебор | Влезает? | Нужен отбор в |
|---|---|---|---|
| 3e+03 | 12.0 мс | да | — |
| 5e+05 | 2000.0 мс | НЕТ | 7 раз |
| 5e+08 | 2000000.0 мс | НЕТ | 6667 раз |
Порог, где перебор перестаёт влезать: 75 000 кандидатов.
Числа воспроизводятся скриптом _tools/design_demo.py в этом репозитории.
Ниже порога кандидатогенерация не нужна вовсе — и это не упрощение, а правильное решение: любая воронка теряет кандидатов, а потолок качества задаёт первая стадия. Кандидат, который начинает ответ с «сначала построим двухбашенный ретривал», не спросив про размер каталога, показывает, что применяет шаблон, а не думает.
Шаги 3–4: техника и ML
Сформулируйте ключевых акторов — чаще всего это пользователь, айтем и контекст. Затем придумайте идеи для кандидатогенерации и признаков:
- для каждого актора по отдельности — три ячейки;
- для каждой пары акторов — ещё три.
Получается сетка 3 + 3, которая почти всегда покрывает всё существенное. И её же удобно проговаривать вслух как структуру ответа — интервьюер видит систему, а не перечисление.
| Актор или пара | Примеры признаков |
|---|---|
| Пользователь | частота покупок (с ограничением по времени, чтобы признаки меньше «ехали»), средний чек, эмбеддинги любимых категорий |
| Айтем | продажи товара, его категории и производителя за \(X\) дней; показы, CTR, CVR и их взвешенные версии; эмбеддинг |
| Контекст | день недели, секунды с начала суток, эмбеддинг поверхности, эмбеддинг корзины |
| Пользователь × айтем | скалярное произведение башен; сколько раз пользователь брал этот товар, эту категорию, этого производителя за \(X\) дней |
| Пользователь × контекст | сколько пользователь обычно покупает в этот день и час; насколько текущая корзина для него характерна |
| Айтем × контекст | как часто товар берут в это время; его CTR и CVR в это время; близость товара к корзине |
Шаг 5: эксперимент
- Устройство A/B: какой сплит, какой критерий, мощность и уровень значимости, нужная длина, интересные срезы.
- Ключевые метрики: фиксируем список, опираясь на целеполагание, и делаем его шире для удобства интерпретации.
- Метрики-антагонисты: те, что помогут понять, что происходит, когда что-то неизбежно пойдёт не так.
Зафиксируйте конкретную гипотезу, которую проверяете. Иначе при большом количестве метрик вы всегда найдёте что-то приятное, что покрасилось, — и эксперимент превратится в поиск подтверждений вместо проверки.
И до запуска стоит прикинуть, хватит ли трафика: размер выборки и MDE считаются заранее, а не после того, как эксперимент не покрасился.
3. Кейс 1: e-grocery
Якорный пример, на фоне которого дальше проще показывать отличия.
Продуктовые. Рекомендации на очень многих поверхностях, которые активно каннибализируют друг друга. Пользователь хорошо известен: большая доля регулярных покупателей, без логина ничего не работает — холодный старт менее актуален. И у пользователя уже есть интент: почти никто не заходит «просто поскроллить». Задача — сильно не мешать, при этом продать ещё что-то по дороге.
Технические. Каталог на точке — чаще всего до 3000 айтемов, хорошо структурирован. Пользователей десятки миллионов, запросов — сотни в секунду. Бюджет ответа с учётом предзагрузок — до 300 мс. Товары становятся доступны и недоступны мгновенно, и они портятся, поэтому новинки должны появляться в выдаче быстро.
Простой первый подход. Ключевая задача бизнеса — рост оборота. Офлайн проксируем его через NDCG, где gain — цена товара. Отсюда очевидный таргет — покупка, а скор собирается как
$$ \text{score} = p(\text{buy}\mid \text{show}) \cdot \text{price} $$либо цена вшивается прямо в ранжирующий лосс. Датасет следует явно: покупка против показа в слейте.
А теперь критика — то самое второе прохождение схемы:
- деньги в моменте могут не соответствовать деньгам в долгосроке; бизнес скорее хочет растить сумму по пользователям за длинный горизонт;
- метрика не учитывает, что пользователи покупают несколько одинаковых товаров;
- и она никак не учитывает каннибализацию поверхностей.
Заметьте: ни одна из трёх проблем не чинится улучшением модели. Все три — про постановку.
Две поверхности, каждая изолированно даёт +10 единиц оборота:
| Пересечение аудитории и ассортимента | 0.0 | 0.2 | 0.5 | 0.8 | 1.0 |
|---|---|---|---|---|---|
| суммарный прирост | +20.0 | +18.0 | +15.0 | +12.0 | +10.0 |
| потеря против суммы | 0% | 10% | 25% | 40% | 50% |
Числа воспроизводятся скриптом _tools/design_demo.py.
При полном пересечении вторая поверхность не добавляет ничего: пользователь купил бы то же самое и без неё. A/B каждой поверхности по отдельности покажет сумму 20, а совместный запуск даст меньше — и разница тем больше, чем ближе поверхности.
Это классическая ловушка отчётности: команды честно показывают приросты, а общий оборот не растёт. Лечится метриками на уровне пользователя, а не поверхности, и аплифт-постановкой — «покупка на этой поверхности относительно покупки вообще».
| Решение | Почему так |
|---|---|
| Без шардирования | при таком каталоге и нагрузке в нём нет смысла |
| Статическое хранилище профилей, обновление раз в сутки | пользователи редко покупают чаще раза в сутки |
| Статический индекс айтемов, строится раз в сутки | выход нового товара известен заранее |
| Кеш доступных товаров прямо на сервисе + дофильтровка | фильтрация критична: наличие меняется мгновенно |
| Фильтр по текущей корзине и по легальным ограничениям | не показываем добавленное; табак, алкоголь |
Полезно назвать самому, не дожидаясь вопроса.
- Пользователь может долго собирать корзину. Тогда действия внутри сессии становятся важны — особенно для тех, про кого мы знаем меньше. Значит, нужен рантайм-контур для пользовательской части.
- Решения против каннибализации потребуют полноценного общего слоя признаков.
- Рост каталога потребует выноса кандидатогенерации и признаков в отдельные сервисы.
Переранжирование: гигиенический минимум — софтмакс-сэмплинг против петли обратной связи, плюс категориальное разнообразие и отдельная работа с новинками через бандитные подмесы.
Эксперимент: сплит пользовательский; ключевая метрика — интегральный оборот; вспомогательные — маржа и конверсия поверхности; антагонисты — оборот по категориям (не перекосили ли выдачу) и конверсия по позициям (не сломали ли верх).
4. Кейс 2: онлайн-кинотеатр
Дальше — только то, что значимо отличается от якорного кейса.
- Каталог 300–500 тысяч: кандидатогенерация точно нужна, но шардирование ещё нет.
- Всё потребление строится от одного айтема: смотрит серию — дальше почти наверняка смотрит этот сериал. Поэтому рантайм-профиль пользователя может быть и не нужен.
- Много региональных ограничений и видов подписки — обязательно учитывать на ранней стадии фильтрации.
- Детский контент замусоривает историю — на практике чаще всего просто удаляют.
Бизнесу нужны подписки. Но число подписок — плохая метрика для A/B: событие очень редкое, и в коротком эксперименте оно не покрасится. Насколько именно плохая:
| Базовая конверсия | Нужно на группу | Дороже, чем при 40% |
|---|---|---|
| 0.400 | 9.42e+03 | 1.0 |
| 0.200 | 2.51e+04 | 2.7 |
| 0.050 | 1.19e+05 | 12.7 |
| 0.010 | 6.22e+05 | 66.0 |
| 0.005 | 1.25e+06 | 132.7 |
Числа воспроизводятся скриптом _tools/design_demo.py; относительный прирост 5%, значимость 0.05, мощность 80%.
Число наблюдений пропорционально \((1-p)/p\) при фиксированном относительном эффекте. Значит поймать тот же эффект на конверсии 0.5% вместо 40% стоит в 133 раза больше трафика.
Прокси-метрику берут не из упрямства аналитика — прямое измерение просто не помещается ни в какой разумный эксперимент.
- Выделяем список базовых метрик с правдоподобным долгосрочным эффектом: просмотр сериалов (у них лучше удержание), дискавери и разнообразие потребляемого, частота использования.
- Строим из них десяток комбинаций-кандидатов.
- Ретроспективно понимаем, какие из них лучше показывают долгосрочный эффект — сопоставлением похожих пользователей или скорректированными корреляциями. Можно и модель обучить, но нужна интерпретируемость.
- Проверяем на золотом наборе прошлых экспериментов — тех A/B, у которых долгосрочный исход уже известен.
Четвёртый пункт — тот, который обычно забывают назвать, а он и есть настоящая валидация: без него прокси остаётся гипотезой.
Холодных пользователей много, нужен онбординг. Наивная схема: показать топ популярного, пользователь выбирает, пересчитать топ. Проблема в том, что топ почти не меняется.
Приём: после выбора удалить из выборки всех пользователей, которым выбранное тоже нравилось, и перестроить топ. Смоделируем на аудитории из пяти вкусовых групп разного размера, пять вопросов:
| Схема | Про какие группы спросили | Групп покрыто |
|---|---|---|
| наивно, пул не меняется | 0, 0, 0, 0, 0 | 1 из 5 |
| удаляем тех, кому зашло | 0, 1, 2, 3, 4 | 5 из 5 |
Числа воспроизводятся скриптом _tools/design_demo.py.
Наивная схема пять раз подряд спрашивает про один и тот же вкус: самая большая группа никуда не девается, и каждый следующий вопрос не приносит новой информации. Удаление превращает топ в «топ для тех, кому не зашло предыдущее» — и за пять вопросов мы обходим все группы.
Формулировка, которую стоит унести: мы выбираем следующий вопрос так, чтобы он максимально делил оставшуюся неопределённость, а не так, чтобы он был самым популярным.
Карусельный интерфейс добавляет свой сюжет: выдача состоит из блоков — «продолжить просмотр», «похожие на…», жанровые подборки. Можно выбирать блоки и ранжировать внутри них, а можно сгенерировать общий пул и нарезать; на практике делают оба. Внешнее ранжирование между блоками часто бандитное из-за гетерогенности: сравнивать «продолжить просмотр» с жанровой подборкой одной моделью тяжело — у них разная природа и разный масштаб откликов.
5. Кейс 3: медиа-фид
Масштаб меняет всё.
- Индекс — сотни миллионов айтемов: шардирование абсолютно точно нужно.
- Явного интента нет — заходят посмотреть «что-нибудь». Потребление дешёвое и короткое, и точно нужен рантайм-контур признаков.
- Не все айтемы надёжны — это пользовательский контент, нужен пайплайн модерации и мгновенных банов.
- Деградация и свежесть: тренды появляются и исчезают быстро, модели устаревают заметно быстрее, чем в предыдущих двух кейсах. Помогают онлайн-дообучение и конвейеры переобучения.
Это главное отличие кейса, и оно меняет постановку. У автора свои интересы и ожидания: авторы рассчитывают на показы, их отсутствие вызывает жалобы. Система перестаёт быть двусторонней «пользователь — айтем» и становится трёхсторонней.
Типичные подходы: встроенный в модель item exploration, PID-контроллеры для стабилизации объёма показов, явные квоты.
И следствие, о котором забывают: авторам платят за показы, значит начинается фрод — крутят ботов, накручивают CTR. В хорошей системе таргет антифрод-aware, как и целевая метрика, а команды работают тесно.
Тонкость, которую хорошо назвать на собеседовании. На больших интерфейсах — лента, подборки — можно оптимизировать длинные сессии. На маленьких — один ролик на экране, карусель — скорее нет: длинные статьи и видео будут пропускаться, и мы получим артефакты в метрике.
Мультимодальность добавляет ещё: статьи, видео и короткие ролики замешаны в одном фиде, значит и представления, и метрики должны быть сравнимы между типами контента. «Досмотрел» для статьи и для ролика — разные события с разной ценой.
6. Три системы рядом
| E-grocery | Кинотеатр | Медиа-фид | |
|---|---|---|---|
| Каталог | ~3 000 на точке | 300–500 тыс. | сотни миллионов |
| Кандидатогенерация | можно без неё | нужна, шардов нет | нужна + шардирование |
| Интент | сильный, покупка | от одного айтема | отсутствует |
| Холодный старт | менее актуален | критичен для пользователей | критичен для айтемов |
| Бизнес-цель | оборот → NDCG с gain = цена | подписки → прокси от времени просмотра | внимание + счастье авторов |
| Ключевая боль | каннибализация поверхностей | редкое целевое событие | масштаб, модерация, фрод |
Обратите внимание, что ни одна строка не про модель. Ранжирующая архитектура во всех трёх случаях будет похожей; различает системы всё остальное — и именно про это спрашивают.
Как разложить 300 мс по стадиям — полезно держать в голове порядок величин:
| Стадия | мс | Доля |
|---|---|---|
| сеть и разбор запроса | 20 | 6.7% |
| профиль пользователя | 30 | 10.0% |
| кандидатогенерация | 40 | 13.3% |
| фильтрация | 15 | 5.0% |
| признаки | 60 | 20.0% |
| ранжирование | 80 | 26.7% |
| переранжирование и блендинг | 25 | 8.3% |
| сериализация и сеть назад | 20 | 6.7% |
| итого | 290 | 96.7% |
Числа воспроизводятся скриптом _tools/design_demo.py.
Запас — 10 мс, и он весь уйдёт на хвост распределения: бюджет надо держать по p99, а не по среднему. И заметьте, где деньги: признаки и ранжирование съедают 47%, поэтому ускорение ищут сначала там, а не в кандидатогенерации, где его инстинктивно ищут первым делом.
7. Чек-лист собеседования
- Какая бизнес-метрика? И сразу — насколько часто происходит целевое событие. Если оно редкое, разговор про прокси начинается здесь, а не в конце.
- Размер каталога и число пользователей? От этого зависит, нужна ли воронка вообще.
- Есть ли у пользователя интент? Лента и поиск — разные задачи, хотя формально одна.
- Бюджет ответа? 50 мс и 300 мс — это разные архитектуры.
- Кто известен: пользователи, айтемы, оба? Отсюда, какой холодный старт критичен.
- Есть ли третья сторона — авторы, продавцы, рекламодатели? Это меняет постановку целиком.
- Какие фильтрации обязательны? Легальные ограничения проектируются с самого начала, а не прикручиваются.
- Начать с архитектуры. «Возьмём двухбашенный ретривал и DCN сверху» — до того, как выяснен размер каталога. Показывает шаблон вместо мышления.
- Пропустить целеполагание. Самая дорогая ошибка: её не вытащит ничто ниже по стеку.
- Не назвать, где решение сломается. Хороший ответ сам содержит критику; ждать её от интервьюера — упущенная возможность.
- Забыть про эксперимент. Система, которую нельзя измерить, не спроектирована.
- Не назвать метрики-антагонисты. Признак того, что человек не запускал ничего в прод: там всегда что-то ломается в соседнем месте.
Вопросы с собеседований
Спроектируйте рекомендательную систему для X. С чего начнёте?
С пяти шагов в таком порядке: целеполагание (бизнес-метрика → офлайн-метрика → таргет модели), ограничения (каталог, пользователи, SLA, фильтрации, интент, смещения), техническая архитектура, ML-архитектура, сетап эксперимента. Потом критика получившегося и второй проход.
Обоснование — закон Голла: работающая сложная система всегда развилась из простой работающей, а спроектированная с нуля не работает и не чинится заплатками. Причём простое решение нужно не как запасной вариант, а как инструмент понимания: критиковать мы будем на основе того, что доосознали, пока его строили.
Первое, что я спрошу до всякой архитектуры: какая бизнес-метрика, насколько редкое целевое событие, каков размер каталога и бюджет ответа.
Почему целеполагание нельзя пропускать?
Потому что грубую ошибку на этом этапе не вытащит никакая модель ниже: если оптимизируется не то, качество реализации не имеет значения.
И это ровно тот этап, который хочется пропустить: многим не терпится начать делать, и обычно это полезное свойство — потому оно и опасно именно здесь. Частичная компенсация — строить сначала простое решение, чтобы быстро дойти до работающего и вернуться к цели уже с пониманием задачи.
Как понять, нужна ли кандидатогенерация?
Посчитать. Если ранжирующая модель тратит порядка 4 мкс на кандидата, а бюджет 300 мс, то полный перебор влезает примерно до 75 тысяч кандидатов. Каталог в 3000 айтемов перебирается за 12 мс — воронка не нужна вовсе. Каталог в 500 тысяч требует отбора в 7 раз, сотни миллионов — в тысячи раз.
И важно, что отказ от воронки при маленьком каталоге — не упрощение, а правильное решение: любая воронка теряет кандидатов, а потолок качества задаёт её первая стадия.
Бизнесу нужны подписки. Почему нельзя мерить подписки?
Потому что событие слишком редкое. Число наблюдений для фиксированного относительного эффекта пропорционально (1−p)/p, поэтому поймать тот же прирост на конверсии 0.5% вместо 40% стоит в 133 раза больше трафика: 1.25 млн на группу против 9.4 тысяч.
Нужна прокси-метрика — чувствительная в коротком A/B и коррелирующая с долгосрочной целью. Строят её так: выбрать базовые метрики с правдоподобным долгосрочным эффектом (просмотр сериалов, дискавери, частота использования), собрать из них комбинации-кандидаты, ретроспективно отобрать лучшие и обязательно проверить на золотом наборе прошлых экспериментов с известным исходом. Последний шаг и есть настоящая валидация.
Как быстро сгенерировать идеи для признаков?
Сеткой 3 + 3. Выписать акторов — обычно пользователь, айтем, контекст — и придумать признаки для каждого по отдельности и для каждой пары.
Например: пользователь — частота покупок и средний чек; айтем — продажи и CTR за X дней; контекст — день недели и эмбеддинг поверхности; пользователь × айтем — скалярное произведение башен и сколько раз он брал эту категорию; пользователь × контекст — сколько он обычно покупает в этот час; айтем × контекст — CTR товара в это время.
Сетка почти всегда покрывает существенное, и её удобно проговаривать вслух как структуру ответа.
Что такое каннибализация поверхностей и как с ней быть?
Ситуация, когда две поверхности показывают одному пользователю пересекающийся ассортимент, и их приросты не складываются. Если каждая изолированно даёт +10, то при половинном пересечении вместе они дадут +15, а не +20; при полном — +10, то есть вторая не добавляет ничего.
Классическая ловушка отчётности: команды честно показывают приросты своих поверхностей, а общий оборот не растёт. Лечится метриками на уровне пользователя, а не поверхности, и аплифт-постановкой — покупка на этой поверхности относительно покупки вообще. Причём для аплифтов нужен полноценный общий слой признаков.
Как сделать онбординг холодного пользователя?
Показать топ популярного, дать выбрать — и дальше ключевой приём: удалить из выборки всех пользователей, которым выбранное тоже нравилось, и перестроить топ.
Зачем: без удаления топ почти не меняется, и следующий вопрос не приносит новой информации. В симуляции на пяти вкусовых группах наивная схема пять раз подряд спрашивает про одну и ту же группу — покрыта 1 из 5; с удалением покрываются все 5.
Общая формулировка: мы выбираем следующий вопрос так, чтобы он максимально делил оставшуюся неопределённость, а не так, чтобы он был самым популярным.
Что меняется, когда в системе появляются авторы?
Система перестаёт быть двусторонней и становится трёхсторонней. У авторов свои ожидания: они рассчитывают на показы, и их отсутствие вызывает жалобы. Инструменты — item exploration внутри модели, PID-контроллеры для стабилизации объёма показов, явные квоты.
И следствие, о котором забывают: авторам платят за показы, значит появляется фрод — боты и накрутка CTR. Таргет и целевая метрика должны быть антифрод-aware, иначе система будет добросовестно оптимизировать накрутку.
Как разложить бюджет ответа по стадиям?
Порядок величин при бюджете 300 мс: сеть и разбор 20, профиль 30, кандидатогенерация 40, фильтрация 15, признаки 60, ранжирование 80, переранжирование 25, ответ 20 — итого 290, запас 10 мс.
Два вывода. Первый: запас уйдёт на хвост распределения, поэтому бюджет держат по p99, а не по среднему. Второй: признаки и ранжирование съедают 47% бюджета, и ускорение искать надо сначала там, а не в кандидатогенерации, куда смотрят инстинктивно.
Шпаргалка одним экраном
Закон Голла
Простое работающее → критика → полное. Простое решение — инструмент понимания.
Пять шагов
Цель → ограничения → техника → ML → эксперимент. И второй проход.
Первое число
Порог перебора ≈ 75 000 кандидатов. Ниже — воронка не нужна и не полезна.
Редкое событие
n ∝ (1−p)/p. 0.5% против 40% — в 133 раза больше трафика. Отсюда прокси.
Сетка 3+3
Пользователь, айтем, контекст — по отдельности и попарно. Покрывает существенное.
Каннибализация
При полном пересечении вторая поверхность даёт ноль. Мерить по пользователю.
Онбординг
Удалять тех, кому зашло: 5 групп из 5 вместо 1 из 5.
Бюджет
Признаки и ранжирование — 47%. Держать по p99, а не по среднему.
Первоисточники
- J. Gall. Systemantics: How Systems Really Work and How They Fail, 1975 — источник закона Голла.
- C. Gomez-Uribe, N. Hunt. The Netflix Recommender System: Algorithms, Business Value, and Innovation, TMIS 2015 — карусельный интерфейс и связь с бизнес-целями.
- H. Steck et al. Deep Learning for Recommender Systems: A Netflix Case Study, AI Magazine 2021.
- D. Sculley et al. Hidden Technical Debt in Machine Learning Systems, NeurIPS 2015.
- R. Kohavi, D. Tang, Y. Xu. Trustworthy Online Controlled Experiments, Cambridge 2020 — прокси-метрики, золотые наборы экспериментов и метрики-антагонисты.
- Числа главы:
_tools/design_demo.pyв этом репозитории.
Девятнадцать глав шли по дорожке запроса: от постановки задачи и измерения — через кандидатогенерацию, ранжирование, работу с последовательностями и выдачей — к инженерии и обратно к проектированию.
Если из всего этого стоит унести одну мысль, то такую: в рекомендациях почти каждое техническое решение выводится из ограничения, а не из моды. Воронка — из бюджета задержки. Факторизация — из разреженности. Двухбашенность — из числа кандидатов. Стохастичность выдачи — из потребности мерить. Хороший ответ на собеседовании отличается от плохого не набором названий, а тем, называет ли человек ограничение прежде решения.
Дальше — повторение по вопросам, виджеты и разбор открытого кода реальной ленты, где всё это видно в работающем виде.