Как посчитать Mean Reciprocal Rank в SQL

Закрепи формулу mean reciprocal rank в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать mean reciprocal rank в Telegram

Зачем MRR

Mean Reciprocal Rank (MRR) — стандартная метрика для search и recsys, когда важна позиция первого релевантного результата. Применяется для вопрос-ответных систем, поиска, navigation. MRR = 1 если каждый раз первый результат правильный. MRR = 0 если ни одного релевантного результата нет.

Не путать с MRR в SaaS — Monthly Recurring Revenue. Контекст обычно проясняет, какой MRR имеется в виду.

Формула

MRR = (1/N) × Σ (1 / rank_first_relevant_i)
  • N — число запросов
  • rank_first_relevant_i — позиция первого релевантного документа в результатах для запроса i

Если для запроса нет релевантного — 1/rank = 0.

MRR в SQL

Данные: для каждого query_id есть doc_id, rank (позиция от 1), is_relevant.

WITH first_relevant AS (
    SELECT
        query_id,
        MIN(rank) AS first_relevant_rank
    FROM search_results
    WHERE is_relevant = TRUE
      AND query_date >= CURRENT_DATE - INTERVAL '30 days'
    GROUP BY query_id
),
all_queries AS (
    SELECT DISTINCT query_id
    FROM search_results
    WHERE query_date >= CURRENT_DATE - INTERVAL '30 days'
)
SELECT
    AVG(1.0 / fr.first_relevant_rank) AS mrr,
    COUNT(fr.query_id) AS queries_with_hit,
    COUNT(aq.query_id) AS total_queries,
    COUNT(fr.query_id)::NUMERIC * 100 / NULLIF(COUNT(aq.query_id), 0) AS hit_rate_pct
FROM all_queries aq
LEFT JOIN first_relevant fr USING (query_id);

LEFT JOIN чтобы учесть запросы без релевантных результатов (вклад 0 в MRR).

MRR@k

Часто считают только top-k результатов:

WITH all_queries AS (
    SELECT DISTINCT query_id FROM search_results
    WHERE query_date >= CURRENT_DATE - INTERVAL '30 days'
),
first_relevant_in_top_k AS (
    SELECT
        query_id,
        MIN(rank) AS first_relevant_rank
    FROM search_results
    WHERE is_relevant = TRUE
      AND rank <= 10
    GROUP BY query_id
)
SELECT
    AVG(COALESCE(1.0 / first_relevant_rank, 0)) AS mrr_at_10
FROM all_queries aq
LEFT JOIN first_relevant_in_top_k fr USING (query_id);

COALESCE(1/rank, 0) — для запросов без релевантного в top-10 вклад = 0.

Закрепи формулу mean reciprocal rank в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать mean reciprocal rank в Telegram

Сравнение моделей

SELECT
    model_name,
    AVG(COALESCE(1.0 / first_relevant_rank, 0)) AS mrr,
    COUNT(*) AS queries
FROM model_evaluations
WHERE eval_date >= CURRENT_DATE - INTERVAL '7 days'
GROUP BY model_name
ORDER BY mrr DESC;

Модели с разницей MRR ≥ 0.02 обычно стат-значимо различимы на 1000+ запросов.

Частые ошибки

Ошибка 1. Не учитывать запросы без релевантных результатов. Если просто фильтровать, MRR завышен. LEFT JOIN всех запросов + 0 для no-hit.

Ошибка 2. Использовать last relevant, не first. MRR — про first. MAP — про all relevant. Разные метрики.

Ошибка 3. Считать MRR без cutoff k. В реальности юзер не доскроллит до position 100. MRR@10 или MRR@5 — практичнее.

Ошибка 4. Сравнивать MRR на разных query sets. MRR зависит от сложности запросов. Сравнения — только on same evaluation set.

Ошибка 5. Confidence interval. MRR через bootstrap (CI [0.42, 0.48]). Без CI разница 0.01 может быть шумом.

Связанные темы

FAQ

MRR vs NDCG?

MRR — позиция first relevant. NDCG — учитывает все позиции с весами. NDCG богаче, MRR проще.

Какой MRR хороший?

В web search: 0.6-0.8 stat-of-the-art. В recsys 0.3-0.5 норма.

MRR на multi-relevant docs?

Берёт только первый. Если важны все — MAP или NDCG.

MRR на бинарной релевантности?

Да, прямо подходит.

Можно ли MRR for recsys feed?

Да, особенно для top-1 cardinality (одна рекомендация в фокусе).