lm-evaluation-harness 中的 CommonsenseQA 任务:MMLU 风格提示的常识问答评测实战与源码解析
【免费下载链接】lm-evaluation-harnessA framework for few-shot evaluation of language models.项目地址: https://gitcode.com/GitHub_Trending/lm/lm-evaluation-harness
本文围绕 lm-evaluation-harness 仓库内 lm_eval/tasks/commonsense_qa 目录下的任务实现展开,系统讲解 CommonsenseQA 数据集背景、default.yaml任务配置的每个字段、MMLU 风格提示模板的由来,以及multiple_choice输出类型在框架底层的完整评测链路。读者学完后,既能直接用 CLI 跑通commonsense_qa评测并正确解读 acc 指标,也能理解如何基于该任务模板扩展自定义的常识问答评测。
一、数据集与论文背景
CommonsenseQA 是论文COMMONSENSEQA: A Question Answering Challenge Targeting Commonsense Knowledge(Talmor 等人,NAACL 2019,arXiv:1811.00937)提出的多项选择问答数据集,其核心目标是考察模型是否具备运用多种类型常识知识预测正确答案的能力。
从任务 README(lm_eval/tasks/commonsense_qa/README.md)可以确认以下关键事实:
- 数据集共包含12,102 道题目;
- 每道题包含1 个正确答案和 4 个干扰项(即 5 选 1,对应选项 A–E);
- 题目覆盖不同维度的常识知识,用于检验语言模型对世界知识的推理能力。
论文摘要与全文可通过 arXiv 编号 1811.00937 检索获取,数据集主页由 TAU NLP 实验室维护。若在学术工作中引用该数据集,任务 README 中提供了完整 BibTeX:
@inproceedings{talmor-etal-2019-commonsenseqa, title = "{C}ommonsense{QA}: A Question Answering Challenge Targeting Commonsense Knowledge", author = "Talmor, Alon and Herzig, Jonathan and Lourie, Nicholas and Berant, Jonathan", booktitle = "Proceedings of the 2019 Conference of the North {A}merican Chapter of the Association for Computational Linguistics: Human Language Technologies, Volume 1 (Long and Short Papers)", month = jun, year = "2019", address = "Minneapolis, Minnesota", publisher = "Association for Computational Linguistics", url = "https://aclanthology.org/N19-1421", doi = "10.18653/v1/N19-1421", pages = "4149--4158", archivePrefix = "arXiv", eprint = "1811.00937", primaryClass = "cs", }二、任务注册与"random"划分
任务 README 的Groups and Tasks一节明确了该任务在当前仓库中的组织方式:
- Groups:
commonsense_qa目前尚未归属于任何评测组(README 中标注为 "Not part of a group yet"),是独立存在的单一任务; - Tasks:注册的任务名为
commonsense_qa,对应论文中的"random" 划分(random split),并采用MMLU 风格提示模板——按 README 的说明,这一提示格式"(推测)与 Llama 系列评测所使用的格式一致"。
"random" 划分是 CommonsenseQA 论文中的一种数据切分方式,其干扰项的采样策略区别于其他变体;本仓库实现的是该划分,因此评测结果可直接与论文及社区后续采用相同划分的工作进行对照。
此外,README 附带的贡献清单(Checklist)确认了该任务满足新增基准的两项硬性要求:它是文献中已有的基准(is the task an existing benchmark in the literature),且已正确引用引入该任务的原始论文——这也解释了为什么任务目录中同时保留了论文标题、摘要入口与完整引用信息。
三、核心配置:default.yaml 逐字段解析
任务的实际行为完全由 lm_eval/tasks/commonsense_qa/default.yaml 定义,这是该任务唯一且默认的 YAML 配置文件。全文如下:
task: commonsense_qa dataset_path: tau/commonsense_qa training_split: train validation_split: validation output_type: multiple_choice doc_to_text: "Question: {{ question.strip() }}\nA. {{choices['text'][0]}}\nB. {{choices['text'][1]}}\nC. {{choices['text'][2]}}\nD. {{choices['text'][3]}}\nE. {{choices['text'][4]}}\nAnswer:" doc_to_target: answerKey doc_to_choice: ['A', 'B', 'C', 'D', 'E'] metric_list: - metric: acc aggregation: mean higher_is_better: true各字段含义与设计要点如下:
| 字段 | 值 | 作用与说明 |
|---|---|---|
task | commonsense_qa | 任务唯一标识名,CLI 的--tasks参数即用此名引用 |
dataset_path | tau/commonsense_qa | Hugging Face Datasets 上的数据集标识,框架运行时通过该标识加载数据 |
training_split | train | 训练划分,主要用于 few-shot 示例采样(未显式设置fewshot_split时,框架默认从该划分取示例) |
validation_split | validation | 验证划分,供开发调参时使用 |
output_type | multiple_choice | 输出类型,决定评测请求构造与结果聚合方式(详见下文源码解析) |
doc_to_text | 提示模板字符串 | 把一条数据渲染成发给模型的上下文(prompt),见下节详解 |
doc_to_target | answerKey | 取数据字段answerKey作为标准答案(gold label),其取值为'A'–'E'之一 |
doc_to_choice | ['A', 'B', 'C', 'D', 'E'] | 候选选项列表,multiple_choice模式下框架会逐个计算每个候选的续写对数似然 |
metric_list | acc/mean/higher_is_better: true | 评测指标为准确率,按均值聚合,越大越好 |
值得注意的是:该配置没有显式声明test_split,此时框架会回退到默认的test划分作为正式评测集;tau/commonsense_qa在 Hugging Face 上提供train/validation/test三个划分,其中test划分(约 1140 条)即为默认评测目标。若希望用验证集调试,可以自行调整或通过脚本指定划分。
四、MMLU 风格提示模板
doc_to_text是整个配置中最体现设计意图的字段:
Question: {{ question.strip() }}\nA. {{choices['text'][0]}}\nB. {{choices['text'][1]}}\nC. {{choices['text'][2]}}\nD. {{choices['text'][3]}}\nE. {{choices['text'][4]}}\nAnswer:渲染后,一条数据会变成如下形式的上下文(\n为真实换行):
Question: 常识问题正文 A. 选项1 B. 选项2 C. 选项3 D. 选项4 E. 选项5 Answer:其"Question + 选项字母 + Answer:"的结构正是 MMLU 任务的标志性提示格式。将它与仓库中 lm_eval/tasks/mmlu/default/_default_template_yaml 的 MMLU 模板对比即可看出同源性:
doc_to_text: "{{question.strip()}}\nA. {{choices[0]}}\nB. {{choices[1]}}\nC. {{choices[2]}}\nD. {{choices[3]}}\nAnswer:" doc_to_choice: ["A", "B", "C", "D"]两者唯一的实质性差异是:CommonsenseQA 是 5 选 1(A–E),MMLU 是 4 选 1(A–D)。此外doc_to_text中对question.strip()的调用去除了题面首尾空白,保证提示整洁、避免在选项前引入多余空格,这类细节会直接影响以对数似然比较为基础的multiple_choice评测结果。
五、multiple_choice 输出类型的底层评测链路
output_type: multiple_choice是理解本任务运行机制的关键。它不属于loglikelihood/generate_until等基础类型,而是框架在 lm_eval/api/task.py 中专门实现的组合逻辑,分为请求构造与结果处理两个阶段。
5.1 请求构造:每个选项一个 loglikelihood 请求
在Task.construct_requests(lm_eval/api/task.py#L1362-L1445)中,当OUTPUT_TYPE == "multiple_choice"时,框架取出doc_to_choice得到的候选列表,把"上下文 + 目标分隔符 + 选项文本"组成续写对:
choices = self.doc_to_choice(doc) target_delimiter = self.config.target_delimiter ... arguments = [(ctx, f"{target_delimiter}{cont}") for cont in choices]随后为每一个选项各生成一个request_type="loglikelihood"的Instance:
request_list = [ Instance(request_type="loglikelihood", doc=doc, arguments=arg, idx=i, **kwargs) for i, arg in enumerate(arguments) ]也就是说,一道 CommonsenseQA 题目会被拆成5 个独立的 loglikelihood 请求,分别计算P(选项 | Question + A. … E. 选项列表 + Answer:)。因此,评测耗时与选项数量成正比——这也是multiple_choice任务通常比单 token 续写任务慢的原因。
5.2 结果处理:argmax 选取最高对数似然选项
在Task.process_results(lm_eval/api/task.py#L1489-L1512)中,框架对每个选项的 loglikelihood 结果做归一化取最大:
lls, is_greedy = zip(*results, strict=True) choices = self.doc_to_choice(doc) completion_len = np.array([float(len(i)) for i in choices]) byte_length = np.array([float(len(i.encode("utf-8"))) for i in choices]) ... pred = np.argmax(lls) # 原始对数似然 pred_norm = np.argmax(lls / completion_len) # 按字符长度归一化 pred_byte = np.argmax(lls / byte_length) # 按字节长度归一化这里生成了三种预测口径:原始对数似然pred、按字符长度归一化的pred_norm、按字节长度归一化的pred_byte。它们分别对应acc、acc_norm、acc_bytes三类指标的计算输入——由于生成式模型对较长选项天然更"不利"(对数似然为负值、长度越长越负),长度归一化可缓解选项长度偏差,这也是社区在多项选择评测中广泛使用acc_norm的原因。
5.3 指标注册
acc、acc_norm、acc_bytes、acc_mutual_info等指标均在 lm_eval/api/metrics.py 中通过@register_metric注册(见 acc_fn 等定义),并声明了适用的output_type与aggregation="mean"、higher_is_better=True。本任务配置只启用了acc,即:对每题比较模型预测选项与answerKey标准答案,命中记 1,最终取全部题目的均值作为任务分数。若想对比长度归一化效果,可以在metric_list中追加acc_norm条目,框架会自动复用已构造的请求。
六、运行评测
使用命令行工具即可直接评测任意 Hugging Face 模型。基础命令如下(详细 CLI 说明见 docs/README.md 与 docs/interface.md):
lm_eval --model hf \ --model_args pretrained=EleutherAI/pythia-14m \ --tasks commonsense_qa \ --device cuda:0 \ --batch_size auto \ --limit 5--tasks commonsense_qa:指定本任务(支持逗号分隔多个任务);--model hf/--model_args pretrained=...:使用 Hugging Face Transformers 模型,pretrained可以是 Hub 模型名或本地权重路径;--device cuda:0:指定计算设备;--batch_size auto:自动选择批大小;--limit 5:只评测前 5 条样本,用于快速验证链路是否跑通(正式评测请去掉该参数以覆盖完整 test 集)。
few-shot 相关参数可直接透传:
lm_eval --model hf \ --model_args pretrained=meta-llama/Llama-3.2-1B \ --tasks commonsense_qa \ --num_fewshot 5 \ --batch_size auto--num_fewshot 5会从training_split(train划分)中采样 5 条示例拼接到每条测试样本的提示之前。需要说明的是,任务配置本身未设置num_fewshot,因此默认按 0-shot 评测;README 中"MMLU 风格提示(推测为 Llama 评测所用)"的描述,意味着若要复现特定评测设置,需自行确认对应的 few-shot 数量与示例格式(few-shot 采样器的配置方式可参考 lm_eval/tasks/mmlu/default/_default_template_yaml 中的fewshot_config写法)。
运行结束后,终端会输出形如下面的结果摘要,其中acc即本任务的最终得分:
| Tasks |Version|Filter|n-shot| Metric | |Value| |Stderr| |----------|------|------|-----:|-----------|---|----:|---|-----:| |commonsense_qa| 1|none | 0|acc |↑ |0.41 |± |0.015 |七、扩展与自定义
基于这份最小配置,可以做以下常见扩展:
- 更换提示模板:修改
doc_to_text即可改变评测提示,例如在选项前加入序号、调整问题表述等;修改doc_to_choice与doc_to_target可适配其他数据字段结构; - 增加长度归一化指标:在
metric_list中追加acc_norm(以及acc_mutual_info,后者会额外计算无条件对数似然,见 task.py 的 mutual info 分支),观察选项长度对得分的影响; - 接入对话式模型:
multiple_choice模式支持apply_chat_template,可在评测脚本中开启聊天模板以匹配指令微调模型的推理习惯; - 作为模板孵化新任务:CommonsenseQA 是典型的"HF 数据集 + YAML 配置"式任务范例,新增基准时可直接复制该目录并替换
dataset_path、字段映射与提示模板,具体规范见 docs/new_task_guide.md 和 docs/config_files.md。
小结
commonsense_qa任务以极简的 YAML 配置完整落地了一个经典 5 选 1 常识问答基准:数据集层面对接tau/commonsense_qa的 random 划分,提示层面采用与 MMLU 同源的"Question + 选项 + Answer:"格式,评测层面则由框架的multiple_choice输出类型自动完成"逐选项对数似然 + argmax 判分"的闭环。理解这份配置与 lm_eval/api/task.py 的对应关系后,无论是复现论文结果、横向对比模型常识推理能力,还是以此为蓝本接入新数据集,都能做到有据可依、开箱即用。
【免费下载链接】lm-evaluation-harnessA framework for few-shot evaluation of language models.项目地址: https://gitcode.com/GitHub_Trending/lm/lm-evaluation-harness
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考