在 LLM 质量保障的日常里,最常被问到的问题之一是:"这个模型到底行不行?"
但"行不行"从来不是一个数字能回答的。同样是"MMLU 78 分",few-shot 设成几?prompt 模板用什么?是不是 chat 格式?温度多少?有没有踩到测试集污染?——任何一项没对齐,跨团队、跨模型、跨版本的数字都不可比。
lm-evaluation-harness 就是为消灭这种"不可比"而生的。它是 EleutherAI 维护的 LLM 评测框架,也是 HuggingFace Open LLM Leaderboard 背后的评测引擎。这篇文章是我把它真正用进质量体系的实战笔记,不堆概念,只讲怎么跑、怎么定制、怎么避坑。
它解决什么问题
一句话:把"评测"固化成可版本化、可复现、可对比的工程件。
- Task(评测任务)用 YAML 声明,数据集、prompt 模板、few-shot、指标全部显式化
- Model(被测对象)通过统一后端接入:HuggingFace 本地模型、vLLM、OpenAI 兼容 API 都能跑
- 输出结构化结果(JSON),可直接入库、diff 对比版本差异
这意味着评测不再是"人肉跑一遍脚本记个分",而是流水线里的一等公民:模型换版本、prompt 改一版、数据集加一批 case,跑同一套 task 就能给出可解释的回归结论。
安装与最小上手
pip install lm-eval
# 以 HuggingFace 后端跑一个 7B 模型
lm_eval \
--model hf \
--model_args pretrained=Qwen/Qwen2.5-7B-Instruct,dtype=bfloat16 \
--tasks mmlu,gsm8k \
--num_fewshot 5 \
--batch_size auto \
--output_path results/
跑完会得到每个 task 的 acc / exact_match 等指标,以及每条样本的原始输出(samples),方便失败 case 回放。这一步已经解决了"口径可复现":把命令行和版本写进 CI 配置,任何人重跑结果一致。
核心概念:Task 是一等公民
一个 task 的 YAML 大致长这样:
task: my_company_qa
dataset_path: json
dataset_kwargs:
data_files:
test: data/qa_test.jsonl
output_type: generate_until
test_split: test
doc_to_text: "问题:{{question}}\n回答:"
doc_to_target: "{{answer}}"
metric_list:
- metric: exact_match
aggregation: mean
关键字段:
doc_to_text/doc_to_target:prompt 模板与参考答案的映射,这是评测口径的核心,改一个字结果就可能漂移output_type:multiple_choice(选择题,看选项 token 概率)或generate_until(生成式,匹配答案)num_fewshot:few-shot 示例数,必须在 config 里显式声明metric_list:指标与聚合方式
所以真正专业的评测实践是:自定义 task 承载业务 golden set,而不是只跑公开 benchmark。公开榜看"通用能力水位",自定义 task 看"业务场景生死"。
把业务数据接进来
以我们对话质检场景为例,把 300 条人工标注的"问题 + 期望回答要点"转成 JSONL:
{"question": "退款多久到账?", "answer": "通常 1-3 个工作日,原路退回"}
{"question": "会员能退吗?", "answer": "虚拟商品不支持退款,可申请补偿"}
再用上面的 YAML 声明一个 task,评测时 --tasks my_company_qa 即可。相比手写脚本,好处是:数据、prompt、指标三件套被锁进同一个文件,改一处 diff 一目了然。
工程化:把它接进流水线
单次跑分没意义,持续跑、可对比才有意义。几点实操建议:
- 固定版本:
lm-eval、数据集、模型 commit 全部锁定,评测环境用容器镜像 - 结果落库:
--output_path的 JSON 解析后写入评测平台,按"模型版本 × task × 日期"建索引 - diff 而非绝对分:新模型版本 vs 基线,看每个 task 的涨跌与失败样本变化,而不是只看总分
- 门禁:核心 task 掉分超过阈值(如 2%)直接卡发布——这就是质量门禁的入口
- 抽样复核:
generate_until类 task 的自动指标有误判,定期人工抽检失败样本,校准指标与人工判断的一致性
几个真实的坑
- prompt 模板不一致:同一模型,
问题:{q}和Q: {q}能差好几分。模板必须进版本管理 - few-shot 数不统一:公开分和自查分对不上,80% 的锅在 few-shot 设置
- chat vs base 模型:Instruct 模型若未用 chat template 评测,分数会显著偏低;
--apply_chat_template按需开启 - 数据污染:自建 golden set 要防止被训练数据"见过",评测集与训练集做去重
- 采样参数:生成类任务建议贪心解码(temperature=0)保证可复现;否则每次结果有抖动
- batch_size=auto 只是省心:显存吃紧时手动设小一点,避免 OOM 打断评测
在质量体系里的定位
我的理解是:lm-evaluation-harness 解决的是"可复现的批量回归",但它替代不了两件事——真实流量的线上评测,和人工的精细判断。
比较合理的分层是:
- 公开 benchmark(MMLU/GSM8K 等):通用能力水位线,横向对比同类模型
- 业务 golden set(自定义 task):验收标准,进发布门禁
- 线上 A/B 与人工抽检:真实分布兜底,校准离线结论
三层各司其职,评测才不会沦为"跑分自嗨"。
小结
lm-evaluation-harness 的价值不在于它能跑多少 benchmark,而在于它把评测的"口径"变成了代码资产:task 可版本化、结果可对比、流程可进 CI。对于做 LLM 质量保障的人来说,它是把"我觉得还行"变成"数据证明有提升"的最短路径。
一句话建议:先跑通一个公开 task 建立手感,再把你自己的 300 条业务 case 变成第一个自定义 task——从那一刻起,评测才真正开始为你的业务服务。