iT邦幫忙

2025 iThome 鐵人賽

DAY 26
0
生成式 AI

阿,又是一個RAG系列 第 26

Day25: 實戰:MCQ Data Challenge

  • 分享至 

  • xImage
  •  

Intro

  • 最後一個篇章我們要把 Evaluating 加到解題的過程裡

    • 系統會自己檢索,自己看看有沒有找到,再自己回答,(也許)再自己看看有沒有亂講
    • 所以這是:反思的技能(應該算吧)
  • 還記得我們在 Day17: exam_and_structured_output_dataset 構造的第二個 dataset

    • exam_dataset
    • 具體來說,他是一份單選題的國考題考卷,考的是中醫的針灸
    • 因為針灸理論上資料很少,所以通用的大模型很可能會做不好,那這時候就輪到我們的 RAG 出場啦!
  • 我們來看看這樣可不可以把它破台吧

Task:

今天的任務很簡單(我們也只會做簡單的),具體來說有三個:

    1. 我們要先直接 prompt LLM 回答一次看結果
    • 如果他直接就可以滿分的話我們就收工不玩了
      • 還是能夠應是很逞強的說它詳解可能亂講
    1. 然後我們要讓他先找資料才作答
    • 由於我們沒有針灸的本地資料庫,所以這邊我們打算 prompt llm 生成適合搜尋 wiki 的關鍵字
    • 再用關鍵字去 wiki 搜尋,當然啦用的是套件
    • 然後把搜尋結果全部整起來當 context 給 llm 答
    • 如果他直接就可以滿分的話我們就收工不玩了
      • 這次不逞強
    1. 最後我們會應用我們在昨天學到的 Evaluation 與 Error Analysis 技巧
    • 由於這是單選題,我們可以直接把單選題的正確率當成我們 end2end 的 evaluation
    • component-wise evaluating 部分,我們會跑出 Context Relevancy vs Faithfulness 的圖表
      • 來看看它是資料沒相關,還是回答亂講
        • 未看先猜又是資料找不到所引發的問題喔

那我們就開始吧

Action:

直接 prompt llm 作答

  • 我們這邊會測兩隻 llm:

    • 具體一隻是 gpt-5-mini,後面我們就叫 mini
    • 另一隻是 gemma3:12b,後面我們就叫 gemma
  • 我們直接開啟 json mode,要求他回答一個 dictionary,包含兩個 keys:

    • ans: str,就是 ABCD 四選一,不要講其他有的沒的
    • feedback: str,有的沒的在這邊講
  • prompt:

from llama_index.core.prompts import PromptTemplate

ANSWER_PROMPT = PromptTemplate(
    template="""
你是一個中醫考題專家,請根據下面的題目回答單選題。
要求:
1. 輸出 JSON 格式
2. key: "ans" 只回答單選答案 (A/B/C/D)
3. key: "feedback" 簡短說明為什麼選這個答案
4. 不要輸出其他文字

#{query}

請直接輸出 JSON:
"""
)
  • 結果:
gemma: "{4}/{10}"
gpt-5-mini: "{7}/{10}"
  • gemma 10 題只有對 4 題喔
    • 看來我們還有工作要做
  • 完整程式碼在這裡
  • 完整結果在這裡

先搜維基百科再作答

  • 遙想當年,你引用維基百科講話還會被吐槽說那個都亂講,現在這成為我們相對信任的資料了
  • 這區塊其實有四個任務:
    • prompt llm 產生適合維基百科的關鍵字
    • 維基百科搜尋
    • 把搜尋的結果整理成一大段 context
    • 整合 context 回答

首先是 prompt llm 產生關鍵字:

  • 我們一樣是直接開啟 json mode
from llama_index.core.prompts import PromptTemplate

QUERY_TRANSFORM_PROMPT = PromptTemplate(
    template="""
你是一個專門處理中醫考題的語言模型。我將給你一道中醫考題,請從題目和選項中提取最多 5 個「專有名詞」,
這些名詞應該是適合拿去維基百科搜尋的關鍵字,也就是在維基百科上可能有條目的中醫專有名詞,例如穴位名稱、方劑名稱、病證名稱等。  

請以 JSON 格式返回,key 為 "keyword",value 為一個字串列表。  
不要輸出其他文字或解釋。  

範例輸出:
{
  "keyword": ["四關穴", "合谷", "太衝"]
}

#{query}

請直接輸出 JSON:
"""
)

檢索維基百科

這邊坑就比較多:

  • 我們直接用 python 的 wikipedia 套件,假設給定某個要搜的關鍵字 keyword
import wikipedia
wikipedia.set_lang('zh')
wikipedia.page(keyword, auto_suggest=False)
data = {
    "title": page.title,
    "url": page.url,
    "summary": page.summary,
    "content": page.content,
}
  • 首先是我們只想要中文的頁面

  • 把 auto_suggest關掉,不然很容易出很多不相干的文章

    • 副作用就是很容易找不到東西
  • 它有可能你搜某一個關鍵字是有很多歧異的,它會回你可能的選項列表

    • 舉例來說就是 apple 是公司還是水果
  • 回傳部分,title 就是條目名稱,會有這個條目的 summary,看起來是開頭的描述,然後才是比較長的內文 content

把 wiki 回傳的結果包成一大段 context

  • 雖然說這邊我們可以上個 rarank 之類的,但在測試的時候我發現主要問題其實是很容易找不到結果,所以就沒上
def get_context(wiki_results):
    context = ''
    for keyword, value in wiki_results.items():
        if not value:
            continue
        try:
            title = value['title']
            content = value['content']
            context+=f'-----'
            context+=f"搜索關鍵字: {keyword}\n"
            context+=f"條目名稱: {title}\n"
            context+=f"內容: {content}\n"
            context+=f'-----'
        except:
            continue
    if len(context) == 0:
        context+='沒有找到相關結果'
    return context

放在一起回答:

  • prompt
ANSWER_PROMPT_WITH_CONTEXT = PromptTemplate(
    template="""
你是一個中醫考題專家,請根據下面的題目回答單選題。

請遵守以下規則:
1. 嚴格依據提供的參考資料 context 作答。
2. 輸出 JSON 格式。
3. JSON 需包含兩個 key:
   - "ans" :只回答單選答案 (A/B/C/D)
   - "feedback" :簡短說明為什麼選這個答案
4. 不要加入題目之外的說明或其他文字。

題目:
#{query}

參考資料 (context):
#{context}

請直接輸出 JSON:
"""
)

結果:

- gemma: "{4}/{10}" -> "{6}/{10}"
- gpt-5-mini: "{7}/{10}" -> "{8}/{10}"
  • wow,原來這樣檢索它竟然真的是有做事
    • 雖然不多
  • 完整程式碼在: 這裡
  • 完整結果在: 這裡

Context_relevancy vs Faithfulness

  • 我們這邊直接用 default 的 prompt,然後就是把它們跑出來

結果

來人,上圖
https://ithelp.ithome.com.tw/upload/images/20251010/201778551N6UXNjibr.jpg

  • 這張圖的 x 軸是 faithfulness score,越高代表越有照著 context 回答

  • y 軸是 context relevancy score,越高代表找回來的資料跟問題越相關

  • 藍點代表的是做對的題目,紅點代表是做錯的題目

  • 可以看到大部分的情況都在左下角跟右上角

    • 左下角就是沒找到相關資料,然後模型就也沒把找到的東西當回事自己回答了
      • 還給他猜對了一題
    • 右上角雖然說是理想的情況,但如果在右上角還錯了的話,就要實際檢視一下是什麼問題
      • 就是說我們這樣的 evaluation 不 work 了
    • 右下角的藍點與紅點:
      • 沒有進一步的資訊我們比較難下定論
      • 有可能是 evaluator 誤判
      • 也有可能是 參考資料說找不到,然後 response 也說找不到,只是最後還是亂塞了一個答案 (所以可以拿到高分的 Faithfulness)
  • 整體來說所有錯的題目都是 context relevancy 很低,所以我們要修正的話還是要從 retriever 下手

最後我們換個 xy軸看看
https://ithelp.ithome.com.tw/upload/images/20251010/20177855MHvEERZLb2.jpg

  • 這張圖的 x 軸是 題號,所以一個 column 就是一題的資訊
  • y 軸是 score,我們把所有 score 都放在 0-1 之間可以一起畫,包含:
    • correctness: 綠色帶線三角:
      • 這題做對就是1,做錯就是0,我們把正確性中間連成線,這樣方便我們檢查
    • Faithfulness: 藍色圓圈:
      • 有沒有照 context 答,有就1沒有就0
    • Context Relevancy:
      • 檢索回來的context 跟 問題的相關程度/能不能回答問題,越相關越接近1
  • 這張圖的用法就是,看一下怪怪的點然後我們回頭去找實例

第 8 題

  • 我們 index 從 0 開始
  • 這題在上面那張圖是左下角的藍點,整體亂答還給他猜對
"query": "題目: 下列穴位屬三焦經與膽經的交會穴共有幾個?1臑會 2顴髎 3秉風 4聽宮 5耳門\n選項:\n A: 2\n B: 3\n C: 4\n D: 5\n",
"context": "沒有找到相關結果",
"feedback": "參考資料中未提及三焦經與膽經的交會穴,但題目要求根據資料回答,因此選擇A,因為選項中只有2個穴位(臑會和顴髎)被提及,且假設其為正確答案。"
  • 好,果然是矇對,有提到參考資料沒有,然後後面就開始亂講

第 6 題

  • 這題是右下角的紅點(答錯),問題應該是資料沒找到,但是他的 Faithfulness 給了很高的分數
"query": "題目: 依《難經.六十八難》,下列何穴主心下滿?\n選項:\n A: 前谷\n B: 液門\n C: 湧泉\n D: 跗陽\n",
"context": # 太長省略
"feedback": "《難經》論述腧穴,但未提及前谷穴主心下滿,故選A。"
  • 這邊可以跟第 8 題做一個對照,feedback 同樣都提及參考資料沒有相關的答案,但是一個 Faithfulness 給了高分一個給了低分
  • 問題應該出在,胡謅的成分大不大,在上一題硬是扯了個看不懂的理由所以低分,這題就只有說有查到難經,但沒講到痛點,還是選了個答案
  • 整體來說還是呼應我們先前的經驗,在 context 很低分的時候就不要去解決 Faithfulness,不然會解到一些對我們答題其實沒幫助的問題
  • 本來我有想說如果參考資料沒有答案,就不准 llm 回答,但是這太違反人性了
    • 你寫選擇題的時候會不猜嗎?
    • 所以最後我就作罷,事實上硬是要他不能猜估計結果也不會好就是了

第 4 題

  • 這題是右下角的藍點,資料沒找到,但是有照著 context 回答,而且還答對了,我們來看看發生什麼事
"query": "題目: 下列何者為陰維脈之郄穴?\n選項:\n A: 交信\n B: 築賓\n C: 府舍\n D: 腹哀\n",
"feedback": "參考資料中明確指出陰維脈起於築賓穴,因此築賓是陰維脈的郄穴。"
"cr_feedback": "1) 是否與使用者查詢的主題相符?\n部分相符 (給予 1.0 / 2.0)。\n說明:檢索到的內容確實在討論「陰維脈」,與題目主題相符;且內容明確提到陰維脈起於「小腿內側築賓穴」,因此與選項 B(築賓)相關。但題目問的是「陰維脈之郄穴(xi‑cleft)為何?」——檢索內容並未提及任何有關郄穴(郄穴名稱或定位)的資訊
  • 這邊這個 cr 就是 context relevancy 的 feedback
    這題的問題是:
  • 題目問的是陰維脈的郄穴
    • chatgpt: 這個「郄」字確實容易讓人誤會成「起源、起點」的意思,但實際上 「郄穴」的『郄』並不是指起源,而是指『縫隙、間隙、聚集處』。
  • context 只有講到陰維脈的起源,沒有講到陰維脈的郄穴
    • 所以 context relevancy 判了個低分,還知道關鍵是郄穴
  • 因為 context 確實有講到起源,所以 faithfulness 給了個高分,沒有去檢查這個因此的邏輯不通
  • 然後模型還猜對了

Summary:

  • 我們今天首先直接 prompt llm 回答單選題,gemma 只答對 4 個,讓我們覺得這是 rag 出場的大好時機
  • 我們優先使用 wiki 檢索資料,檢索的關鍵字是 prompt llm 產生出來的,但是我們發現 wiki 其實常常查不到
  • 我們直上兩個不用答案的 evaluator: context_relevancy 與 faithfunless 來看一下現在低分要先優化什麼,並且跟 correctness 一起比較
    • 整體評估起來就是一般 rag 系統的常見問題:沒找到、找到相關的,但是沒騷到癢處
  • 我們最後實際看了幾題比較特殊的資料,就是 context relevancy 跟 faithfulness 或者 correctness 背離的題目
    • 我們的 context relevancy 其實很靠譜,也就是說在評論到底資料是有找到沒找到這個現在 llm 可以做的很好
    • 我們的 faithfulness 給的答案很迷,llm 在 邏輯跳躍的時候不會判定是胡謅還是會給出高分
      • 但主要問題都是出在 context 已經做不好的情況下,這部分我們可以先不用管
  • 明天我們來做: 如果判定了 context relevancy 有問題,那就改用 tavily 檢索的機制

其他:

  • 最後一個篇章了,我們且行且珍惜
  • 實際上我也不知道這個單選題我們能不能破台,就讓我們繼續看下去

上一篇
Day24: Baseline RAG 的 Evaluation
下一篇
Day26: Online Retriever Evaluator
系列文
阿,又是一個RAG28
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言