context
: 是 python string ,包含完整的單一題目資訊reference_answer
: 是結構化提取出的理想答案json_gemma_response
: json_gemma 提取出的 "好" 結果
llama_en_response
: llama 提取出的 "差" 結果
{'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.主递,一递'}}
我們首先要介紹的是 Semantic Similarity Evaluator,驗的是答案跟參考答案是否相似
他的 input 包含:
reference_answer
: 就是參考答案response
: 就是系統的預測直觀的想法就是問: 這是回答,這是答案,他們有多像?
實作:
reference_answer
跟 response
都直接做 embedding 然後算 similarity他的 output 有:
similarity score
passing
similarity_threshold
: initial 的時候給可以給這個 thresholdpassing
就是 True,否則是 false我們來看一下例子
呼叫:
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
在使用者已習慣 chatgpt 的情況下,使用非 llm 的 model 來做事很長會顯得效果不夠看
但這個 evaluator 有兩個很大的好處:
缺點也很明顯,一定要有答案才可以算
實驗想法:
我們第二個用的是 CorrectnessEvaluator,驗的是答案是否正確
他的 input 有三個:
query
: 原始給 llm 的 queryresponse
: model 的 responsereference
: reference_answer直觀的想法就是問: 這是問題,這是回答,這是答案,0-5分給幾分?
實作:
query
, response
, reference_answer
去給 0-5 分他的 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
這方法其實還是很有參考性
我們在 feedback 的部分看到他用英文回覆,這個主要就是 prompt 沒有自訂的關係
實測跑兩次,溫度為零的情況下,有看到應該 fail 的 case 他的 score 從 1.0 變成 2.0
這個每次 call 其實很貴,會不敢亂做實驗
他 pass 的 threshold 預設是 4 分,要超過 4 分才會算 pass (最高也就 5 分)
他預設一定要給 reference_answer
才能 judge ,但其實同樣的方法不給 answer 其實也能做
reference answer
跟 不給 reference answer
最後是 FaithfulnessEvaluator
context
寫出來的他的 input 有兩個:
context
: 這邊我是放原始的題目 str
response
: 就是模型最終的回答直觀的想法就是問: 這是 response
,這是 context
,阿 context
有沒有支持 response
query_str
當成是傳進去的 response
query
只有在最後回傳的時候才會用到他的 output 有:
feedback
: 只會回答 YES, NOpassing
: 如果 feedback
有 yes 就給 Truescore
: 如果 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 是不是可以穩定的抓出來