Precision@kとRecall@k

入門

4.5.3

Precision@kとRecall@k

最終更新 2020-02-26 読了時間 3 分
まとめ
  • Recall@k は上位 k 件に含まれる正解数の割合を測る指標です。
  • レコメンドリストから Recall@k と Precision@k を算出し、候補の網羅性と純度を確認します。
  • クエリごとの正解数や候補数によって解釈が変わる点と評価設計の注意点を整理します。

1. 定義 #

クエリ$q$に対する正解アイテム集合を$G_q$、上位kの候補集合を$S_{q,k}$とすると、

$$ \mathrm{Recall@k} = \frac{|G_q \cap S_{q,k}|}{|G_q|} $$

Precision@k #

同じ記号を用いると、Precision@kは次のように定義されます。

$$ \mathrm{Precision@k} = \frac{|G_q \cap S_{q,k}|}{k} $$
  • Recall@k:正解をどれだけ拾えたか(網羅性)。
  • Precision@k:提示した候補のうち、どれだけ正解だったか(純度)。

2. Python で計算 #

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import numpy as np

def recall_at_k(y_true: np.ndarray, y_score: np.ndarray, k: int) -> float:
    """上位 k 件に含まれる正例の割合(リコール)を計算する。

    Args:
        y_true: 正例を 1 とするラベル配列。
        y_score: モデルが出力したスコア配列。
        k: 上位何件を見るか。

    Returns:
        リコール値。
    """
    idx = np.argsort(-y_score)[:k]
    return float(y_true[idx].sum() / y_true.sum())

def precision_at_k(y_true: np.ndarray, y_score: np.ndarray, k: int) -> float:
    """上位 k 件の正例率(適合率)を計算する。

    Args:
        y_true: 正例を 1 とするラベル配列。
        y_score: モデルが出力したスコア配列。
        k: 上位何件を見るか。

    Returns:
        適合率。
    """
    idx = np.argsort(-y_score)[:k]
    return float(y_true[idx].sum() / k)

y_true は 0/1 のラベル、y_score はモデルのスコアです。複数クエリがある場合は平均を取ります。


3. k の選択 #

  • UI や配信枠に合わせて k を決定(例:おすすめ 5 件 → Recall@5)。
  • 複数の k を設定し、Recall@5 / Recall@10 などで段階的に評価すると改善ポイントが掴みやすい。

4. 実務での活用 #

  • レコメンド:ユーザーが実際に選ぶアイテムが候補リストに含まれているかを確認。
  • 広告配信:インプレッション枠の中で、クリックやコンバージョンが発生した広告をどれだけ含められたかを評価。
  • A/B テスト:オンライン実験と合わせて Recall@k をウォッチし、指標の改善がユーザー行動に直結しているかを確認。

5. Precision@kとのトレードオフ #

  • Recall@kを上げるには候補を増やす必要があり、Precision@kは下がりやすい。
  • kを固定した上で、Recall@kとPrecision@kを両立できるようモデルを改善することが理想。
  • F1@kやMAPを併用し、バランス良く改善できているかをチェック。

6. 計算例 #

あるレコメンドシステムがユーザーに対して10件のアイテムを推薦したとします。正解アイテム(ユーザーが実際に購入・クリックしたアイテム)は全部で8件あり、推薦リストの各順位における正解/不正解は以下のとおりです。

順位アイテム正解
1Ao
2Bx
3Co
4Do
5Ex
6Fo
7Gx
8Ho
9Ix
10Jo

正解アイテムの総数: $|G_q| = 8$

Recall@5の計算 #

上位5件(A, B, C, D, E)のうち正解はA, C, Dの3件です。

$$ \mathrm{Recall@5} = \frac{|G_q \cap S_{q,5}|}{|G_q|} = \frac{3}{8} = 0.375 $$

Precision@5の計算 #

$$ \mathrm{Precision@5} = \frac{|G_q \cap S_{q,5}|}{k} = \frac{3}{5} = 0.600 $$

Recall@10の計算 #

上位10件すべてを見ると、正解は A, C, D, F, H, Jの6件です。

$$ \mathrm{Recall@10} = \frac{|G_q \cap S_{q,10}|}{|G_q|} = \frac{6}{8} = 0.750 $$

Precision@10の計算 #

$$ \mathrm{Precision@10} = \frac{|G_q \cap S_{q,10}|}{k} = \frac{6}{10} = 0.600 $$

結果の比較 #

指標
Recall@50.375
Precision@50.600
Recall@100.750
Precision@100.600

kを5から10に増やすとRecall@kは0.375から0.750へ大幅に向上しました。一方、Precision@kは0.600のまま変化していません。この例では候補を増やしても精度が維持できていますが、一般的にはkを大きくするとPrecision@kは低下する傾向にあります。


7. F1@k:RecallとPrecisionの調和平均 #

Recall@kとPrecision@kの両方を1つの数値で要約したい場合、F1@kが有効です。分類タスクのF1スコアと同様に、調和平均で定義されます。

$$ F_1@k = 2 \cdot \frac{P@k \cdot R@k}{P@k + R@k} $$

ここで$P@k = \mathrm{Precision@k}$、$R@k = \mathrm{Recall@k}$です。

先ほどの例でF1@kを計算 #

F1@5:

$$ F_1@5 = 2 \cdot \frac{0.600 \times 0.375}{0.600 + 0.375} = 2 \cdot \frac{0.225}{0.975} \approx 0.462 $$

F1@10:

$$ F_1@10 = 2 \cdot \frac{0.600 \times 0.750}{0.600 + 0.750} = 2 \cdot \frac{0.450}{1.350} \approx 0.667 $$

F1@kはRecallとPrecisionのどちらかが極端に低い場合にスコアが大きく引き下げられるため、両方のバランスが取れているかを確認する際に役立ちます。

Pythonでの実装 #

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import numpy as np

def f1_at_k(y_true: np.ndarray, y_score: np.ndarray, k: int) -> float:
    """上位 k 件における F1 スコアを計算する。

    Args:
        y_true: 正例を 1 とするラベル配列。
        y_score: モデルが出力したスコア配列。
        k: 上位何件を見るか。

    Returns:
        F1@k の値。
    """
    idx = np.argsort(-y_score)[:k]
    hits = y_true[idx].sum()
    prec = hits / k
    rec = hits / y_true.sum()
    if prec + rec == 0:
        return 0.0
    return float(2 * prec * rec / (prec + rec))

8. 他のランキング指標との関係 #

Recall@kは単独で使うだけでなく、他のランキング指標と組み合わせることで多面的な評価が可能になります。各指標の特徴とRecall@kとの関係を整理します。

Hit Rate(ヒット率)との関係 #

Hit Rateは上位k件に少なくとも1件の正解が含まれるかどうかを見る二値指標です。Recall@kは正解の何割を拾えたかを測る指標なので、Hit Rateの拡張版と位置づけることができます。

$$ \mathrm{HR@k} = \mathbf{1}\{ \mathrm{Recall@k} > 0 \} $$

Hit Rate = 1であってもRecall@kが低い場合は、正解の一部しかカバーできていないことを意味します。

NDCG(Normalized Discounted Cumulative Gain)との関係 #

NDCGは推薦リストの順序の質を評価する指標です。正解が上位に集まるほど高スコアになります。一方、Recall@kは順序を無視し、上位k件に含まれる正解の割合のみを見ます。

  • Recall@kが高くてもNDCGが低い場合 → 正解アイテムは拾えているが、下位に偏っている
  • NDCGが高くてもRecall@kが低い場合 → 少数の正解を上位に配置できているが、全体の網羅性が不足している

MAP(Mean Average Precision)との関係 #

MAPは正解を見つけた各位置での適合率を平均したものです。Recall@kが正解の網羅性を測るのに対し、MAPは正解を見つける「タイミング(順位)」も加味します。

  • MAPが高い → 正解アイテムが上位に集中して出現している
  • Recall@kが高い → 上位k件で正解アイテムの多くをカバーしている

指標の使い分けガイド #

評価の観点推奨指標
正解をどれだけ拾えたかRecall@k
候補リストの純度Precision@k
RecallとPrecisionのバランスF1@k
正解が上位に来ているかNDCG
正解の出現順を加味した精度MAP
最低1件ヒットしたかHit Rate

まとめ #

  • Recall@kは候補リストの網羅性、Precision@kは純度を示す基本指標。
  • F1@kを使えばRecallとPrecisionのバランスを1つの数値で把握できる。
  • kの設定を明確にし、NDCG・MAP・Hit Rateなど複数の指標を併用してランキングモデルの性能を多面的に評価しよう。
  • オンライン指標(CTR、CVRなど)と関連づけて分析することで、ビジネスインパクトを説明しやすくなる。

よくある質問 #

Recall@kとHit Rateの違いは? #

Recall@kは「正解アイテム全体のうち何件を上位k件に含めたか」の割合(0〜1)です。Hit Rate@kは「ユーザーごとに1件でもヒットしたか」をバイナリ(0/1)で評価します。正解が1件だけの場合は両者は一致しますが、複数の正解アイテムがある場合はRecall@kの方が細かい評価ができます。

kの値はどうやって決める? #

ビジネス要件に合わせて決めます。「ユーザーが実際に見る画面枠が10件」ならk=10が自然です。複数のk(k=5, 10, 20)で評価し、kの変化に対する感度も確認するのが実務的です。正解数が少ないタスクでは小さいkが有効で、正解数が多いタスクでは大きいkも意味を持ちます。

Precision@kとRecall@kはどちらを優先する? #

正解アイテムの数によります。正解が多い(類似商品が多い)場合はカバレッジ優先でRecall@kが重要です。提示枠が少ない(kが小さい)場合はノイズを避けるためPrecision@kも重視します。両者のバランスを見たい場合はF1@kを使います。

マクロ平均とマイクロ平均の違いは? #

マクロ平均はユーザーごとにRecall@kを計算してその平均を取ります(全ユーザーを均等に扱う)。マイクロ平均は全ユーザーの正解数・候補数を合算してから計算します(インタラクション数が多いユーザーの影響が大きい)。ユーザー体験の公平性を重視するならマクロ平均が適しています。


  • ヒット率 — 最もシンプルな想起指標
  • NDCG — 順位を重視した評価
  • MAP — 適合率ベースのランキング評価