← к модулям

Метрики рекомендаций простым языком

Как понять, хороша ли рекомендательная система. Метрики ниже считаются в этом приложении для каждой модели — открой любой модуль и сравни. Строгие формулы спрятаны под «Строгая формула», чтобы простой текст оставался читаемым.

Сначала — три понятия

Top-K. Система показывает не всё, а короткий список из K лучших (например, 10). Все метрики смотрят именно на этот список.

Релевантный айтем. То, что пользователю реально зашло. У нас «релевантно» = пользователь поставил оценку не ниже порога (по умолчанию ≥ 4 из 5). Чтобы проверить модель честно, мы прячем последнее взаимодействие каждого пользователя (leave-last-out) и смотрим, угадает ли его модель. Подробно — на странице «Как устроен эксперимент».

Почему значения такие маленькие. Мы прячем 1 айтем и ищем его среди ~9700. Угадать его в топ-10 трудно, поэтому precision/recall в сотых долях — это нормально. Важны не абсолютные числа, а сравнение моделей между собой.

Какую боль решает каждая метрика

Обычная accuracy / rating RMSE не отвечает на главный вопрос — хорош ли топ-10. Каждая ранговая метрика ловит свою боль:

  • Precision@K — сколько мусора в выдаче.
  • Recall@K / HitRate@K — нашли ли нужный айтем вообще.
  • MRR — насколько высоко стоит первый правильный.
  • NDCG — насколько хорошо упорядочены релевантные айтемы с учётом позиции (и, в общем случае, силы релевантности).
  • Coverage / Novelty / Diversity — не схлопнулась ли система в популярную голову.

Поэтому смотрят набор метрик под конкретный продуктовый вопрос, а не одно число.

Метрики точности

Отвечают на вопрос «угадали ли мы то, что человеку нужно, и насколько высоко в списке».

Precision@K

0…1, выше — лучше

Какая доля из K показанных рекомендаций оказалась релевантной.

💡 «Из 10 предложенных фильмов сколько реально зашли?»

Пример: Показали 10, угадали 2 → precision@10 = 2/10 = 0.2.

(попаданий в топ-K) / K

Строгая формула
P@K=1Ki=1Kreli\text{P@}K = \frac{1}{K}\sum_{i=1}^{K} \mathrm{rel}_i

reli=1\mathrm{rel}_i = 1, если айтем на позиции ii релевантен, иначе 0. Делим на KK — поэтому при одном спрятанном айтеме максимум 1/K1/K.

⚠️ Не учитывает порядок и сколько релевантного вы пропустили. При одном спрятанном айтеме максимум = 1/K, поэтому значения крошечные.

Recall@K

0…1, выше — лучше

Какую долю всех релевантных айтемов вы поймали в топ-K.

💡 «Из всего, что человеку понравилось бы, сколько мы показали?»

Пример: Релевантных всего 4, в топ-10 попали 2 → recall@10 = 2/4 = 0.5.

(попаданий в топ-K) / (всего релевантных)

Строгая формула
R@K=1Reli=1Kreli\text{R@}K = \frac{1}{|\mathrm{Rel}|}\sum_{i=1}^{K} \mathrm{rel}_i

Rel|\mathrm{Rel}| — сколько всего релевантных айтемов у пользователя. У нас спрятан ровно один, поэтому Rel=1|\mathrm{Rel}|=1 и recall совпадает с hit-rate.

⚠️ Если спрятан 1 айтем (как у нас, leave-last-out), recall = hit-rate (0 или 1 на пользователя).

Hit-rate@K

0…1, выше — лучше

Доля пользователей, у которых хотя бы одно попадание в топ-K.

💡 «У скольких людей мы угадали хоть что-то?»

Пример: У 41 из 1000 пользователей был хит → hit-rate = 0.041.

(пользователей с ≥1 попаданием) / (всего пользователей)

Строгая формула
HR@K=1UuU1[hitsu@K>0]\text{HR@}K = \frac{1}{|U|}\sum_{u \in U} \mathbf{1}\big[\,\text{hits}_u@K > 0\,\big]

Усреднение идёт по множеству оцениваемых пользователей UU. Индикатор 1[]\mathbf{1}[\cdot] равен 1, если хотя бы один релевантный айтем попал в топ-K.

⚠️ Самая «прощающая» метрика: одного угаданного айтема достаточно. Не различает «угадал на 1-м месте» и «на 10-м».

MRR (Mean Reciprocal Rank)

0…1, выше — лучше

Насколько высоко в списке стоит первый релевантный айтем.

💡 «Как быстро человек дойдёт до чего-то нужного, листая сверху?»

Пример: Первый хит на 2-м месте → reciprocal rank = 1/2. Усредняем по людям.

среднее по пользователям от 1 / (позиция первого попадания)

Строгая формула
MRR=1UuU1ranku\text{MRR} = \frac{1}{|U|}\sum_{u \in U} \frac{1}{\mathrm{rank}_u}

ranku\mathrm{rank}_u — позиция первого релевантного айтема в списке пользователя uu (если попаданий нет, слагаемое = 0).

⚠️ Смотрит только на первый хит — что дальше, неважно.

MAP@K (Mean Average Precision)

0…1, выше — лучше

Точность с учётом позиций всех релевантных айтемов в списке.

💡 «Награждаем за то, что нужное стоит выше и его много».

Пример: Хиты на позициях 2 и 4 → average precision = среднее (1/2 и 2/4) = 0.5.

среднее precision на каждой позиции с попаданием, усреднённое по людям

Строгая формула
AP@K=1Reli=1KP@ireli,MAP=1UuAP@Ku\text{AP@}K = \frac{1}{|\mathrm{Rel}|}\sum_{i=1}^{K} \text{P@}i \cdot \mathrm{rel}_i, \quad \text{MAP} = \frac{1}{|U|}\sum_{u} \text{AP@}K_u

Для каждой позиции ii с попаданием берём precision до этой позиции, усредняем — это AP одного пользователя; MAP — среднее AP по всем. При одном спрятанном айтеме MAP совпадает с MRR.

⚠️ Самая «полная» из ранговых метрик. При одном спрятанном айтеме MAP совпадает с MRR.

NDCG@K

0…1, выше — лучше

Качество ранжирования: попадания выше ценятся сильнее, нормировано на идеал.

💡 «Угадать на 1-м месте лучше, чем на 10-м» — и это считается математически.

Пример: Хит на позиции 1 даёт 1.0; на позиции 3 — около 0.5.

DCG (сумма 1/log2(позиция+1) по хитам) / идеальный DCG

Строгая формула
DCG@K=i=1Krelilog2(i+1),NDCG@K=DCG@KIDCG@K\text{DCG@}K = \sum_{i=1}^{K} \frac{\mathrm{rel}_i}{\log_2(i+1)}, \quad \text{NDCG@}K = \frac{\text{DCG@}K}{\text{IDCG@}K}

IDCG@K\text{IDCG@}K — это DCG идеального порядка (все релевантные айтемы наверху). Деление на него и есть «нормировка»: 1.0 — идеальный порядок, поэтому значения разных задач сравнимы.

⚠️ Одна из ключевых offline-метрик ранжирования: учитывает и попадание, и позицию. «Нормированная» (N) — значит 1.0 это идеальный порядок.

Beyond-accuracy (за пределами точности)

Точность — не всё. Система может быть «точной», но скучной: всем советовать одни хиты. Эти метрики ловят такие перекосы.

Coverage@K

0…1, выше — обычно лучше

Какую долю всего каталога система вообще когда-либо рекомендует.

💡 «Крутим мы весь каталог или одни и те же хиты всем?»

Пример: Из 9700 фильмов в рекомендациях встретилось 1200 → coverage ≈ 0.12.

(уникальных рекомендованных айтемов) / (всего айтемов)

Строгая формула
Coverage@K=uUtop-K(u)I\text{Coverage@}K = \frac{\big|\bigcup_{u \in U} \text{top-}K(u)\big|}{|I|}

В числителе — объединение всех рекомендованных топ-K по всем пользователям (каждый айтем считается один раз), I|I| — размер каталога.

⚠️ Низкая coverage = popularity bias (всем одно и то же). Высокая сама по себе не означает качество.

Novelty@K

в битах, выше — нишевее

Насколько непопулярны (неожиданны) рекомендованные айтемы.

💡 «Советуем мегахиты, которые и так все знают, или находки?»

Пример: Очень популярный фильм ≈ 1–2 бита; редкий — 8–12 бит.

среднее −log2(доля пользователей, видевших айтем)

Строгая формула
Novelty@K=1Kitop-Klog2p(i)\text{Novelty@}K = \frac{1}{K}\sum_{i \in \text{top-}K} -\log_2 p(i)

p(i)p(i) — доля пользователей, у которых было взаимодействие с айтемом ii. Величина log2p(i)-\log_2 p(i) (само-информация) растёт, когда айтем редкий, и измеряется в битах.

⚠️ Слишком высокая novelty часто = низкая точность (советуем странное). Нужен баланс.

Diversity@K (intra-list)

0…1, выше — разнообразнее

Насколько непохожи айтемы внутри одного списка (у нас — по жанрам).

💡 «В подборке 10 разных вещей или 10 почти одинаковых?»

Пример: Список из одних боевиков → низкая diversity; микс жанров → высокая.

средняя попарная непохожесть (1 − Jaccard по жанрам) в топ-K

Строгая формула
Diversity@K=2K(K1)i<j(1sim(i,j))\text{Diversity@}K = \frac{2}{K(K-1)}\sum_{i<j}\big(1 - \mathrm{sim}(i,j)\big)

Сумма по всем парам айтемов списка; sim(i,j)\mathrm{sim}(i,j) — близость по жанрам (Jaccard: GiGjGiGj\frac{|G_i \cap G_j|}{|G_i \cup G_j|}). Чем меньше пары похожи, тем выше diversity.

⚠️ Контентные модели часто дают низкую diversity (рекомендуют по одному признаку). Слишком высокая — теряется релевантность.

🎚 Покрути порог релевантности

Подними планку «что считается понравившимся» и смотри, как плывут метрики и кто из моделей вырывается вперёд. Это те же числа, что в каждом модуле.

МодельNDCG@10Hit-rate@10Coverage@10
Popularity
Content-based
Item-CF
MF (ALS)

Онлайн-метрики (как меряют в проде)

⚠️ Здесь они не считаются

Все метрики выше — offline: их можно посчитать на исторических данных, что мы и делаем. Метрики ниже — online: им нужен живой трафик и реальные пользователи, поэтому в этом учебном приложении их нет. Но именно по ним принимают решения в продакшене, так что знать их обязательно.

CTR (click-through rate)

Доля показов, по которым кликнули. Базовый сигнал «зацепило ли».

клики / показы

CVR (conversion rate)

Доля показов/кликов, дошедших до целевого действия (покупка, просмотр до конца, подписка).

конверсии / показы (или клики)

Watch / dwell time

Сколько времени человек реально провёл с контентом. Ловит «кликбейт»: клик был, а смотреть не стали.

среднее время взаимодействия на сессию

Retention (DAU/MAU)

Возвращаются ли люди. Главный долгосрочный сигнал здоровья продукта — рекомендации влияют на него косвенно.

активных за день / активных за месяц

Session length / depth

Сколько айтемов человек потребляет за заход и насколько глубоко листает ленту.

среднее число действий / просмотренных позиций за сессию

Revenue / GMV

Деньги: выручка, оборот, доход на показ. В e-commerce обычно и есть конечная цель.

выручка на пользователя / на сессию / на 1000 показов

Guardrail-метрики

«Предохранители», которые не должны просесть, пока мы гонимся за основной метрикой: жалобы, отписки, разнообразие, доля длинного хвоста, время загрузки.

следим, чтобы не упали ниже порога

Почему offline-числа врут, и как меряют по-настоящему: A/B, gap, biased feedback

Offline↔online gap. Хорошая offline-метрика не гарантирует пользу в проде. Offline мы переигрываем прошлое (где система уже на что-то влияла), а в проде рекомендации меняют поведение людей. Поэтому offline — это фильтр-ориентир, а финальное решение — за онлайном.

A/B-тест. Случайно делим пользователей на контроль (старая модель) и тест (новая) и сравниваем целевую метрику со статистикой значимости. Золотой стандарт, но дорогой и медленный.

Interleaving. Смешиваем выдачу двух моделей в одном списке и смотрим, чьи айтемы кликают чаще. Чувствительнее A/B и требует меньше трафика, но применим в основном к ранжированию.

Delayed / biased feedback. Отклик приходит с задержкой (покупку видно через дни) и смещён: мы наблюдаем клики только по тому, что сами показали (exposure / position bias). Поэтому «нет клика» ≠ «не понравилось» — это надо корректировать (debiasing, counterfactual-оценка).

Long-term value. Можно накрутить сиюминутный CTR кликбейтом и потерять пользователя через месяц. Зрелые системы оптимизируют долгосрочную ценность (retention, LTV), а не только мгновенный клик — поэтому и нужны guardrail-метрики.

Что важно помнить

• На нашем датасете прячется 1 айтем, поэтому recall = hit-rate и MAP = MRR. На данных с несколькими релевантными айтемами они разойдутся.

Одна метрика — разные реализации. «NDCG@10» в разных библиотеках считают по-разному (обработка ничьих, усреднение, что считать релевантным). Сравнивать вслепую числа из двух источников нельзя — только модели внутри одного протокола, как здесь.

• Высокая offline-точность ≠ польза в проде. Реальное качество проверяют A/B-тестами на живых пользователях — offline-метрики лишь ориентир.

• Хорошая система балансирует точность и beyond-accuracy: не только угадывает, но и не загоняет всех в «пузырь» из одних и тех же хитов.