Как посчитать Mean Reciprocal Rank в SQL
Зачем 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.
Сравнение моделей
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 может быть шумом.
Связанные темы
- Как посчитать NDCG в SQL
- Как посчитать precision-recall в SQL
- Как посчитать hit rate в SQL
- Ranking-метрики NDCG на собесе DS
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 (одна рекомендация в фокусе).