相關度 / 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 %}