iT邦幫忙

2025 iThome 鐵人賽

DAY 21
0
生成式 AI

阿,又是一個RAG系列 第 21

Day20: structured output challenge report

  • 分享至 

  • xImage
  •  

Situation

Task

  • 今天的任務分成兩部分:
    • 首先我們會先以 gemma_12b 的結果作為測試資料,來確認我們的驗證算法
    • 驗證算法確定之後,我們再套用到其他四組進行整體的比較

Action

part1: 驗證算法

  1. 首先是資料準備的部分:
  • 我們針對每一個"選手",都構建一個 evaluation_dataset.json 來方便我們今天做事
  • 每個檔案內容都是一個 list of dictionary
    • 每個 element 都包含以下 3 個 keys
      • reference_answer:
        • 放的是我們使用 regex parser 出來的 考題字典,是 ground-truth
      • reference_context:
        • 是題目的純 python string 片段
      • response:
        • 是個選手 inference 出來的結果
  • 這其實就是我們之前只有跑出 response ,沒有整理成方便我們進行驗證的格式所留的債務
  • 這邊的第一個發現是: gemma:12b ,就算沒有開 json mode, predict 的結果也是可以順利的直接轉為 json (在我們的 80 筆測試資料下無失誤)
  1. 嚴格的 stem exact match:
  • 為了簡化問題,我們今天只會關注在 stem 的題取,因為其他各選項的結果也可以直接套用

  • 我們用一個 for 遍歷我們整個 80 筆資料:

    • 如果 (response.stem) == (reference_answer.stem),就判對
      • 否則就判斷錯誤
  • 如此一來我們就可以針對每一個結果計算 stem extract 的 succ_rate

    • (num_succ / 80)
  • 結果: succ_rate = 0.0

    • 一題都沒有成功
  • 問題:

    {'label': '常見針灸配穴法中,所指的「四關穴」,為下列何穴位之組合?',
     'pred': '常見針灸配穴法中,所指的「四關穴」,為下列何穴位之組合?'}
    
    • 這邊 pred 是 model 的 response (parser 過後),而 label 是從我們的 reference_answer 取出來的
    • 可以看到,其實這題 model 並沒有做錯,而是因為標點符號的全形半形問題,導致 pred 被判錯
      • 這也解釋了為什麼,我們今天還要先開發驗證算法
  • 解: 我們測試一下,先把 pred 和 label 都經過一個 "normalize",來把這類全型半型的問題解決

  1. normalize exact match:
  • 我們的 normalize_text 函數如下:

    def normalize_text(s: str) -> str:
        """簡單的 normalization: 小寫化、去標點、去多餘空白"""
        # 全部轉小寫
        s = s.lower()
        # 移除所有標點符號(只保留中英文、數字、空白)
        s = re.sub(r"[^0-9a-z\u4e00-\u9fff\s]", "", s)
        # 去掉多餘空白
        s = " ".join(s.split())
        return s
    
  • 就做三件事:

    • 轉小寫
    • 移除標點符號
    • 把有 2 個以上的空白情況合併成單 1 空白
  • 結果: succ_rate = 0.75

    • 有起色但不夠
  • 問題:以下是兩筆 fail_case:

    {'qid': '2',
      'label': '依靈樞經脈記載其直者從巔入絡腦還出別下項循肩膊內挾脊抵腰中指下列 何經的循行內容',
      'pred': '依靈樞經脈記載其直者從巔入絡腦還出別下項循肩膊內挾脊抵腰中指下列何經的循行內容'},
    {'qid': '9',
      'label': '下列穴位屬三焦經與膽經的交會穴共有幾個1臑會 2顴髎 3秉風 4聽宮 5耳門',
      'pred': '下列穴位屬三焦經與膽經的交會穴共有幾個臑會 顴髎 秉風 聽宮 耳門'},
    
    • 這次仍然不是模型的問題,而是驗證算法的問題:
      • 單純去掉多餘空白,還是會留存空白,導致 qid: 2 的情況被判錯 (多一個空白)
      • 第 9 題出現了特殊符號:
        • 原題目: 下列穴位屬三焦經與膽經的交會穴共有幾個?①臑會 ②顴髎 ③秉風 ④聽宮 ⑤耳門
      • 由於我們的算法直接把符號移除了,導致只要是這類型的題目我們全部都會判錯
    • 我們改正我們的 normalize 方法再試一次
  1. unicodedata.normalize
  • 我們這次使用 unicodedata normalize 的 NFKC 規則,具體呼叫方法如下:
    import unicodedata
    def unicode_normalize(txt):
        s = unicodedata.normalize("NFKC", txt)
        return s
    
  • 拿剛剛的特殊符號測試:
    example_text = '①臑會 ②顴髎 ③秉風 ④聽宮 ⑤耳門'
    print(unicode_normalize(example_text))
    # 1臑會 2顴髎 3秉風 4聽宮 5耳門
    
  • NFKC 實際上做了什麼?
    • 全形 -> 半形
      • "ABC123" → "ABC123"
    • 相容字元 -> 基本字元
      • "①②③" → "123"
      • "㎏" → "kg"
    • 合併字元組合 → 單一字元
      • "e\u0301" (e + combining acute) → "é"
    • 特殊符號變為等價字元
      • "﹣" (全形減號) → "-"
  1. customize normalize exact match:
  • 做完 NFKC 後,我們把所有英文轉為小寫、移除所有的標點符號、移除所有的空白,再次測試
  • 這次 gemma:12b 的結果有 99% 被判定為正確
  • 唯一錯誤的一題如下:
    [{'qid': '28',
      'label': '48歲女性患者診斷為類風濕性關節炎已2年主訴四肢肩膀至手指遊走性疼痛夜間及氣候變化疼痛加重下列敘述何者錯誤',
      'pred': '2848歲女性患者診斷為類風濕性關節炎已2年主訴四肢肩膀至手指遊走性疼痛夜間及氣候變化疼痛加重下列敘述何者錯誤'}]
    
  • 這題 gemma 錯把題號當成題幹的一部分也進行了提取,而且原因可以理解:
    • 因為原始題幹是數字開頭(28.48歲),gemma 可能不確定應該當成是 28.48 歲,還是 48歲
    • 但這確實可以算在是 算法 的錯誤
  1. 我們來把所有的考題都跑完
  • normalized_exact_match.py
  • 結果在 evaluation_result
  • 我們保留了:
    • qid: 原始題號
    • ispass: 是否判定通過
    • label: reference_answer 的 stem
    • pred: 從 model response 中提取的 stem
    • nlabel: 經過上述 normalize 後的 label
    • npred: 經過上述 normalize 後的 pred
  1. 統計結果:
  • 1_llama_en_evaluation_dataset.json: succ_rate: 0.07
  • 3_llama_chat_evaluation_dataset.json: succ_rate: 0.12
  • 2_llama_zh_evaluation_dataset.json: succ_rate: 0.36
  • 4_gemma_evaluation_dataset.json: succ_rate: 0.99
  • 5_json_gemma_evaluation_dataset.json: succ_rate: 0.99
  1. 發現與討論:
  • llama_en 符合預期的正確率低下,這個我們在第一版測試就已經知道

    • 他會是我們下一步,evaluating evaluation 的絕佳素材
  • llama_zh 的正確率從 0.07 -> 0.36

    • 雖然結果還是不能用
    • 但從這裡可以看出,在通用的範例裡,把預設 prompt 簡單的直接翻譯為中文,確實有可能可以帶來顯著的提升
    • 符合我們的經驗直覺
  • 使用 chat-gpt 編寫的 prompt 的 llama 並沒有如預期的明顯好過預設 prompt 英翻中

    • 我們使用的 prompt 在 這裡
      • 有確實有給出具體的範例 input / output
    • 在檢查 fail case 時後發現,大多數的錯誤來自於會把題號或者選項一併提取到題幹裡
    • 這部分並不表示 prompt engineering 無用
    • 但確實顯示出:花了很多時間改 prompt ,是有可能遇到結果還不如不改的情況
      • 當前的想法是:也許我們可以構造一個 workflow,在給定 k 個 example 答案的情況下,讓 llm 自行產生 prompt ,直到這 k 個 example 都做對,我們再進行批量測試
  • gemma 開啟 json mode 與 沒有開啟 json mode 錯的題目是同一題

    • 並沒有在我們的 toy problem 直接分出高下
    • 本來以為 開 json mode 會讓錯誤率高出一點,但並沒有發生
    • 本來以為沒有開 json mode 會讓我們 parser 的時候遇到麻煩,也並沒有發生

Summary

  • 我們今天從 evaluation 的角度出發,實際體驗了一把'驗證的算法本身也是算法'。

    • 一般在進行 evaluation 的時候,通常有兩種選擇:
      • 使用某種廣為人知的驗證流程
      • 自訂符合自己情境的方法
    • 兩種方式各有優缺點,但無論選哪一個都有可能遇到:
      • 被判錯的 case 其實不能怪算法
      • 模型只有在指標上佔便宜,實際沒有比較好
    • 我們今天從 自訂符合自己情境的方法 出發,修改至我們認為合理的情況,才進行批量的測試
    • 關於更通用的 NLP evaluation 可以參考 huggingface
  • 模型比較也帶來幾個直覺

    • 語言的匹配有可能馬上帶來有幅度的提升
    • 精心設計的 prompt 有可能還不如直接中翻英
    • json parser 有可能比你想像中穩定
    • 以及 json mode 可能沒有想像中弱
  • 我們明天來測測看,用 llm 做 evaluation 和我們今天做的結果有沒有一致


上一篇
Day19: evaluator in llama-index
系列文
阿,又是一個RAG21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言