美文网首页
大模型系列:C-Eval中文大模型评测数据集介绍和实践

大模型系列:C-Eval中文大模型评测数据集介绍和实践

作者: xiaogp | 来源:发表于2024-03-08 10:09 被阅读0次

    关键词:大模型测评C-Eval

    前言

    C-Eval是目前权威的中文AI大模型评测数据集之一,用于考察大模型的知识和推理能力,本篇对C-Eval数据集做简要介绍,并演示如何使用C-Eval对大模型进行评测。


    内容摘要

    • C-Eval整体结构概述
    • C-Eval数据预览
    • C-Eval的Prompt范式
    • Python脚本实现C-Eval评估ChatGLM2-6B

    C-Eval整体结构概述

    在前文《大模型系列:LLM-Eval大模型评测理论简述》中介绍了大模型需要评测的内容,包括NLP任务知识和逻辑推理安全性对齐性等多个角度,C-Eval数据集主要用于评测大模型的知识和逻辑推理能力,即大模型是否能够认识和理解广泛的世界知识,并类似人类一样对事物进行推理规划。
    C-Eval数据集由13948道多选题组成,涉及4个学科大类,52个学科小类,分别对应四个难度等级,如下所示。

    C-EVAL的题目构成
    • STEM:科学、技术、工程和数学教育,包含计算机、电气工程、化学、数学、物理等多个学科
    • Social Science:社会科学,包含政治、地理、教育学、经济学、工商管理等多个学科
    • Humanity:人文科学,包含法律、艺术、逻辑学、语文、历史等多个学科
    • Other:其他,其他学科的汇总,包含环境、消防、税务、体育、医学等多个学科

    共有四个难度等级,在图示中使用颜色标记区分,分别是初中(蓝色)、高中(绿色)、大学(黄色)和专业(红色),每个学科对应一个难度等级。


    C-Eval数据预览

    C-Eval的数据形式为4个选项的单选题,包含问题、选项值、答案、解释,形式预览如下。

    C-Eval试题举例

    C-Eval包含三份数据分别是dev,val和test,其中dev数据有答案并且带有答案解释,目的是用来构建CoT思维链的few-shot提示语,val数据集有答案,而test数据集没有答案,一般的,利用dev的few-shot在val数据做离线测试获得C-Eval评分,而在test数据集上提交答案给C-Eval官网获得最终得分。

    数据集 问题+选项 答案 解释说明
    dev
    val ×
    test × ×

    具体的数据在HuggingFace官网中Datasets下搜索ceval-exam既可进行预览,选择Subset为college_programming,数据分割为dev,预览5条大学编程试题。

    C-Eval的HuggingFace数据预览

    C-Eval的Prompt范式

    分别有两种Prompt提示语方式来引导模型给出答案,一种是answer-only,一种是chain-of-thought,answer-only指的是不是用思维链,直接输出答案,而chain-of-thought采用思维链的方式生成中间过程再输出答案。每一种又可以采用zero-shot和few-shot两种形式,对于Base模型,由于没有经过指令微调,因此需要结合few-shot给到范例进行提示,而chat模型采用zero-shot直接对话既可。

    few-shot思维链的提示方式举例

    Python脚本实现C-Eval评估ChatGLM2-6B

    本节采用ChatGLM2-6B项目下的evaluate_ceval.py脚本进行演示,目标是评估ChatGLM2-6B在C-Eval的val数据集下,每个学科的答题正确率和总体平均正确率。
    数据已经提前处理为JSON格式,数据预览如下

    {
        "id":0,
        "inputs_pretokenized":"蓝印花布是一种传统的民间纺织印染工艺品。蓝印花布印制方法始于____。\nA. 汉代\nB. 魏晋时期\nC. 唐代\nD. 宋代",
        "choices_pretokenized":[
            " A",
            " B",
            " C",
            " D"
        ],
        "label":0,
        "targets_pretokenized":[
            "A"
        ]
    }
    

    evaluate_ceval.py对val文件夹下所有科目进行遍历,分别对每个科目进行回答,读取试题的代码如下

    accuracy_dict, count_dict = {}, {}
    with torch.no_grad():
        for entry in glob.glob("./CEval/CEval/val/**/*.jsonl", recursive=True):
            dataset = []
            with open(entry, encoding='utf-8') as file:
                for line in file:
                    # {"id": 9, "inputs_pretokenized": "用户冲击负荷引起的系统频率变动一般不得超过____。\nA. ±0.5Hz\nB. ±0.4Hz\nC. ±0.3Hz\nD. ±0.2Hz", "choices_pretokenized": [" A", " B", " C", " D"], "label": 3, "targets_pretokenized": ["D"]}
                    dataset.append(json.loads(line))
            correct = 0
            dataloader = torch.utils.data.DataLoader(dataset, batch_size=8)
    

    每个科目下的试题以8个为一个batch进行推理,将试题文本改造为固定的chat模型的Prompt模板

    # 模板
    def build_prompt(text):
        return "[Round {}]\n\n问:{}\n\n答:".format(1, text)
    

    然后进行分词编码,调用模型的generate方法进行推理,本次推理结果为回答的中间结果,并不直接映射到选项A、B、C、D。

            for batch in tqdm(dataloader):
                texts = batch["inputs_pretokenized"]
                queries = [build_prompt(query) for query in texts]
                inputs = tokenizer(queries, padding=True, return_tensors="pt", truncation=True, max_length=2048).to('cuda')
                # TODO transformers的generate 批量推理
                outputs = model.generate(**inputs, do_sample=False, max_new_tokens=512)
                intermediate_outputs = []
                for idx in range(len(outputs)):
                    output = outputs.tolist()[idx][len(inputs["input_ids"][idx]):]
                    response = tokenizer.decode(output)
                    intermediate_outputs.append(response)
    

    采用贪婪模式最大推理512个token作为中间结果存储在intermediate_outputs中,以一条数据为例,原始问题的Prompt和推理的中间结果如下

    试题prompt 大模型输出的中间过程

    输出的中间过程表明模型对于问题有思考过程,并且初步给到了答案选D。紧接着我们把中间过程拼接到原始试题后面,并且在末尾加入提示模板extraction_prompt,让大模型基于问题和中间过程,最终输出选项答案。本质上该脚本采用的Prompt方案是zero-shot的CoT思维链。

    extraction_prompt = '综上所述,ABCD中正确的选项是:'
    
    answer_texts = [text + intermediate + "\n" + extraction_prompt for text, intermediate in
                                zip(texts, intermediate_outputs)]
    
    最终的Prompt格式

    重新分词编码之后再次给大模型推理,本次推理只需要拿到下一个token即可,通过return_last_logit参数拿到每个token的在词表的得分分布,通过下标-1拿到最后一个token

                input_tokens = [build_prompt(answer_text) for answer_text in answer_texts]
                inputs = tokenizer(input_tokens, padding=True, return_tensors="pt", truncation=True, max_length=2048).to('cuda:2')
                # TODO return_last_logit 控制了只取最后一个词
                outputs = model(**inputs, return_last_logit=True)
                # TODO [2, 1, 65024] 取最后一个token
                logits = outputs.logits[:, -1]
    

    然后定位到A、B、C、D四个字符的得分,以下一个token在四个字符的概率分布作为依据,取最大者来获得答案

    choices = ["A", "B", "C", "D"]
    choice_tokens = [tokenizer.encode(choice, add_special_tokens=False)[0] for choice in choices]
    
    logits = logits[:, choice_tokens]
    preds = logits.argmax(dim=-1)
    

    例如在该题目中,A、B、C、D的得分分别是9.4844,10.5469, 7.2500, 11.9375,因此大模型回答为D

    >>> logits
    tensor([[ 9.4844, 10.5469,  7.2500, 11.9375]], device='cuda:2',
           dtype=torch.float16, grad_fn=<IndexBackward0>)
    

    本质上ChatGLM2-6B的官方测试代码采用条件概率CLP的方式,考察概率的范围仅限于备选项标号所对应的 token,取其中概率最高的token所对应的选项为模型的推理结果,示意图如下

    image.png

    接下来和样本数据集中的正确选型进行比对即可获得正确率

    correct += (preds.cpu() == batch["label"]).sum().item()
    accuracy = correct / len(dataset)  # TODO 正确率
    accuracy_dict[entry] = accuracy
    count_dict[entry] = len(dataset)
    

    在求得每个科目的正确率之后,再统计一次全局的正确率,代码实现如下

    acc_total, count_total = 0.0, 0
    for key in accuracy_dict:
        acc_total += accuracy_dict[key] * count_dict[key]
        count_total += count_dict[key]
    # TODO 平均正确率
    print(acc_total / count_total)
    

    在笔者的环境下最红ChatGLM2-6B的全局平均正确率为0.536,运行日志如下

    ./CEval/CEval/val/Other/clinical_medicine.jsonl 0.6818181818181818
    100%|██████████| 7/7 [02:11<00:00, 18.83s/it]
      0%|          | 0/3 [00:00<?, ?it/s]./CEval/CEval/val/Other/tax_accountant.jsonl 0.3877551020408163
    100%|██████████| 3/3 [00:41<00:00, 13.95s/it]
      0%|          | 0/7 [00:00<?, ?it/s]./CEval/CEval/val/Other/sports_science.jsonl 0.47368421052631576
    100%|██████████| 7/7 [02:11<00:00, 18.73s/it]
    ./CEval/CEval/val/Other/accountant.jsonl 0.46938775510204084
    0.5364041604754829
    

    相关文章

      网友评论

          本文标题:大模型系列:C-Eval中文大模型评测数据集介绍和实践

          本文链接:https://www.haomeiwen.com/subject/zpekadtx.html