Как посчитать coverage в recsys в SQL

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

Зачем coverage

Recsys может иметь высокий precision/recall, но рекомендовать только top-100 популярных товаров. Coverage показывает, насколько широко используется catalog. Низкий coverage = «бестселлер bias», новые/нишевые товары не получают exposure. Это убивает long-tail revenue.

Catalog coverage

catalog_coverage = unique_items_recommended / total_items_in_catalog

Range: [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;
Закрепи формулу recsys coverage в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать recsys coverage в Telegram

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. Считайте отдельно.

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

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 в краткосрочной.