LESSON

推薦の評価指標

「モデルを作っただけでは意味がない。推薦の良し悪しをどう測るかが重要だ。」

田中VPoEが評価ダッシュボードを開く。

「推薦システムの評価は通常の分類・回帰とは異なる。ランキングの質を測る指標を理解しよう。」

オフライン評価指標の全体像

指標何を測るか値域高いほど良い
Precision@K上位K件中の適合率0〜1Yes
Recall@K全正解中のK件以内での再現率0〜1Yes
NDCG@K順位を考慮した適合度0〜1Yes
MAP@K平均適合率の平均0〜1Yes
Hit Rate@K1件でも正解を含むユーザーの割合0〜1Yes
Coverage推薦できるアイテムのカバー率0〜1Yes

Precision@K と Recall@K

import numpy as np

def precision_at_k(recommended, relevant, k):
    """上位K件の適合率"""
    rec_k = recommended[:k]
    hits = len(set(rec_k) & set(relevant))
    return hits / k

def recall_at_k(recommended, relevant, k):
    """上位K件の再現率"""
    rec_k = recommended[:k]
    hits = len(set(rec_k) & set(relevant))
    return hits / len(relevant) if relevant else 0

# 例: 推薦10件、正解5件
recommended = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']
relevant = ['A', 'C', 'F', 'K', 'L']

print(f"Precision@5: {precision_at_k(recommended, relevant, 5)}")  # 2/5 = 0.4
print(f"Recall@5: {recall_at_k(recommended, relevant, 5)}")        # 2/5 = 0.4
print(f"Precision@10: {precision_at_k(recommended, relevant, 10)}") # 3/10 = 0.3
print(f"Recall@10: {recall_at_k(recommended, relevant, 10)}")      # 3/5 = 0.6

NDCG(Normalized Discounted Cumulative Gain)

def dcg_at_k(relevance_scores, k):
    """DCG@K: 順位による割引付き累積利得"""
    scores = relevance_scores[:k]
    gains = np.array(scores)
    discounts = np.log2(np.arange(2, len(gains) + 2))
    return np.sum(gains / discounts)

def ndcg_at_k(recommended, relevant, k):
    """NDCG@K: 正規化DCG"""
    # 実際の順位での関連度スコア
    relevance = [1 if item in relevant else 0 for item in recommended[:k]]
    actual_dcg = dcg_at_k(relevance, k)

    # 理想的な順位での関連度スコア
    ideal_relevance = sorted(relevance, reverse=True)
    ideal_dcg = dcg_at_k(ideal_relevance, k)

    return actual_dcg / ideal_dcg if ideal_dcg > 0 else 0

# NDCG は上位に正解があるほど高スコア
print(f"NDCG@10: {ndcg_at_k(recommended, relevant, 10):.4f}")

MAP(Mean Average Precision)

def average_precision(recommended, relevant, k):
    """Average Precision@K"""
    hits = 0
    sum_precision = 0
    for i, item in enumerate(recommended[:k]):
        if item in relevant:
            hits += 1
            sum_precision += hits / (i + 1)
    return sum_precision / min(len(relevant), k) if relevant else 0

def mean_average_precision(all_recommended, all_relevant, k):
    """MAP@K: 全ユーザーのAPの平均"""
    aps = [
        average_precision(rec, rel, k)
        for rec, rel in zip(all_recommended, all_relevant)
    ]
    return np.mean(aps)

ビジネス指標との対応

オフライン指標ビジネス指標関係
NDCG@10CTR(クリック率)NDCG↑ → CTR↑
Recall@20購入率Recall↑ → 購入機会↑
Coverageカタログ活用率Coverage↑ → ロングテール販売↑
Diversityユーザー満足度多様性↑ → 発見体験↑

まとめ

項目ポイント
ランキング指標NDCG/MAPで順位の質を評価
カバレッジPrecision/Recallで網羅性を評価
ビジネス連動オフライン指標をCTR/CVRに対応づける
複合評価単一指標ではなく複数指標で多角的に評価

チェックリスト

  • Precision@KとRecall@Kの違いを説明できる
  • NDCGが順位をどう考慮するか理解した
  • MAPの計算手順を説明できる
  • オフライン指標とビジネス指標の対応を理解した

次のステップへ

推薦の評価指標を理解した。次は演習で推薦アルゴリズムを実装・評価しよう。

推定読了時間: 30分