iT邦幫忙

2025 iThome 鐵人賽

DAY 20
0
生成式 AI

阿,又是一個RAG系列 第 20

Day19: evaluator in llama-index

  • 分享至 

  • xImage
  •  

Intro

  • 我們今天先來熟悉一下 llama-index 世界的 Evaluating
  • 具體來說,我們會在範例資料上使用幾種不同的 evaluator 來實際操作
  • 完整的實驗 notebook 可以參考: 這裡

範例資源

那我們開始吧

1. 範例資料

  • 我們的範例資料如下:點我
  • context: 是 python string ,包含完整的單一題目資訊
  • reference_answer: 是結構化提取出的理想答案
  • json_gemma_response: json_gemma 提取出的 "好" 結果
    • 我們期望當我們把這個 response 給 evaluator 的時候,都可以高分通過
  • llama_en_response: llama 提取出的 "差" 結果
    • 我們期望當我們把這個 response 給 evaluator 的時候,可以順利拒絕
{'context': '1.常見針灸配穴法中,所指的「四關穴」,為下列何穴位之組合?\n\xa0\nA.上星、日月\nB.合谷、太衝\nC.內關、外關\nD.上關、下關',
 'reference_answer': {'qid': '1',
  'stem': '常見針灸配穴法中,所指的「四關穴」,為下列何穴位之組合?',
  'A': '上星、日月',
  'B': '合谷、太衝',
  'C': '內關、外關',
  'D': '上關、下關'},
 'json_gemma_response': {'qid': 1,
  'stem': '常見針灸配穴法中,所指的「四關穴」,為下列何穴位之組合?',
  'A': '上星、日月',
  'B': '合谷、太衝',
  'C': '內關、外關',
  'D': '上關、下關'},
 'llama_en_response': {'A': '主蒙,曜月',
  'B': '合座,夡里',
  'C': '到递,割递',
  'D': '主递,一递',
  'qid': 1,
  'stem': '台九気动组化。\n\nA.主蒙,曜月\nB.合座,夡里\nC.到递,割递\nD.主递,一递'}}

2. Semantic Similarity Evaluator

  • 我們首先要介紹的是 Semantic Similarity Evaluator,驗的是答案跟參考答案是否相似

  • 他的 input 包含:

    • reference_answer: 就是參考答案
    • response: 就是系統的預測
  • 直觀的想法就是問: 這是回答,這是答案,他們有多像?

  • 實作:

    • reference_answerresponse 都直接做 embedding 然後算 similarity
  • 他的 output 有:

    • similarity score
      • 越接近 1 越好
    • passing
      • similarity_threshold: initial 的時候給可以給這個 threshold
      • 超過 passing 就是 True,否則是 false
      • 預設 0.8
  • 我們來看一下例子

  • 呼叫:

    semantic_evaluator = SemanticSimilarityEvaluator(similarity_threshold=0.8)
    gd_result = await semantic_evaluator.aevaluate(
        response=str(test_data['json_gemma_response']),
        reference=str(test_data['reference_answer']),
    )
    print("Score: ", gd_result.score)
    print("Passing: ", gd_result.passing)  # use similarity threshold 
    
  • 回傳

    Score:  0.9967628031093531
    Passing:  True
    
  • 預期要失敗的 case:

    Score:  0.8674033244211179
    Passing:  True
    
    • 這邊可以看到 Score 有降低,但是還是 Pass 了
  • 在使用者已習慣 chatgpt 的情況下,使用非 llm 的 model 來做事很長會顯得效果不夠看

  • 但這個 evaluator 有兩個很大的好處:

    • 一個是便宜,當實驗組數上百的時候,每個都呼叫 chatgpt judge 太傷錢包
    • 一個是穩定,不會像 llm 一樣長長呼叫的時候給分還不一樣
  • 缺點也很明顯,一定要有答案才可以算

  • 實驗想法:

    • precision 高的話可以用來抓 fail case
      • 因為這個 model 說有錯的話就是有錯,所以要測到這個 model 抓不出錯
    • recall 高的話可以當成常態的 filter
      • 他可以先濾一波,因為他會把有錯的大部分都挑出來

3. CorrectnessEvaluator

  • 我們第二個用的是 CorrectnessEvaluator,驗的是答案是否正確

  • 他的 input 有三個:

    • query: 原始給 llm 的 query
    • response: model 的 response
    • reference: reference_answer
  • 直觀的想法就是問: 這是問題,這是回答,這是答案,0-5分給幾分?

  • 實作:

    • 就 prompt llm 基於 query, response, reference_answer 去給 0-5 分
    • 預設的 prompt 可以在 這裡
  • 他的 output 包含

    • feedback: llm 解釋的原因
    • score: 0-5 分,越高越好
    • passing: 初始化的時候可以給一個 threshold,超過算 pass
  • 呼叫:

    query = prompt_en_llama.format(text=test_data['context'])
    result = correct_evaluator.evaluate(
        query=query,
        response=str(test_data['json_gemma_response']),
        reference=str(test_data['reference_answer']),
    )
    print(f"feedback: {result.feedback}")
    print(f"score: {result.score}")
    print(f"passing: {result.passing}")
    
  • 回傳

    feedback: The generated MCQ matches the reference exactly in stem and options (only minor non-substantive difference in qid formatting as an integer vs string). It correctly omits an answer field as required.
    score: 5.0
    passing: True
    
  • 預期要失敗的 case:

    feedback: The generated answer is incorrect and largely garbled: the stem text is nonsensical and the options do not match the original question or the reference answer. It fails to extract the correct MCQ fields and provides wrong characters/words, so it is not a valid extraction.
    score: 1.0
    passing: False
    
  • 這方法其實還是很有參考性

    • 不然也不會變成大名鼎鼎的 LLM-as-a-Judge
  • 我們在 feedback 的部分看到他用英文回覆,這個主要就是 prompt 沒有自訂的關係

    • 所以我們後續要改 prompt
  • 實測跑兩次,溫度為零的情況下,有看到應該 fail 的 case 他的 score 從 1.0 變成 2.0

    • 所以確實是會飄
  • 這個每次 call 其實很貴,會不敢亂做實驗

    • 解決辦法是平常就直接把 llm 換成本地的 model,然後想辦法增強它,
    • 等到真的要報告再用貴的
    • 實驗想法: 用 本地 model 當 judge 跟 chat-gpt 的差距
  • 他 pass 的 threshold 預設是 4 分,要超過 4 分才會算 pass (最高也就 5 分)

    • 個人體感是 llm 除非很確定是做錯不然通常分數都不敢給太低
    • 所以我們後續實驗要測一下 他是不是其實分數都會給偏高
  • 他預設一定要給 reference_answer 才能 judge ,但其實同樣的方法不給 answer 其實也能做

    • 實驗想法: 自己 prompt 比較 給 reference answer 跟 不給 reference answer

4. FaithfulnessEvaluator

  • 最後是 FaithfulnessEvaluator

    • 驗的是答案是不是照著 context 寫出來的
  • 他的 input 有兩個:

    • context: 這邊我是放原始的題目 str
      • 在 rag 系統放的就是 retriever 檢回來的 context
      • 這邊要注意他吃的是 list of string
    • response: 就是模型最終的回答
  • 直觀的想法就是問: 這是 response,這是 context,阿 context 有沒有支持 response

  • 實作

    • 基本上也是 prompt llm 回答
      • prompt 在 DEFAULT_EVAL_TEMPLATE
      • 這個 prompt 有點繞,在看的時候要把 query_str 當成是傳進去的 response
      • 傳進去的 query 只有在最後回傳的時候才會用到
  • 他的 output 有:

    • feedback: 只會回答 YES, NO
    • passing: 如果 feedback 有 yes 就給 True
    • score: 如果 passing 是 1,不然是 0
  • 呼叫:

    eval_result = evaluator.evaluate(contexts=[test_data['context']], response=str(test_data['json_gemma_response']))
    print(f"feedback: {eval_result.feedback}, passing: {eval_result.passing}, score: {eval_result.score}")
    
  • 回傳:

    feedback: YES, passing: True, score: 1.0
    
  • 我們來造一個胡謅的例子,直接硬塞一個沒有的答案

    fake_response = test_data['json_gemma_response']
    fake_response['ans'] = 'C'
    eval_result = evaluator.evaluate(contexts=[test_data['context']], response=str(fake_response))
    print(f"feedback: {eval_result.feedback}, passing: {eval_result.passing}, score: {eval_result.score}")
    
  • 回傳:

    feedback: NO, passing: False, score: 0.0
    
    • 真的可以成功抓到亂達,有驚豔到
  • 這是今天介紹唯一一個不需要 reference_answer 就可以做的 evaluator

  • 最後硬塞答案這件事能抓到我真心覺得猛

  • 實驗想法: 可以自行實作偷加料,來看 judge 是不是可以穩定的抓出來

Summary

  • 我們今天學習了 llama-index 的 evaluator 模組
  • 在我們的資料上實測了三種 evaluator,他們分別是
    • SemanticSimilarityEvaluator 這個就是告訴我們 response 跟 reference_answer 有沒有像
    • CorrectnessEvaluator 告訴我們 response 有沒有像 reference_answer 一樣回答 query
    • FaithfulnessEvaluator 告訴我們這個 response 是不是依照 context 回的
  • 我們培養了一些實驗的直覺,具體包含:
    • Semantic Similarity Evaluator 的 precision/recall
    • 用本地 model 當 judge 跟 chat-gpt 的差距
    • 有 reference 和 沒有 reference 的 CorrectnessEvaluator 差很多嗎
    • FaithfulnessEvaluator 是不是夠穩定
  • 現在萬事俱備我們明天來跑實驗吧!

其他

  • rag 系統最直觀要驗證的就三件事:
    1. response <-> query: 這個回答到底有沒有回答到問題
    2. query <-> context: 檢索回來的 context 有沒有涵蓋 query
    3. response <-> context: 答案是否忠於檢索內容
  • 但這三個驗證幾乎都還是靠 LLM 完成 -> 又帶來了新問題
    • 例如: 如果 chat-gpt 說我的測試資料全數通過,你會完全相信嗎?
  • 另一個困境是:沒有測試資料,或測試資料本身就是 llm 生成的
  • 近期的系列希望先從可控的資料集開始逐步實驗
    • 培養一些對 llm 能力的直覺
      • 知道說: 喔這個已經很穩了、喔那個還很菜
    • 然後再過渡到生成的資料集
      • 比較看看生成的跟控制的具體差異
    • 繼續踩坑

上一篇
Day18: structured output challenge
系列文
阿,又是一個RAG20
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言