文章目录
手搓一个LLM Eval
TinyEval:简单的LLM评测框架
github项目地址:https://github.com/datawhalechina/tiny-universe/blob/main/content/TinyEval/readme.md
项目的动机
刚进入LLM领域时,你可能会遇到一些困惑:
- 各种评测指标五花八门,让人眼花缭乱。
- 除了
rouge
、bleu
外,不知道该如何选择其他评测指标。 - 想让LLM做选择题,结果模型输出了一堆内容,如何评估它的选择能力?
- 各种模型和领域任务千差万别,除了
human_eval
,如何为个性化任务提供有说服力的定量性能指标?
本项目就是为了解决这些问题的!
评测流程概述
评测任务的基础流程如下图所示:
- 根据数据集任务类型,确定合适的评测指标。
- 根据数据形式,编写模型引导
prompt
。 - 根据模型初步预测结果,设计合理的抽取方式。
- 对预测结果与标准答案进行评分。
上述步骤覆盖了TinyEval仓库的所有模块。
支持的数据集与评测指标
相关数据集可以在./Eval/dataset/找到,目前已有的数据集及类型如下:
数据集名称 | 类型 | 评测指标 |
---|---|---|
multi_news | 长文本问答 | Rouge |
multifieldqa_zh | 短文本问答 | F1 |
trec | 生成式选择题 | Accuracy |
你可以根据任务需求探索这些数据集,接下来我会为大家详细讲解评测步骤!
详细的评测过程
可能你会有这样的疑问:
- 为什么
F1
能用于生成式任务? accuracy
不是用来评估分类任务的吗?怎么用在生成式任务里?
下面我们一起来解答这些问题。如果你已经熟悉基础知识,可以先行探索相关代码./Eval/metrics.py。
1. 生成式任务的F1评分
1.1 模型推理
对于评测数据集,首先需要构造引导性prompt
,用于引导LLM生成我们期望的答案。大多数已有的数据集都提供了相应的prompt
,但在自定义数据集时,可以自行设计。以multifieldqa_zh
为例,它的prompt
如下:
阅读以下文字并用中文简短回答:\n\n{context}\n\n现在请基于上面的文章回答下面的问题,只告诉我答案,不要输出任何其他字词。\n\n问题:{input}\n回答:
接下来,设置模型的输入长度。通常可以设置为模型的最大长度,通常在模型的config.json
文件中查询。例如,此处我们将其设为2048用于演示。
然后创建模型类,灵活支持不同模型的组装:
class BaseLLM:
def __init__(self, path: str, model_name: str) -> None:
self.path = path
self.model_name = model_name
def build_chat(self, tokenizer: str, prompt: str, model_name: str):
pass
def load_model_and_tokenizer(self, path: str, model_name: str, device):
pass
def post_process(self, response: str, model_name: str):
pass
def get_pred(self, data: list, max_length: int, max_gen: int, prompt_format: str, device, out_path: str):
pass
其中重点是get_pred
函数,它处理推理过程,截断策略尤为重要,确保保留输入两端的关键信息:
def get_pred(self, data, max_length, max_gen, prompt_format, device, out_path):
model, tokenizer = self.load_model_and_tokenizer(self.path, device)
for json_obj in tqdm(data):
prompt = prompt_format.format(**json_obj)
tokenized_prompt = tokenizer(prompt, truncation=False, return_tensors="pt").input_ids[0]
if len(tokenized_prompt) > max_length:
half = int(max_length/2)
prompt = tokenizer.decode(tokenized_prompt[:half], skip_special_tokens=True)+tokenizer.decode(tokenized_prompt[-half:], skip_special_tokens=True)
prompt = self.build_chat(prompt)
input = tokenizer(prompt, truncation=False, return_tensors="pt").to(device)
output = model.generate(
**input,
max_new_tokens=max_gen,
do_sample=False,
temperature=1.0,
eos_token_id=[tokenizer.eos_token_id],
)[0]
pred = tokenizer.decode(output[context_length:], skip_special_tokens=True)
pred = self.post_process(pred)
with open(out_path, "a", encoding="utf-8") as f:
json.dump({"pred": pred, "answers": json_obj["answers"], "all_classes": json_obj["all_classes"], "length": json_obj["length"]}, f, ensure_ascii=False)
f.write('\n')
1.2 结果评测
让我们以一个例子为例:
"pred": "57081.86元", "answers": "人民币57081.86元。"
经过数据清洗后,预测结果和答案会被分词,然后通过F1评分函数进行评估:
def f1_score(prediction, ground_truth, **kwargs):
common = Counter(prediction) & Counter(ground_truth)
num_same = sum(common.values())
if num_same == 0:
return 0
precision = 1.0 * num_same / len(prediction)
recall = 1.0 * num_same / len(ground_truth)
f1 = (2 * precision * recall) / (precision + recall)
return f1
最终计算F1得分,并取平均值作为任务的总得分。
2. 思考
当前的评分指标存在局限性,例如如果答案是"厦门大学",而预测是"厦大"或"不是厦门大学",F1分数无法精确反映模型的表现。
提高评测指标的准确性是学术界的持续目标,本项目也会随着更先进的评测策略不断更新,欢迎贡献PR!
🚀 如何运行
1. 运行模型推理
python inference.py
2. 运行评测
python eval.py
支持的评测指标
- F1 Score
- Rouge系列/Blue系列
- Accuracy
支持自定义评测
该项目支持自定义数据集的评测。如果你有自定义的SFT数据,命名为custom_zh
(中文)或custom_en
(英文),数据格式如下:
{
"instruction": "假设你是皇帝身边的女人--甄嬛",
"input": "你是谁?",
"output": "臣妾是甄嬛,家父是大理寺少卿。"
}
这样就可以支持自定义数据集的评测了!
学习总结
TinyEval
是一个简单的语言模型(LLM)评测框架,旨在帮助用户评估生成式、判别式、选择式等多种任务中的LLM表现。它通过以下几个关键步骤进行评测:
- 确定评测指标:根据任务类型选择合适的评测标准,如
F1
、Rouge
、Accuracy
等。 - 模型推理:根据数据集任务构建
prompt
,进行模型推理,生成预测结果。 - 结果处理与评分:对模型生成的结果与标准答案进行对比,通过指标如F1得分进行评估。
框架支持多种评测任务和自定义数据集,并提供了易于使用的接口来运行模型推理和评测。