Last Updated on 2024-07-25 by Clay
前言
2023 年是生成式 AI 大爆發的一年,各式各樣的 AI 應用層出不窮。其中在自然語言處理(NLP)領域中,大型語言模型(Large Language Model, LLM)絕對是最重要的技術。只要把 LLM 訓練好、減少幻覺,就會在各式各樣的任務上減少人力。
而基於檢索增強生成(Retrieval Augmented Generation, RAG)的 LLM 更是一種替模型帶來新知識、減少幻覺的一種架構。其架構如下圖:
從右上角的使用者問題 Query 進入系統,我們會透過檢索系統(由 Embedding Model、Vector DB 聯合檢索)返回跟使用者的 Query 最相關的文件,再讓 LLM 同時閱讀 Retrieved Contexts + Query 生成回覆 Response。
這是個很有用的系統,也衍生出各式各樣更強化的架構,如 RA-DIT、Self-RAG…… 等等。但是,我們該如何知道什麼模型最適合去做 RAG 呢?什麼系統架構又更適合當前的 LLM 呢?
也就是說,我們必須去『評估』 RAG 的效果才行。
然而眾所皆知,由於生成的文字訊息跟傳統的 AI 分類不同,很難去進行量化的計分;就算是過往計算模型生成答案與標準答案之間相似度的 BLEU 和 ROUGE-N,也依然很難評估現在的 LLM。
我斷斷續續看了一些評估的 Medium 文章、部落格文章與論文,今天就來跟紀錄一個最近剛看到、並且同事表示要使用其框架套件的論文:RAGAS: Automated Evaluation of Retrieval Augmented Generation
RAGAs 介紹
RAGAs(Retrieval Augmented Generation Assessment)是一個不受檢索資料領域限制的自動評估框架,相關的程式碼可以從官方 GitHub 上瀏覽,並且有被現在知名的 LLM pipeline 框架 llama-index 和 LangChain 所整合。
而 RAGAs 框架本身是採用 LLM 去評估當前 RAG 系統效果的。說得更精準一點,預設就是以 gpt-3.5-turbo-16k 進行 RAG 系統不同面向的評估的。
當然,這個評估的架構是允許我們隨意替換評估用的模型的(這個模型跟我們應用在 RAG 架構中的模型不是同一個模型),比方說,你可以根據自己的需求切換成 GPT-4、或是你自己訓練/下載的評估用 LLM。
評估策略
研究團隊思考了標準 RAG 的設置:給定問題 q,系統檢索出相關資訊 c(q),並依照 c(q) 的資訊再讓 LLM 生成回覆 a(q)。
而在這種特定檢索回覆生成任務中,通常並沒有人工評估的資料集可以使用,而直接人工評估 RAG-based LLM 的時間開銷實在太大(可能每調整一個 RAG 系統的設定就得全部重新測量),所以選擇了使用 LLM 來評估的作法(現在普遍的標準都是拿 GPT-4 進行評估)。
而讓 LLM 去評估 LLM,該怎麼評估?眾所皆知,LLM 對於數字其實並不敏感,並且 LLM 還會帶有一定程度的幻覺與偏見 —— 所以在這點上,還是得由人類事先設定好評估的標的給 LLM。
而在 RAGAs 的論文中,設定了三個評估面向:
- 忠誠度(Faithfulness)
- 答案相關性(Answer Relevance)
- 上下文相關性(Context Relevance)
官方框架的文件中還提到了另外兩個面向:
4. 上下文精確率(Context Precision)
5. 上下文召回率(Context Recall)
那我們一一紀錄這些面向分別代表什麼意思。以下我所提到的評估 LLM,指的是替 RAG 系統打分的 LLM,並非與 RAG 系統整合在一起的 LLM。
忠誠度(Faithfulness)
這個評估指標是在量化『生成回覆』與『檢索資料』之間的事實一致性,分數會被映射到 (0, 1) 之間;當然,分數是越高越好。
首先,我們通過給評估 LLM 設定一組 prompt(當然,可根據你的評估需求自行更改),讓其從 RAG 系統生成的回覆中拆出更多的敘述段落(statements)。
Given a question and answer, create one or more statements from each sentence in the given answer.
question: [question]
answer: [answer]
拆出更多的敘述段落後,我們會再次讓評估 LLM 去評估每一個敘述段落是否可以從檢索資料(Retrieved Context)中推論出來。
Consider the given context and following statements, then determine whether they are supported by the information present in the context. Provide a final verdict for each statement in order at the end in the given format. Do not deviate from the specified format.
statement: [statement 1]
...
statement: [statement 2]
那最終的忠誠度分數,就是由『可以從 contexts 推論出的敘述段落數量 / 敘述段落總數』計算而出。
也就是說,假設你的評估結果如下:
CONTEXT: A = 1, B = 2, A + B = 3. ANSWER: A = 1, B = 2, C = 3, A + B = 3, A + C = 4. STATEMENTS: O | A = 1 O | B = 2 X | C = 3 O | A + B = 3 X | A + C = 4
那麼你的 RAG 系統幻覺很嚴重,所得到的忠誠分數便是 3 / 5,也就是 0.6 分。
相對的,如果你的評估結果非常好:
CONTEXT: A = 1, B = 2, A + B = 3. ANSWER: Because A = 1 and B = 2, A + B = 3. STATEMENTS: O | A = 1 O | B = 2 O | A + B = 3
那麼,得分自然就是 3 / 3 = 1.0 的滿分。
答案相關性(Answer Relevance)
答案相關性的部份,則是將 RAG 生成的答案透過評估 LLM 生成一個『全新的問題』,並依照這個全新的問題與原本使用者的問題進行相似度計算。
在論文中,相似度計算用的模型也是使用 OpenAI 的 text-embedding-ada-002
。你可以重新製作多的問題計算平均分數。
我們可以想像成如果 RAG 生成的答案如果沒辦法回答原本的使用者問題的話,那麼重新生成的問題多半跟使用者問題在向量空間上並不接近。
所以這確實是一種評估 RAG 生成的回覆是否有跟使用者問題相關的方式。
上下文相關性(Context Relevance)
跟忠誠度的計算方式很像,這次我們換成對檢索出來的 Context 進行分句。每個句子透過評估 LLM 判斷是否可以回答使用者問題的 Question。
Please extract relevant sentences from
the provided context that can potentially
help answer the following question. If no
relevant sentences are found, or if you
believe the question cannot be answered
from the given context, return the phrase
"Insufficient Information". While extracting candidate sentences you’re not allowed to make any changes to sentences
from given context.
當然,我們也同樣可以透過與 Question 相關聯的句數除以總句數,計算這個檢索出來的 Context 跟問題之間的相關性分數。
上下文精確率(Context Precision)
接下來的兩個指標都需要標準答案(Ground Truth)才能計算了;若是希望完全排除人工的部份,那麼這需要人工製作標準答案的這兩個指標可能就不能使用了。
k 是檢索取 top-k 個 contexts,true positive 和 false positive 可以理解成『當我猜 1 時答案就是 1』和『當我猜 1 時答案卻是 0』。
從原始碼中來看:
denominator = sum(response) + 1e-10
numerator = sum(
[
(sum(response[: i + 1]) / (i + 1)) * response[i]
for i in range(len(response))
]
)
scores.append(numerator / denominator)
response 通常是評估 LLM 已經判斷完 Context 是否跟問題相關了,所以假設現在有兩個不同的檢索系統,查找出來的 Contexts 評估列表分別是 [1, 1, 0] 和 [1, 0, 1]。
在計算 denominator 時,兩者分數皆為 2 + 1e-10(這是一種不讓分數為 0 的作法)。
但是在計算 numerator 時分別就出來了。前者分數為 2.0、後者為 1.66。並且直覺上我們也會覺得檢索系統把相關的 Contexts 都排在前面是比較有好的檢索結果。
上下文召回率(Context Recall)
召回率就真的是必須要有標準答案 Ground Truth 了。我們的計算方式就是把 Ground Truth 的句子中跟檢索資料有關的列出,並除以 Ground Truth 的句子總數,我們便判斷 Context 覆蓋的 Ground Truth 比例。
使用方法
使用 ragas
框架的方式非常簡單,首先安裝該套件。
pip install ragas
接著設定 OpenAI 的 API key,然後準備你自己的資料集(下方是範例資料集)。
import os
os.environ["OPENAI_API_KEY"] = "your-openai-key"
from datasets import load_dataset
fiqa_eval = load_dataset("explodinggradients/fiqa", "ragas_eval")
接著可以選擇想要使用的評估方式。
from ragas.metrics import (
answer_relevancy,
faithfulness,
context_recall,
context_precision,
)
from ragas import evaluate
result = evaluate(
fiqa_eval["baseline"].select(range(3)), # selecting only 3
metrics=[
context_precision,
faithfulness,
answer_relevancy,
context_recall,
],
)
df = result.to_pandas()
df.head()
當然,由於這個評估框架的 prompt 終究還是需要貼齊我們的需求的。所以在情況允許的情況下,我還是建議自己實作會比較好。