Tiny-universe手戳大模型TinyEval--task4

TinyEval

一个简单的LLM评测框架,该框架是一个双阶段的评测体系,我们称之为TinyEval,包含了LLM通用评测的核心功能,支持生成式、判别式、选则式评测问题,框架主要包含inferenceeval部分。

1.Eval都包含哪些流程?

首先要明确评测任务的基础pipeline。下图是评测任务的简要流程:

在这里插入图片描述

  • 首先,根据目标数据集的任务类型指定合理的评测metric.
  • 根据目标数据的形式总结模型引导prompt.
  • 根据模型初步预测结果采纳合理的抽取方式.
  • 对相应的predanwser进行得分计算.

OK,上述这些也就是TinyEval仓库的所有模块内容。

2.支持的评测数据集与评测Metric.

nametypemetric
multi_news长文本问答Rouge
multifieldqa_zh短文本问答F1
trec生成式选则accuracy

1. 生成式的F1

1.1 模型推理
  • 首先,对于一个评测数据集,我们首先要构造引导prompt,即引导llm生成我们想要的答案。对于已有的数据集,大部分都提供了相应的prompt,在自己数据集评测时,也可自行设计。以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
  • 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')
  • 这个函数就告诉了你答案。原因就在于截断策略,对于模型而言,尤其是制定了输入的长度,如果使用阶段命令则其会在输入的末尾进行阶段,但由于引导性prompt的存在,在inputs的两端均有关键信息,故需要对两端的信息进行保留,对中间部位进行截断操作,才能最大限度地抱持输出效果!
1.2 结果评测

直接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
  • 首先记录两个list中相同的元素,再统计相同的元素的总数,最终再按照precision与recall的定义分别计算相应的分数。
  • 然后就得到该结果的对应分数啦,最后再将所有的结果取平均值,即得到该taskF1_score

2.思考

当然,这些只是基础的metric评测指标,或许细心的你已经发现了相应的漏洞,比如在上述预测中,相比较的结果都是经过了相应的规则抽取的,如果出现了比如answer是"厦门大学",而pred是"不是厦门大学"/“厦大”,则二者的结果按照当前的评分指标则有失偏颇。

当然,更加准确的评测metric也是学术界一直努力的目标,本项目也会及时跟进更加先进的评测策略,也欢迎大佬PR!!

运行结果

``在这里插入图片描述在这里插入图片描述

support metrics

  1. F1 score
  2. rouge-series/blue-series
  3. accuracy

支持自定义评测

我们repo也支持自定义评测,如果进行了自定义sft数据,我们命名为custom_zh,或如果是英文的话可以为custom_en,数据形式与sft格式一致,如下:

{
    "instruction": "假设你是皇帝身边的女人--甄嬛",
    "input": "你是谁?",
    "output": "臣妾是甄嬛,家父是大理寺少卿。"
}

即可支持自定义数据集的评测~

Reference & Acknowledgment

LongBench: A Bilingual, Multitask Benchmark for Long Context Understanding

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值