使用 LLM 作为评判 🧑⚖️ 实现自动化和多功能的评估
大型语言模型 (LLM) 的评估通常是一项艰巨的任务:鉴于其广泛的功能,赋予它们的 任务通常需要以非常广泛和松散定义的要求进行判断。例如,助手对一个问题的 回答可能
- 与上下文不符
- 重复、重复、重复
- 语法错误
- 过长,以过多的词语为特征,导致话语或书面内容过于详细和冗长
- 不连贯
- …
标准列表还在不断增长。即使我们有有限的标准列表,每个标准都很难衡量:“设计一个 基于规则的程序来评估输出非常具有挑战性。传统的基于输出和参考答案相似性的评估指标 (例如 ROUGE、BLEU)对于这些问题也不起作用。”
✅ 一种无需昂贵的人工时间,以人性化方式评估输出的强大解决方案是将 LLM 作为 评判。此方法在 使用 MT-Bench 和 Chatbot Arena 评判 LLM 作为评判 中提出 - 我建议您阅读这篇文章。
💡 这个想法很简单:让 LLM 为您评分。🤖✓
但我们会发现,它无法直接有效地使用:您需要仔细设置它才能获得良好的结果。
!pip install huggingface_hub datasets pandas tqdm -q
import re
import pandas as pd
from tqdm.auto import tqdm
from datasets import load_dataset
from huggingface_hub import InferenceClient, notebook_login
tqdm.pandas() # load tqdm's pandas support
pd.set_option("display.max_colwidth", None)
notebook_login()
repo_id = "mistralai/Mixtral-8x7B-Instruct-v0.1"
llm_client = InferenceClient(
model=repo_id,
timeout=120,
)
# Test your LLM client
llm_client.text_generation(prompt="How are you today?", max_new_tokens=20)
1. 准备创建和评估我们的 LLM 评判
假设您想让 LLM 完成特定任务,例如回答开放式问题。
困难在于,正如我们上面讨论的那样,衡量答案的质量很难,例如,精确的字符串匹配 会将许多正确但措辞不同的答案标记为错误。
您可以让人工标注者来判断输出,但这对他们来说非常耗时,而且如果您想更新模型 或问题,您必须重新进行所有操作。
✅ 在这种情况下,您可以设置 LLM 作为评判。
但是,要使用 LLM 作为评判,您首先需要评估它对模型输出的评级可靠性。
➡️ 因此,第一步将是……创建人工评估数据集。但您只需要为少量示例获取人工标注 - 大约 30 个就足够了,可以很好地了解性能。而且您可以随时重新使用此数据集来测试您的 LLM 评判。
在我们的案例中,我们将使用 feedbackQA
,其中包含每个问题/答案组合的两个人工评估和分数:使用 30 个 示例的样本可以代表您的小型评估数据集。
ratings = load_dataset("McGill-NLP/feedbackQA")["train"]
ratings = pd.DataFrame(ratings)
ratings["review_1"] = ratings["feedback"].apply(lambda x: x["rating"][0])
ratings["explanation_1"] = ratings["feedback"].apply(lambda x: x["explanation"][0])
ratings["review_2"] = ratings["feedback"].apply(lambda x: x["rating"][1])
ratings["explanation_2"] = ratings["feedback"].apply(lambda x: x["explanation"][1])
ratings = ratings.drop(columns=["feedback"])
# Map scores to numeric values
conversion_dict = {"Excellent": 4, "Acceptable": 3, "Could be Improved": 2, "Bad": 1}
ratings["score_1"] = ratings["review_1"].map(conversion_dict)
ratings["score_2"] = ratings["review_2"].map(conversion_dict)
始终计算性能基线是个好主意:这里可以是两个人工评判者之间的一致性,例如通过计算 他们给出的分数的 皮尔逊相关系数 来衡量。
>>> print("Correlation between 2 human raters:")
>>> print(f"{ratings['score_1'].corr(ratings['score_2'], method='pearson'):.3f}")
Correlation between 2 human raters: 0.563
两个评审之间的相关性并不高。如果你的人工评级很差,可能意味着评级标准不够清晰。
这意味着我们的“真实情况”包含噪声:因此,我们不能指望任何算法评估能够达到与之相近的结果。
但是,我们可以减少这种噪声
- 通过将平均分数作为我们的真实情况,而不是任何单个分数,我们应该能够消除一些不规则。
- 只选择人工评审一致的样本。
在这里,我们将选择最后一个选项,并**只保留两个评审意见一致的示例**。
# Sample examples
ratings_where_raters_agree = ratings.loc[ratings["score_1"] == ratings["score_2"]]
examples = ratings_where_raters_agree.groupby("score_1").sample(7, random_state=1214)
examples["human_score"] = examples["score_1"]
# Visualize 1 sample for each score
display(examples.groupby("human_score").first())
2. 创建我们的 LLM 评审
我们使用一个基本的提示构建我们的 LLM 评审,包含以下元素
- 任务描述
- 尺度描述:
最小值
、最大值
、值类型(此处为浮点数
) - 输出格式说明
- 答案的开头,尽可能引导 LLM
JUDGE_PROMPT = """
You will be given a user_question and system_answer couple.
Your task is to provide a 'total rating' scoring how well the system_answer answers the user concerns expressed in the user_question.
Give your answer as a float on a scale of 0 to 10, where 0 means that the system_answer is not helpful at all, and 10 means that the answer completely and helpfully addresses the question.
Provide your feedback as follows:
Feedback:::
Total rating: (your rating, as a float between 0 and 10)
Now here are the question and answer.
Question: {question}
Answer: {answer}
Feedback:::
Total rating: """
examples["llm_judge"] = examples.progress_apply(
lambda x: llm_client.text_generation(
prompt=JUDGE_PROMPT.format(question=x["question"], answer=x["answer"]),
max_new_tokens=1000,
),
axis=1,
)
def extract_judge_score(answer: str, split_str: str = "Total rating:") -> int:
try:
if split_str in answer:
rating = answer.split(split_str)[1]
else:
rating = answer
digit_groups = [el.strip() for el in re.findall(r"\d+(?:\.\d+)?", rating)]
return float(digit_groups[0])
except Exception as e:
print(e)
return None
examples["llm_judge_score"] = examples["llm_judge"].apply(extract_judge_score)
# Rescale the score given by the LLM on the same scale as the human score
examples["llm_judge_score"] = (examples["llm_judge_score"] / 10) + 1
>>> print("Correlation between LLM-as-a-judge and the human raters:")
>>> print(f"{examples['llm_judge_score'].corr(examples['human_score'], method='pearson'):.3f}")
Correlation between LLM-as-a-judge and the human raters: 0.567
考虑到两个随机独立变量之间的皮尔逊相关性为 0,这还不错!
但我们很容易做得更好。🔝
3. 改善 LLM 评审
正如 Aparna Dhinakaran 所示,LLM 在评估连续范围内的输出方面很糟糕。 本文 为我们提供了一些构建更好提示的最佳实践
- ⏳ 留出更多思考时间,在最终答案之前添加一个
评估
字段。 - 🔢 使用小的整数尺度,例如 1-4 或 1-5,而不是我们之前使用的大浮点数尺度。
- 👩🏫 提供指示性尺度以供指导。
- 我们甚至添加一个胡萝卜来激励 LLM!
IMPROVED_JUDGE_PROMPT = """
You will be given a user_question and system_answer couple.
Your task is to provide a 'total rating' scoring how well the system_answer answers the user concerns expressed in the user_question.
Give your answer on a scale of 1 to 4, where 1 means that the system_answer is not helpful at all, and 4 means that the system_answer completely and helpfully addresses the user_question.
Here is the scale you should use to build your answer:
1: The system_answer is terrible: completely irrelevant to the question asked, or very partial
2: The system_answer is mostly not helpful: misses some key aspects of the question
3: The system_answer is mostly helpful: provides support, but still could be improved
4: The system_answer is excellent: relevant, direct, detailed, and addresses all the concerns raised in the question
Provide your feedback as follows:
Feedback:::
Evaluation: (your rationale for the rating, as a text)
Total rating: (your rating, as a number between 1 and 4)
You MUST provide values for 'Evaluation:' and 'Total rating:' in your answer.
Now here are the question and answer.
Question: {question}
Answer: {answer}
Provide your feedback. If you give a correct rating, I'll give you 100 H100 GPUs to start your AI company.
Feedback:::
Evaluation: """
examples["llm_judge_improved"] = examples.progress_apply(
lambda x: llm_client.text_generation(
prompt=IMPROVED_JUDGE_PROMPT.format(question=x["question"], answer=x["answer"]),
max_new_tokens=500,
),
axis=1,
)
examples["llm_judge_improved_score"] = examples["llm_judge_improved"].apply(extract_judge_score)
>>> print("Correlation between LLM-as-a-judge and the human raters:")
>>> print(f"{examples['llm_judge_improved_score'].corr(examples['human_score'], method='pearson'):.3f}")
Correlation between LLM-as-a-judge and the human raters: 0.843
相关性**提高了近 30%**,仅仅对提示进行了一些调整(其中一些百分点归功于我对 LLM 的无耻提示,我特此声明该提示不具有法律约束力)。
相当令人印象深刻!👏
让我们显示一些 LLM 评审的错误以进行分析
errors = pd.concat(
[
examples.loc[examples["llm_judge_improved_score"] > examples["human_score"]].head(1),
examples.loc[examples["llm_judge_improved_score"] < examples["human_score"]].head(2),
]
)
display(
errors[
[
"question",
"answer",
"human_score",
"explanation_1",
"llm_judge_improved_score",
"llm_judge_improved",
]
]
)
分歧很小:总体而言,我们似乎已经为我们的系统达到了良好的性能水平!
4. 如何进一步改进 LLM 评审?
🎯 永远无法达到 100%:首先需要注意的是,我们的人工真实情况肯定存在一些噪声,因此即使使用完美的 LLM 评审,一致性/相关性也永远不会达到 100%。
🧭 提供参考:如果你可以访问每个问题的参考答案,你应该将其提供给提示中的 Judge LLM 以获得更好的结果!
▶️ 提供少样本示例:在提示中添加一些关于问题和真实情况评估的少样本示例可以改善结果。(我在这里尝试过,它在这个案例中没有改善结果,所以我跳过了它,但它可能适用于你的数据集!)
➕ 加性尺度:当判断可以拆分成原子标准时,使用加性尺度可以进一步改善结果:请参见下面的 👇
ADDITIVE_PROMPT = """
(...)
- Award 1 point if the answer is related to the question.
- Give 1 additional point if the answer is clear and precise.
- Provide 1 further point if the answer is true.
- One final point should be awarded if the answer provides additional resources to support the user.
...
"""
使用结构化生成
使用**结构化生成**,你可以配置 LLM 评审直接将其输出作为包含评估
和总评
字段的 JSON 提供,这使得解析更容易:请参见我们的 结构化生成 食谱以了解更多信息!
结论
今天就到这里,感谢您的关注!🥳
我得走了,一些怪人正在敲我的门,声称他们代表 Mixtral 来收集 H100。🤔
< > 更新 在 GitHub 上