Как посчитать coverage в recsys в SQL
Содержание:
Зачем coverage
Recsys может иметь высокий precision/recall, но рекомендовать только top-100 популярных товаров. Coverage показывает, насколько широко используется catalog. Низкий coverage = «бестселлер bias», новые/нишевые товары не получают exposure. Это убивает long-tail revenue.
Catalog coverage
catalog_coverage = unique_items_recommended / total_items_in_catalogRange: [0, 1]. 1 = каждый товар хотя бы раз рекомендован. На практике 0.3-0.6 у production recsys.
User coverage
user_coverage = users_with_recommendations / total_active_usersНизкий user coverage = recsys не работает для новых юзеров (cold start) или специфических сегментов.
Coverage в SQL
WITH recommendations AS (
SELECT DISTINCT product_id, user_id
FROM recsys_recommendations
WHERE rec_date >= CURRENT_DATE - INTERVAL '30 days'
),
catalog AS (
SELECT DISTINCT product_id FROM products
WHERE status = 'active'
),
active_users AS (
SELECT DISTINCT user_id FROM users
WHERE last_activity >= CURRENT_DATE - INTERVAL '30 days'
)
SELECT
(SELECT COUNT(DISTINCT product_id) FROM recommendations) AS recommended_items,
(SELECT COUNT(*) FROM catalog) AS total_items,
(SELECT COUNT(DISTINCT product_id) FROM recommendations)::NUMERIC * 100
/ NULLIF((SELECT COUNT(*) FROM catalog), 0) AS catalog_coverage_pct,
(SELECT COUNT(DISTINCT user_id) FROM recommendations) AS users_with_recs,
(SELECT COUNT(*) FROM active_users) AS total_users,
(SELECT COUNT(DISTINCT user_id) FROM recommendations)::NUMERIC * 100
/ NULLIF((SELECT COUNT(*) FROM active_users), 0) AS user_coverage_pct;Long-tail problem
WITH item_rec_counts AS (
SELECT product_id, COUNT(*) AS recommended_times
FROM recsys_recommendations
WHERE rec_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY product_id
),
deciles AS (
SELECT
product_id,
recommended_times,
NTILE(10) OVER (ORDER BY recommended_times DESC) AS decile
FROM item_rec_counts
)
SELECT
decile,
COUNT(*) AS items,
SUM(recommended_times) AS total_recs,
SUM(recommended_times) * 100.0 / SUM(SUM(recommended_times)) OVER () AS pct_of_total
FROM deciles
GROUP BY decile
ORDER BY decile;Если top-decile получает 80% recs — серьёзный bestseller bias. 50% — норма. < 40% — диверсифицированная система.
Частые ошибки
Ошибка 1. Coverage без context. Coverage 30% — это много или мало? Зависит от catalog size: 30% от 10k товаров (3000) — норм. От 100 — катастрофа.
Ошибка 2. Считать coverage на impressions vs clicks. Impression coverage всегда выше click coverage. Зафиксируйте определение.
Ошибка 3. Игнорировать новые товары. Новый товар без data → не рекомендуется. Это cold-start problem, не отсутствие coverage.
Ошибка 4. Coverage + precision: trade-off. Можно искусственно поднять coverage random-injecting товаров. Precision упадёт. Balance важен.
Ошибка 5. Coverage один на recsys. Разные surfaces (homepage, item page, cart) имеют разный coverage. Считайте отдельно.
Связанные темы
- Как посчитать NDCG в SQL
- Как посчитать diversity в recsys в SQL
- Как посчитать precision-recall в SQL
- Как посчитать cosine similarity в SQL
FAQ
Какой coverage хороший?
Catalog: 40-70% для balanced recsys. User: 95%+ для активных.
Coverage низкий из-за cold start?
Часто. Решение — content-based fallback для new items.
Coverage и diversity — одно?
Близко. Diversity — насколько разные товары в одной рекомендации. Coverage — global catalog usage.
Можно ли coverage на feed?
Да: % unique items в personal feed за day/week.
Тестировать coverage в A/B?
Да — компромисс с CTR. Часто +coverage = -CTR в краткосрочной.