TinyEval 手搓LLM评测系统直播:直播链接 下面我会带领大家一步一步实现一个简单的LLM评测框架,该框架是一个双阶段的评测体系,我们称之为 ,包含了 通用评测的核心功能,支持生成式、判别式、选则式评测问题,框架主要包含 与 部分,目的是为了帮助大家更好的力即LLM评测的原理与实现。 1.项目的Motivation是什么? 初入 大门,你是否有类似的困惑: 各个模型的评测指标五花八门?小白初学者看不懂,难以学习? 评测 不会选,除了 , 想不到其他的 ? 想让 做选择题,但是模型输出了一大堆,如何评价选择能力? 模型五花八门,垂域任务也五花八门。除了 之外,如何对个性化的任务提供有说服力的定量性能指标? So, 本项目将逐个为你解开上述的困惑! 2.Eval都包含哪些流程?
手搓LLM评测系统直播:直播链接
下面我会带领大家一步一步实现一个简单的LLM评测框架,该框架是一个双阶段的评测体系,我们称之为TinyEval,包含了LLM通用评测的核心功能,支持生成式、判别式、选则式评测问题,框架主要包含inference与eval部分,目的是为了帮助大家更好的力即LLM评测的原理与实现。
初入LLM大门,你是否有类似的困惑:
metric不会选,除了rouge,blue想不到其他的metric?LLM做选择题,但是模型输出了一大堆,如何评价选择能力?human_eval之外,如何对个性化的任务提供有说服力的定量性能指标?So, 本项目将逐个为你解开上述的困惑!
首先要明确评测任务的基础pipeline。下图是评测任务的简要流程:
metric.prompt.pred与anwser进行得分计算.OK,上述这些也就是TinyEval仓库的所有模块内容。
所采用的数据集在这里here,目前有的数据集与类型包含(后续会持续更新!):
| name | type | metric |
|---|---|---|
| multi_news | 长文本问答 | Rouge |
| multifieldqa_zh | 短文本问答 | F1 |
| trec | 生成式选则 | accuracy |
大家可以按照需要的任务进行探索,接下来我也会手把手为大家讲解评测步骤!
看到了上面的指标是否有这样的疑问:
F1 不是分类指标,怎么跑llm去了?accuracy不是要分label标签的吗?怎么跑生成式里来了?Okey,这一节主要就是讲解上述的疑问,如果有基础的同学,可以先自行探索相关代码
multifieldqa_zh为例,其引导prompt为:阅读以下文字并用中文简短回答:\n\n{context}\n\n现在请基于上面的文章回答下面的问题,只告诉我答案,不要输出任何其他字词。\n\n问题:{input}\n回答:
之后,再指定模型的输入长度,在此主要是规定每次送进模型多少token数,一般为了追求性能可以设置为模型最大长度,可以在下载好的模型文件里面的config.json里面的"max_position_embeddings"查询,也可以不设置作为默认最大长度.但本项目设置为了2048,主要为了演示使用~
之后就是创建model整体,在此我对模型整体创建了一个class,大家可以参考对其他任意的model进行组装:
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
internlm2为例,其为def build_chat(self, prompt): prompt = f'<|im_start|>user\n{prompt}<|im_end|>\n<|im_start|>assistant\n' return prompt
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) # 表示喂进去的tokens的长度 context_length = input.input_ids.shape[-1] eos_token_id = [tokenizer.eos_token_id, tokenizer.convert_tokens_to_ids(["<|im_end|>"])[0]] output = model.generate( **input, max_new_tokens=max_gen, do_sample=False, temperature=1.0, eos_token_id=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')
model.chat()不香吗??prompt的存在,在inputs的两端均有关键信息,故需要对两端的信息进行保留,对中间部位进行截断操作,才能最大限度地抱持输出效果!tips: get_pred部分,可以参考各大模型各自的
model相关脚本中的chat函数(internlm2在modeling_internlm2.py里面),也可以更好的理解原始文本输入与结构化模型输出。
直接show例子:
"pred": "57081.86元", "answers": "人民币57081.86元。"
jieba分词,将短句分为词组,以示例文本为例,经过分词与去掉标点符号等操作,得到下列输出:"pred": ['5708186', '元'], "answers": ['人民币', '5708186', '元']"
将上述的两个"干净"的输出送入f1评分函数如下:
def f1_score(prediction, ground_truth, **kwargs): # Counter以dict的形式存储各个句子对应的词与其对应个数,&操作符返回两个Counter中共同的元素的键值对 common = Counter(prediction) & Counter(ground_truth) # 显示prediction与gt的共同元素的个数 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
task的F1_score当然,这些只是基础的metric评测指标,或许细心的你已经发现了相应的漏洞,比如在上述预测中,相比较的结果都是经过了相应的规则抽取的,如果出现了比如answer是"厦门大学",而pred是"不是厦门大学"/"厦大",则二者的结果按照当前的评分指标则有失偏颇。
当然,更加准确的评测metric也是学术界一直努力的目标,本项目也会及时跟进更加先进的评测策略,也欢迎大佬PR!!
python inference.py
python eval.py
我们repo也支持自定义评测,如果进行了自定义sft数据,我们命名为custom_zh,或如果是英文的话可以为custom_en,数据形式与sft格式一致,如下:
{ "instruction": "假设你是皇帝身边的女人--甄嬛", "input": "你是谁?", "output": "臣妾是甄嬛,家父是大理寺少卿。" }
即可支持自定义数据集的评测~
LongBench: A Bilingual, Multitask Benchmark for Long Context Understanding