lm-eval-harness 实操指南
更新于 2026-04-23
上一篇 benchmark 生态全景 §6 介绍了 lm-eval-harness 是开源评测生态的核心框架、覆盖 200+ 任务。那篇讲的是”这个工具在生态里的位置”和”为什么值得学”。本篇转向实操:Task YAML 系统如何运作、17+ 模型后端怎么接入、评测结果怎么解读。读完应能跑通一次完整评测、理解每个字段的含义、规避常见坑。
1 三层架构
lm-eval 的设计干净地分成三层,职责互不交叠:
1.1 Task 层
YAML 驱动的任务定义。每个 task 就是一个 YAML 文件,用 Jinja2 模板定义 prompt 格式、用 metric_list 指定评估指标。框架内置 208+ 任务,通过 lm_eval/tasks/ 下的目录自动注册。任务可以单独用(如 hellaswag),也可以组合成 group(如 mmlu 包含 57 个子任务)。
1.2 Model 层
统一抽象 LM 基类,定义三个核心方法:
loglikelihood(context, continuation):给定 context,计算 continuation 的条件 log-probability。用于 yes/no 判断、真假命题、选择题等概率比较场景。loglikelihood_rolling(string):无条件计算整段文本的 perplexity。用于 WikiText 等纯语言建模评估。generate_until(context, stop_tokens):模型从 context 开始自由生成,直到遇到停止符。用于 GSM8K、HumanEval 等需要生成完整答案的任务。
17+ 后端适配器实现此接口——hf(HuggingFace transformers)、vllm、openai-chat-completions、anthropic、openvino、gguf、sglang、nemo 等。对评测框架来说,模型只是”能回答这三个方法的黑盒”。
1.3 Evaluation Loop
编排层。把 Task 和 Model 粘在一起:
- 从 task YAML 读取数据集
- 用 Jinja2 模板把每条样本渲染成 prompt
- 从
training_split采样 few-shot 示例拼装 - 打包成 request 发给 Model 后端
- 收到 logprobs 或生成文本
- 执行 filter pipeline(如 CoT 提取)
- 计算 metric、聚合、输出 JSON
1.4 output_type 的四种模式
Task YAML 用 output_type 字段告诉 Evaluation Loop 如何构造请求、如何打分。一共四种:
| output_type | 用途 | 典型任务 |
|---|---|---|
loglikelihood | 给定 context,计算指定 continuation 的 logprob | BoolQ(yes/no)、真假命题 |
multiple_choice | 对每个选项做 loglikelihood,取概率最高者 | MMLU、HellaSwag、ARC |
generate_until | 模型自由生成,用 regex / exact match / 代码执行判分 | GSM8K、HumanEval、TriviaQA |
loglikelihood_rolling | 计算整段文本的 perplexity,无 context | WikiText、Lambada |
上面的组件展示了一个评测请求的完整生命周期,切换底部的 output_type 可以看到 Evaluation Loop 中变化的环节——例如 multiple_choice 会对每个选项各发一次 loglikelihood 请求、generate_until 会多出一个 filter 阶段。
2 Task YAML 深度剖析
以 HellaSwag 为主例,逐字段拆解完整 YAML 配置:
task: hellaswag
dataset_path: Rowan/hellaswag
dataset_name: null
output_type: multiple_choice
training_split: train
validation_split: validation
doc_to_text: "{{query}}" # Jinja2 模板,从数据集字段渲染 prompt
doc_to_target: "{{label}}" # 正确答案(多选场景是选项索引)
doc_to_choice: "choices" # 选项列表字段名
num_fewshot: 0
process_docs: !function utils.process_docs # Python 预处理函数
metric_list:
- metric: acc
aggregation: mean
higher_is_better: true
- metric: acc_norm
aggregation: mean
higher_is_better: true
metadata:
version: 1.0
2.1 关键字段详解
doc_to_text/doc_to_target/doc_to_choice:Jinja2 模板,可访问数据集任意字段,支持条件、循环等完整 Jinja2 语法。HellaSwag 的doc_to_text: "{{query}}"会直接取数据集的query字段作为 prompt。process_docs:!function标签引用 Python 函数做预处理。HellaSwag 的实现做了文本清理(去除方括号和标题标记,正则移除括号内容),让 prompt 更干净。metric_list:支持acc、acc_norm(长度归一化)、exact_match、bleu、rouge、perplexity等。可以同时指定多个 metric。num_fewshot:few-shot 示例数。从training_split采样,0 表示 zero-shot。filter_list:后处理 pipeline,用于 CoT 等场景。典型管线是先regex提取答案再take_first选第一个匹配。
2.2 Task Group 与继承
- Group YAML:用
task字段列出子任务聚合,aggregate_metric_list定义聚合方式。例如 MMLU 的 57 个学科子任务按主题打包成一个 group。 include继承:子 task 可以include: parent_task.yaml继承父配置,只覆盖差异字段。减少重复配置。
2.3 外部 Task 注册
--include_path /my/tasks/ 自动扫描目录下的所有 YAML 并注册。自定义 task 不需要改源码,只需写 YAML + Python utils(如果需要 process_docs / 自定义 metric)。
组件的三个 tab 展示三种 output_type 的典型配置——HellaSwag(multiple_choice)、GSM8K(generate_until + filter_list)、WikiText(loglikelihood_rolling),点击任意字段查看说明。
3 模型后端接入
挑 5 个最常用后端讲配置。所有后端共用 --tasks、--batch_size、--num_fewshot、--limit 等 CLI 参数,只是 --model 和 --model_args 形式不同。
3.1 hf(HuggingFace transformers)— 参考基线
lm_eval --model hf \
--model_args pretrained=meta-llama/Llama-3-8B,dtype=float16 \
--tasks hellaswag --batch_size auto
- 默认后端,数值结果最权威。其他后端的准确性以 hf 为 reference。
dtype:float16/bfloat16/float32parallelize=True:model parallelism(单机多卡跑大模型)accelerate launch -m lm_eval ...:data parallelism(多卡并行跑同一模型、加速)
3.2 vllm — 高吞吐
lm_eval --model vllm \
--model_args pretrained=meta-llama/Llama-3-8B,tensor_parallel_size=2,gpu_memory_utilization=0.8 \
--tasks mmlu --batch_size auto
- 吞吐量远高于 hf,适合大规模评测(200k+ 样本)。
- 注意:vLLM 的数值可能与 hf 有微小差异(浮点精度、连续批处理策略)。paper-quality 的结果应以 hf 为准。
- 可以用
scripts/model_comparator.py对比两个后端输出,定位差异来源。
3.3 openai-chat-completions — API 模型
lm_eval --model openai-chat-completions \
--model_args model=gpt-4o \
--tasks mmlu --apply_chat_template
- 支持 OpenAI 全系、也支持 Anthropic(
--model anthropic-chat-completions)。 --apply_chat_template:自动把 prompt 封装成 chat 格式。对 RLHF 模型几乎必加。--system_instruction "You are...":注入 system prompt。- 注意 rate limit 和 cost:
--batch_size对 API 模型通常设小一点,配合--num_concurrent。
3.4 openvino — Intel 推理
lm_eval --model openvino \
--model_args pretrained=OpenVINO/llama-3-8b-ov,dtype=int4 \
--tasks hellaswag
- 需要 Optimum-Intel 转换过的 OpenVINO IR 格式模型。
- 支持 INT4/INT8 量化模型的精度评测——量化是否损失准确性,靠这个验证。
- 在 Intel CPU / iGPU / NPU 上跑量化模型的精度回归测试非常实用。
3.5 gguf — llama.cpp 服务
lm_eval --model gguf \
--model_args base_url=http://localhost:8080 \
--tasks hellaswag
- 通过 llama.cpp server 连接。后端实现文件为
gguf.py,该文件同时注册了gguf和ggml两个后端名(指向同一实现)。 - 关键坑:必须在
--model_args里明确 tokenizer,否则 tokenizer reconstruction 可能无限挂起。
3.6 多 GPU 配置
- Model parallelism(大模型装不下单卡):hf 用
parallelize=True,vllm 用tensor_parallel_size=N。 - Data parallelism(加速评测):
accelerate launch --num_processes N -m lm_eval ...。 - 两者可以组合:大模型 × 数据并行 = tensor_parallel_size × 多进程。
4 结果解读与常见坑
4.1 结果 JSON 结构
{
"results": {
"hellaswag": {
"acc": 0.7523,
"acc,stderr": 0.0043,
"acc_norm": 0.7891,
"acc_norm,stderr": 0.0041
}
},
"configs": { "hellaswag": { "...": "..." } },
"versions": { "hellaswag": 1.0 },
"n-shot": { "hellaswag": 0 },
"n-samples": { "hellaswag": 10042 }
}
accvsacc_norm:acc_norm对选项长度做归一化(用 token 数归一化 logprob 之和)。HellaSwag 等选项长度差异大的 task 中acc_norm更公平。,stderr:标准误差。用来判断模型间分数差异是否在统计意义上显著(两个模型差 0.5% 但 stderr 是 0.4%,基本不显著)。--log_samples:保存每条样本的输入、输出、logprob。做 error analysis 必开。
4.2 常见坑(7 个)
- Base install 无 torch:v0.4+ 基础包不自带 torch / transformers,要单独
pip install lm-eval[hf]或装你用的后端依赖。 - vLLM vs HF 数值差异:浮点精度和连续批处理导致 0.5–2% 分数差异。一篇论文两个模型都用 vllm 可以横向比,但和别人论文里 hf 的数字不要直接比。
- GGUF tokenizer 挂起:不显式配置 tokenizer 时 reconstruction 可能无限等待。必须在
--model_args里写清楚。 - SGLang OOM:
--batch_size auto在 SGLang 上可能 OOM。手动设 batch size +mem_fraction_static=0.7更稳。 - MPS(Apple Silicon)精度问题:部分算子在 MPS 上与 CPU/CUDA 结果不完全一致。Apple Silicon 上跑的结果要和 CPU 交叉验证。
- CoT 模型推理链污染评分:带
<think>块的模型(如 Qwen-QwQ)直接评分会把思考过程算进去。用think_end_token参数剥离推理链后再评分。 - Task 版本不匹配:
metadata.version变化时分数不能直接对比。新旧 prompt 模板可能差 1–3%。跨版本比较一定要对齐version字段。
5 总结
- 自定义 task 最佳实践:先
--limit 5跑几条调模板 →--log_samples看渲染出的 prompt 是否正确 → 确认 filter pipeline 抽出了期望答案 → 正式跑全量。 - Python API:
lm_eval.simple_evaluate()用于脚本化的评测流水线,适合集成到 CI/CD 做回归测试。 - 集成:
--wandb_args project=...自动上传到 Weights & Biases;--hf_hub_log_args repo_id=...推到 HuggingFace Hub,方便团队共享。
想看框架在评测生态中的定位,回去看 benchmark 生态全景 §6。想看具体 benchmark 的内容和难度分布,看 推理 benchmark、代码 benchmark。