Как посчитать MAP@k в SQL
Содержание:
Зачем MAP
Mean Average Precision (MAP) — стандартная метрика для рейтинговых систем с множественными релевантными документами. В отличие от MRR (только first), MAP усредняет precision на каждой позиции, где релевантный документ. MAP высокий = и first, и middle, и all relevant документы на верху.
Применяется в search, информационный retrieval, рейтинг продуктов в e-com.
Average Precision
Для одного запроса:
AP@k = Σ over relevant ranks r ≤ k of (precision@r) / total_relevantТо есть: для каждой позиции, где есть релевантный, считаем precision до этой позиции (включая её). Усредняем.
MAP@k в SQL
WITH ranked AS (
SELECT
query_id,
rank,
is_relevant,
SUM(CASE WHEN is_relevant THEN 1 ELSE 0 END) OVER (
PARTITION BY query_id ORDER BY rank
) AS relevant_so_far,
rank AS k
FROM search_results
WHERE query_date >= CURRENT_DATE - INTERVAL '30 days'
AND rank <= 10
),
precision_at_relevant AS (
SELECT
query_id,
rank,
relevant_so_far::NUMERIC / rank AS precision_at_rank
FROM ranked
WHERE is_relevant = TRUE
),
ap_per_query AS (
SELECT
query_id,
SUM(precision_at_rank) / NULLIF(COUNT(*), 0) AS ap
FROM precision_at_relevant
GROUP BY query_id
),
all_queries AS (
SELECT DISTINCT query_id FROM search_results
WHERE query_date >= CURRENT_DATE - INTERVAL '30 days'
)
SELECT
AVG(COALESCE(ap, 0)) AS map_at_10,
COUNT(*) AS total_queries
FROM all_queries
LEFT JOIN ap_per_query USING (query_id);Запросы без релевантных получают AP = 0.
MAP vs MRR
WITH ranked AS (
SELECT
query_id, rank, is_relevant,
SUM(CASE WHEN is_relevant THEN 1 ELSE 0 END) OVER (PARTITION BY query_id ORDER BY rank) AS relevant_so_far
FROM search_results
WHERE rank <= 10
),
ap_per_query AS (
SELECT
query_id,
SUM(relevant_so_far::NUMERIC / rank) FILTER (WHERE is_relevant)
/ NULLIF(SUM(CASE WHEN is_relevant THEN 1 ELSE 0 END), 0) AS ap
FROM ranked
GROUP BY query_id
),
first_relevant AS (
SELECT query_id, MIN(rank) AS first_rank
FROM ranked
WHERE is_relevant
GROUP BY query_id
)
SELECT
AVG(1.0 / fr.first_rank) AS mrr,
AVG(COALESCE(ap.ap, 0)) AS map
FROM first_relevant fr
LEFT JOIN ap_per_query ap USING (query_id);MAP всегда ≤ MRR (потому что average precision учитывает и положения после first relevant).
MAP vs NDCG
| MAP | NDCG | |
|---|---|---|
| Бинарная релевантность | Yes | Можно |
| Градированная (0/1/2/3) | Нет | Yes |
| Позиция важна? | Yes | Yes (log) |
| Применение | Информационный retrieval | Web search, recsys |
Для бинарных задач — MAP проще. Для градированных — NDCG.
Частые ошибки
Ошибка 1. MAP на single relevant. Если у каждого запроса 1 релевантный, MAP = MRR. Используйте MRR проще.
Ошибка 2. Считать AP на запросах без релевантных. Деление на 0. Эти запросы AP = 0 явно.
Ошибка 3. precision@rank на total documents. Precision = relevant_so_far / rank. Не «relevant / all in DB».
Ошибка 4. Игнорировать total_relevant unbounded.
Стандартная MAP делит на total_relevant. Если есть docs выше top-k, надо учесть. AP@k без normalization — иногда другая формула.
Ошибка 5. CI без bootstrap. MAP variance высокая. Bootstrap 1000 запросов для CI.
Связанные темы
- Как посчитать Mean Reciprocal Rank в SQL
- Как посчитать NDCG в SQL
- Как посчитать precision-recall в SQL
- Как посчитать Hit Rate@k в SQL
FAQ
MAP@k vs MAP?
MAP@k обрезает по top-k. MAP — на весь ранкинг.
Какой MAP хороший?
В TREC web search 0.4-0.6 — strong. В recsys 0.1-0.3 — норма.
MAP для бинарной vs градированной?
Лучше для бинарной. Для градированной — NDCG.
MAP считается на one query или multi?
AP — на один запрос. MAP — среднее AP по всем запросам.
Можно ли MAP в feed recsys?
Да, если у каждого юзера есть ground truth relevant items.