сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · Классический ML

Метрики ранжирования и рекомендаций

Зачем это спрашивают

Если в резюме есть поиск или рекомендации, вопрос «как ты меряешь качество ранжирования» придёт обязательно. Проверяют две вещи: мыслишь ли ты в терминах верхушки списка и понимаешь ли, что офлайн-метрики на исторических кликах смещены.

Типовые формулировки: «чем NDCG лучше precision@K?», «офлайн вырос, катим в прод?», «как понять, что рекомендации не скатились в попсу?».

// Слова про смещение логов, сказанные к месту, экономят десять минут доказательств компетентности: их говорят те, кто уже видел, как офлайн-прирост умирает на проде.

Почему у всех метрик хвостик @K

В ранжировании задача другая, чем в классификации: не «угадай метку», а «расставь по порядку». И пользователь смотрит не весь порядок, а первые пять-десять позиций из пятидесяти тысяч. Поэтому метрики считают только по первым K результатам и пишут это прямо в названии: Precision@10, Recall@10.

Precision@10 - сколько из десяти показанных оказались релевантными. Recall@10 - какую долю всех релевантных для юзера объектов мы уместили в десятку.

У Recall@K есть встроенная несправедливость, о которой полезно помнить: у пользователя с тремя подходящими товарами Recall@10 может быть 1.0, а у пользователя с двумя сотнями подходящих - максимум 0.05, хотя выдача у него отличная. Сравнивать Recall@K между такими юзерами бессмысленно.

// K выбирают не от балды, а по продукту: сколько позиций реально видно на первом экране. У мобильной ленты это 3-5, у поиска - 10, у почтового автосаджеста - 1.

релевантность
оценка «подходит ли объект запросу». Бывает бинарной (да/нет) или градуированной, например от 0 до 3 - её ставят асессоры или выводят из поведения

NDCG: считаем руками

У Precision@K есть слепое пятно: ей всё равно, стоит релевантный результат первым или десятым. Пользователю не всё равно совсем. NDCG (normalized discounted cumulative gain) чинит именно это - и попутно умеет работать с градуированной релевантностью, а не с одним лишь «да/нет».

Сначала DCG - сумма полезностей, где вклад каждой позиции поделен на логарифм её номера: чем ниже позиция, тем меньше она весит. Пусть выдача из трёх результатов с оценками 3, 0 и 2. DCG = 3/log₂2 + 0/log₂3 + 2/log₂4 = 3/1 + 0 + 2/2 = 4.

Дальше нормировка. Идеальный порядок для тех же оценок - 3, 2, 0, его DCG = 3/1 + 2/1.585 + 0 = 4.26. Это IDCG (ideal discounted cumulative gain), потолок для конкретного запроса. NDCG = 4/4.26 = 0.94. Нормировка нужна, чтобы складывать запросы между собой: у одного релевантных пять, у другого один, и сырые DCG у них несопоставимы, а нормированные - вполне.

// Родственники: MRR (mean reciprocal rank) - обратный ранг первого попадания, 1.0 если нашли сразу, 0.5 если на второй позиции, 0.25 если на четвёртой; берут там, где нужен один правильный ответ побыстрее (автосаджест, вопрос-ответ). MAP (mean average precision) - для бинарной релевантности, когда важен весь список целиком, а не одна верхушка.

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

Офлайн смещён: клики породила старая система

Офлайн-метрики считаются на исторических логах. Ловушка в том, что логи породила прошлая версия ранжирования, и они несут её отпечаток.

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

Второе - позиционное смещение. Клик по первой позиции складывается из двух вещей: объект правда подошёл, и объект просто был первым. Люди кликают верх независимо от содержимого. Сырые клики как метки - это уже искажённые метки, и обучение на них закрепляет искажение.

// Лечится частично: рандомизировать порядок на маленькой доле трафика, чтобы получать несмещённые данные, и перевзвешивать клики на обратную вероятность показа - IPS (inverse propensity scoring). Полностью - только онлайном.

Чего не видно в NDCG

Точность попадания - не единственное свойство хороших рекомендаций. Рядом живут coverage (какая доля каталога попадает хоть в чьи-то рекомендации), разнообразие внутри выдачи и способность подсунуть неожиданное, но уместное.

Почему это не эстетство: модель, которая всем показывает топ-100 бестселлеров, получает прекрасный NDCG - бестселлеры и правда кликают чаще - и медленно убивает продукт. Юзеру нечего открывать, длинный хвост каталога не продаётся, через месяц он перестаёт заходить. Метрика при этом всё время росла.

Финальный судья - онлайн-эксперимент по продуктовым метрикам: CTR (click-through rate), конверсия, возвраты, удержание. Офлайн-прирост в них не обязан конвертироваться, и довольно часто не конвертируется.

// Рабочая рамка простая: офлайн - дешёвый фильтр кандидатов на A/B, онлайн - решение о выкатке. Перепутать порядок дорого: выкатишь по офлайну, а узнаешь через месяц по удержанию.

coverage
доля каталога, которую система вообще кому-нибудь показывает. Низкий coverage означает, что модель крутит один и тот же узкий набор
guardrail
метрика-ограничитель. Её не улучшают, за ней следят, чтобы она не просела, пока улучшают целевую

Как отвечать: «Офлайн NDCG вырос на 5 процентов - катим в прод?»

Пока нет. Офлайн считался на кликах старой системы, а там сидят два смещения - обратная связь и позиционное, поэтому прирост может не пережить встречу с реальным трафиком. Сначала смотрю, за счёт чего он вырос: если модель просто сузилась до популярного, coverage и разнообразие просядут, и это я держу как guardrail. Потом онлайн-эксперимент по продуктовым метрикам - CTR, конверсия, удержание. Офлайн для меня фильтр кандидатов на A/B, а не основание для выкатки.

Названы оба смещения, guardrail-свойства и правильный порядок решений. Так рассуждает человек, который уже обжигался на «офлайн вырос, онлайн упал».

На чём валят

  • Мерить рекомендации accuracy или AUC (area under the curve) по всему каталогу - задача про верхушку списка, метрики обязаны быть @K.
  • Сравнивать NDCG между периодами или датасетами с разной базой релевантных: нормировка спасает внутри запроса, но не между разными наборами данных.
  • Учить модель на сырых кликах и не замечать позиционного смещения - выучится любовь к первой позиции.
  • Максимизировать одну точность: разнообразие деградирует, выдача сходится в попсу, удержание падает позже и незаметно.
  • Считать Recall@K сопоставимым между юзерами с тремя и с двумя сотнями релевантных объектов.

Проверьте себя

Пять вопросов из банка по этой подтеме. Всего их 10, остальные разбираются в тренажёре.

  1. #ranking_recsys_metrics1 / 5
    Пользователю важен один хороший ответ повыше. Какая метрика прямо про это?
    A)Полноту (recall), посчитанную по всей выдаче, без учёта позиции первого релевантного
    B)Средняя релевантность всех показанных элементов
    C)MRR: усредняет 1/позиция первого релевантного результата
    D)Общее число релевантных документов в базе
    показать ответ и разбор
    +C)MRR: усредняет 1/позиция первого релевантного результата

    // разбор: MRR = среднее по запросам от 1/(позиция первого релевантного): первый на 1-м месте даёт 1, на 2-м — 0.5, на 3-м — 0.33. Метрика фокусируется ровно на том, как быстро пользователь находит первый годный ответ, — уместна для навигационного поиска, QA, автоподсказок. Она игнорирует остальные релевантные (в отличие от MAP/NDCG), поэтому не годится, когда важна вся выдача.

  2. #ranking_recsys_metrics2 / 5
    Чем precision@k отличается от recall@k в рекомендациях топ-k?
    A)Это одна и та же метрика: параметр k лишь ограничивает длину рассматриваемой выдачи
    B)Precision@k делит на общее число релевантных, а recall@k — на k
    C)precision@k — доля релевантных среди k; recall@k — доля от ВСЕХ релевантных пользователя
    D)Обе меряют порядок элементов внутри топ-k
    показать ответ и разбор
    +C)precision@k — доля релевантных среди k; recall@k — доля от ВСЕХ релевантных пользователя

    // разбор: Precision@k = (релевантные в топ-k)/k — насколько чиста выдача. Recall@k = (релевантные в топ-k)/(все релевантные пользователя) — какую долю нужного мы показали. При маленьком k recall почти всегда низок (всего не уместить), а precision может быть высок. Они в противофазе, и выбор зависит от продукта: витрина из 5 карточек — про precision, «не потерять важное» — про recall.

  3. #ranking_recsys_metrics3 / 5
    Что показывает hit-rate (доля попаданий) в оценке рекомендаций?
    A)Доля случаев (юзеров/сессий), где хотя бы один релевантный попал в топ-k
    B)Точную позицию первого релевантного элемента у каждого отдельного пользователя в списке
    C)Среднюю релевантность всех элементов каталога
    D)Скорость генерации рекомендаций сервисом
    показать ответ и разбор
    +A)Доля случаев (юзеров/сессий), где хотя бы один релевантный попал в топ-k

    // разбор: Hit-rate@k — доля случаев, где хотя бы один релевантный (например, реально купленный) элемент оказался среди топ-k рекомендаций. Это грубая, но наглядная продуктовая метрика «попали или нет». Она не различает, релевантный был первым или последним в топ-k (для этого — NDCG/MRR) и не учитывает, сколько именно релевантных поймано (recall@k). Часто идёт в связке с coverage/diversity.

  4. #ranking_recsys_metrics4 / 5
    Рекомендатель по офлайн-метрикам точности отличный, но пользователям выдача кажется однообразной. Какой класс метрик это ловит?
    A)Только accuracy — её и надо повышать дальше, ведь именно она отражает качество выдачи
    B)Beyond-accuracy: coverage, diversity, novelty/serendipity — ловят однообразие
    C)Латентность и RPS сервиса рекомендаций
    D)Только precision@k на исторических кликах
    показать ответ и разбор
    +B)Beyond-accuracy: coverage, diversity, novelty/serendipity — ловят однообразие

    // разбор: Оптимизация одной точности часто скатывается к показу популярного и уже знакомого — офлайн-метрики растут, а опыт беднеет. Beyond-accuracy метрики измеряют другое: coverage — какую долю каталога вообще рекомендуем; diversity — насколько элементы в выдаче непохожи; novelty/serendipity — показываем ли неожиданно уместное. Их балансируют с точностью, потому что бизнес-эффект (удержание, LTV) зависит и от разнообразия.

  5. #ranking_recsys_metrics5 / 5
    Почему метрики рекомендаций считают на срезе top-k, а не по всей выдаче?
    A)Пользователь видит только первые позиции — дальше выдача не влияет на опыт
    B)Полную выдачу невозможно отсортировать за приемлемое время
    C)Метрики по всей выдаче требуют разметки релевантности всего каталога
    D)Так принято в библиотеках, и иначе метрики не считаются
    показать ответ и разбор
    +A)Пользователь видит только первые позиции — дальше выдача не влияет на опыт

    // разбор: Смысл среза — совпасть с тем, что человек реально увидит: первый экран, первые десять позиций. Ошибка на 500-й позиции опыта не меняет, а в среднем по всей выдаче она размоется вместе с полезным сигналом. Поэтому k выбирают не из удобства, а из продукта: сколько карточек влезает в экран и сколько люди реально пролистывают.

дальше

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

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