相關度 / NDCG
===
###### tags: `LLM / Metrics`
###### tags: `LLM`, `Metrics`, `相關度`, `NDCG`
<br>
[TOC]
<br>
## 討論
### NDCG 指標如何用?
- ### gpt-o1
**NDCG(Normalized Discounted Cumulative Gain)** 是評估「**搜尋或推薦系統排名品質**」的常用指標。它最初用於多級相關性(relevance)評測,即不只單純的「正確 / 錯誤」,而是能表示「不同等級」的相關度(如:非常相關、中等相關、無相關)。但即使在二元相關情境下,NDCG 也相當常用。下面將依步驟說明它的意義、計算流程,以及如何在 Python 中使用。
---
## 1. 什麼是 DCG / IDCG / NDCG?
### 1.1 DCG(Discounted Cumulative Gain)
給定一個查詢(query),模型產生一個排序列表 `[d1, d2, d3, ...]`,其中每個文件 $d_i$ 都有一個「相關度分數」(relevance) $rel_i$。
- **DCG** 的概念是:若相關度越高、排名越前,則應該貢獻較大的評分;同時,排名越後,**其增益(gain)會因折扣(discount)而下降**。
一種常見的 DCG 定義為:
$$
\text{DCG}_p = \sum_{i=1}^{p} \frac{2^{rel_i} - 1}{\log_2(i+1)}
$$
- $p$ 是只看前幾名(Top-p)做計算,或直接用整個列表長度。
- $rel_i$ 是第 $i$ 項文件的「相關度分數」(通常是非負整數)。
- 分子 $2^{rel_i} - 1$ 讓較高的相關度有指數增益。若是簡單的二元相關度(0/1),則 $2^0 - 1 = 0$ 或 $2^1 - 1 = 1$。
- 分母 $\log_2(i+1)$ 表示排名越往後,折扣越大。
> 也有一些變體會用 $\frac{rel_i}{\log_2(i+1)}$,不同研究或套件可能採不同公式。
### 1.2 IDCG(Ideal DCG)
- 對同一個查詢來說,我們可以將「最理想」的排序方式做 DCG 計算,就得到 **IDCG**。
- 「最理想」排序指的是依照 **相關度分數由大到小排序**。
- **IDCG** 是該查詢排名的理論最高值。
### 1.3 NDCG(Normalized DCG)
- 為了讓不同查詢之間的 DCG 有可比性,我們會將真實排序結果的 DCG,**除以** 最理想排序下的 IDCG,得到
$$
\text{NDCG} = \frac{\text{DCG}}{\text{IDCG}}
$$
- NDCG 介於 $0$ 與 $1$ 之間,越接近 1 代表越接近「理想排序」。
---
## 2. 計算流程舉例
假設我們有一個查詢 $ q $,系統輸出排序為 `[d1, d2, d3]`,相關度分別為 `[3, 2, 0]`(表示 d1 非常相關、d2 中度相關、d3 不相關)。
1. **DCG**
$$
\text{DCG} = \frac{2^3 - 1}{\log_2(1+1)} + \frac{2^2 - 1}{\log_2(2+1)} + \frac{2^0 - 1}{\log_2(3+1)}
$$
- 第 1 名(i=1): $\frac{2^3 - 1}{\log_2(2)} = \frac{7}{1} = 7$
- 第 2 名(i=2): $\frac{2^2 - 1}{\log_2(3)} = \frac{3}{1.585} \approx 1.89$
- 第 3 名(i=3): $\frac{2^0 - 1}{\log_2(4)} = \frac{0}{2} = 0$
- 加總後約等於 $8.89$。
2. **IDCG**
- 最理想排序應該是 `[d1, d2, d3]` (其實跟當前一樣) 或 `[3, 2, 0]` 依分數排序:
$$
\text{IDCG} = \frac{2^3 - 1}{\log_2(1+1)} + \frac{2^2 - 1}{\log_2(2+1)} + ...
$$
- 結果會相同,約 $8.89$。
3. **NDCG**
$$
\text{NDCG} = \frac{\text{DCG}}{\text{IDCG}} = \frac{8.89}{8.89} = 1.0
$$
- 因為實際排名與理想排名相同,所以 NDCG = 1。
如若系統輸出是 `[d2, d1, d3]`,則 DCG 會相對較低,最後得到的 NDCG < 1。
---
## 3. NDCG 在 Python 中的實作
### 3.1 現成工具
#### 3.1.1 `torchmetrics` 或其他
在某些搜尋、推薦相關的 Python 套件中,如 [torchmetrics](https://torchmetrics.readthedocs.io/en/stable/ranking/ndcg.html) 就有實作 NDCG 的方法,可以直接使用。
使用範例(`torchmetrics`):
```python=
# 安裝:pip install torchmetrics
import torch
from torchmetrics.functional import retrieval_normalized_dcg
# 假設我們有一個查詢的預測排序中,每個文件的模型打分
pred_scores = torch.tensor([3.0, 2.0, 0.0]) # or any real-value scores
# 對應的真實相關度 (relevance)
target_relevance = torch.tensor([3, 2, 0])
ndcg_value = retrieval_normalized_dcg(pred_scores, target_relevance, k=3)
print("NDCG =", ndcg_value.item())
```
- 這邊 `k=3` 表示在前 3 名進行計算。
- 也可在多個查詢上做 batch 計算,詳情請參考官方文件。
#### 3.1.2 `evaluate`(Hugging Face)
Hugging Face 的 [evaluate](https://github.com/huggingface/evaluate) 庫中目前沒有直接提供 `ndcg`,但會有其他排名或檢索的 metric。需要使用者自行撰寫或使用其他第三方套件。
---
### 3.2 自訂 Python 函式示範
若要展示整個原理,可以用以下簡單程式示範(二元或多級相關度都可):
```python=
import math
def dcg(relevances, k=None):
"""
計算 DCG
relevances: 排序列表中每個位置的相關度 (list 或 tuple)
k: 只計算前 k 名,若不指定則計算全部
"""
if k is None:
k = len(relevances)
score = 0.0
for i in range(k):
rel_i = relevances[i]
score += (2**rel_i - 1) / math.log2((i+1) + 1)
return score
def ndcg(relevances, k=None):
"""
relevances: 依照「模型排序」好的相關度列表 (最高分排前面)
k: 只計算前 k 名
"""
ideal_rel = sorted(relevances, reverse=True) # 最理想排序:由高到低
actual_dcg = dcg(relevances, k)
ideal_dcg = dcg(ideal_rel, k)
if ideal_dcg == 0:
return 0.0
return actual_dcg / ideal_dcg
# 測試
if __name__ == "__main__":
# 假設我們的排序結果 relevances = [3, 2, 0]
rel_scores = [3, 2, 0]
ndcg_value = ndcg(rel_scores)
print("NDCG =", ndcg_value) # ~1.0
# 如果換個排序 [2, 3, 0]
rel_scores_2 = [2, 3, 0]
ndcg_value_2 = ndcg(rel_scores_2)
print("NDCG =", ndcg_value_2)
```
---
## 4. 如何在多個查詢上計算 NDCG?
若有多個查詢(Q 個),常見做法是先對每個查詢計算 NDCG,再取平均:
$$
\text{NDCG@k} = \frac{1}{|Q|} \sum_{q \in Q} \text{NDCG}_q(k)
$$
- 其中 $\text{NDCG}_q(k)$ 表示第 $q$ 個查詢在考慮前 k 名時的 NDCG。
- 這樣就能得到整個系統在所有查詢上的平均排名品質。
---
## 5. 小結
1. **NDCG** 關鍵點
- 不只看「第一個正確答案」,而是考慮整個排名清單裡各文件的相關度與順序。
- 能處理多級相關性(像是等級:0=無相關, 1=部分相關, 2=非常相關, 3=極度相關等)。
2. **計算步驟**
- 計算 DCG:依據「實際排序」累積貢獻並隨排名折扣
- 計算 IDCG:依據「最理想排序」得到最大可能 DCG
- NDCG = DCG / IDCG
3. **如何用 Python**
- **套件**:建議使用 `torchmetrics`、`rank_bm25`、或其他檢索 / 推薦工具裡的內建函式。
- **手刻**:依照上面公式撰寫計算 DCG、IDCG、最後得到 NDCG 即可。
4. **典型用法**
- 設定一個排名深度 $k$(如 NDCG@5, NDCG@10),只評估前 $k$ 名結果的品質,因為使用者通常只會關心前面幾項。
- 對多個查詢取平均值,比較不同模型或方法誰的 NDCG 較高。
總之,NDCG 是對「**整體排名品質**」相對全面的一種量化方式,在多級相關度情境下更能展現效能;相對於 MRR、Precision@k 只聚焦「第一個正確答案」或「前 k 個正確答案個數」,NDCG 能在搜尋、推薦、問答等任務中帶來更細緻的評價。
{%hackmd vaaMgNRPS4KGJDSFG0ZE0w %}