从零搭建自定义评估循环:lm-evaluation-harness 实战全攻略

📅 2026/8/21 15:58:55
从零搭建自定义评估循环:lm-evaluation-harness 实战全攻略
从零搭建自定义评估循环lm-evaluation-harness 实战全攻略【免费下载链接】lm-evaluation-harnessA framework for few-shot evaluation of language models.项目地址: https://gitcode.com/GitHub_Trending/lm/lm-evaluation-harness开篇当 62 分的 MMLU 救不了你的线上场景假设你所在的公司微调了一个 7B 模型领导拿到评测报告MMLU 62 分表现不错。但模型一上线就露馅了——客服场景里它给出的回答措辞对了但关键信息缺失内部质检要求只要答案包含关键实体就算对而默认的严格匹配准确率把所有半对答案全部判死。问题出在哪你用的是别人的评估循环不是你的。lm-evaluation-harness 这个语言模型评测框架最容易被忽视的恰恰是它真正的威力simple_evaluate和evaluate两个核心函数位于lm_eval/evaluator.py可以让你彻底接管评估流程——自定义提示、自定义指标、接入非标准模型、加缓存、跑分布式。读完本文你将亲手从零搭建一套属于自己的自定义评估循环并带走理解评估循环的内部骨架请求构建 → 模型推理 → 指标聚合 → 报告生成掌握register_metricoverride_metric注册自定义评估指标的正确姿势学会用evaluate函数把非 HuggingFace 格式的模型塞进评估流程一套可直接复用的缓存、批处理、分布式配置方案一份基于真实踩坑经验整理的避坑清单主线目标为一家客服质检 内容审核场景的微调模型搭建一套带自定义容错指标、支持非标准模型接入的质检评估循环。第一步先拆开评估循环的骨架别把它当黑盒很多人用 lm-evaluation-harness 只用 CLI 一行命令lm_eval --model hf --model_args pretrainedyour-model --tasks mmlu这没问题但 CLI 是成品而自定义评估循环要从半成品开始。框架把评估流程拆成了两个层级的入口理解它们的区别是第一步入口所在模块职责适用场景simple_evaluatelm_eval/evaluator.py从模型名/模型对象 任务名出发一条龙完成初始化与评估大多数标准评估evaluatelm_eval/evaluator.py接收已实例化的 LM 对象和已加载的 task_dict只负责执行评估自定义模型接入、多模型对比、需要控制任务实例的场景先画一张评估循环的内部流程图后面所有操作都围绕它展开每个箭头都是一处你可以介入的扩展点任务定义决定了请求长什么样YAML 配置指标函数决定怎么算对register_metricLM 对象决定谁来推理自定义模型类缓存决定算不算重复活。from lm_eval import evaluator # 最简自定义评估循环一行核心调用 results evaluator.simple_evaluate( modelgpt2, # 模型名或 LM 对象 tasks[arc_easy, hellaswag], # 任务列表 num_fewshot5, # 少样本示例数 batch_size4, # 批大小 limit0.1, # 只跑 10% 数据快速验证循环本身 )这行代码背后发生的就是上面流程图里的 7 个步骤。先跑通它再谈定制——这是搭建自定义评估循环的第一条铁律。第二步让评估循环听话——用参数控制喂给模型的数据质检评估循环的第一需求是可控。默认循环会吃完整数据集但调试时你只想看几个样本跑正式评估时你又想固定抽样的随机种子保证可复现。lm-evaluation-harness 给了你三把钥匙。钥匙一limit与samples——控制喂多少数据# 按比例截断快速验证流程 results evaluator.simple_evaluate(modelgpt2, tasks[mmlu], limit0.05) # 精确指定样本只评估你关心的那几条 results evaluator.simple_evaluate( modelgpt2, tasks[mmlu_astronomy], samples{mmlu_astronomy: [0, 3, 6, 10]}, )⚠️ 注意limit和samples是互斥的同时传入会直接抛ValueError——源码里就是这么校验的别踩。钥匙二num_fewshot——控制上下文里塞几个示例少样本示例直接决定评测难度。对于客服质检场景你甚至希望不给任何示例0-shot来模拟冷启动或者给 3 个示例模拟有历史对话参考的情况for n in [0, 1, 3, 5]: res evaluator.simple_evaluate(modelgpt2, tasks[hellaswag], num_fewshotn) print(f{n}-shot acc: {res[results][hellaswag][acc,none]})钥匙三gen_kwargs——控制生成行为对于生成式任务如客服回复质检默认的贪心解码未必合适。你可以通过gen_kwargs注入采样参数results evaluator.simple_evaluate( modelgpt2, tasks[gsm8k], gen_kwargs{temperature: 0.7, max_gen_toks: 512, until: [\n]}, )到这里你已经能用参数遥控评估循环的输入侧了。但质检场景最痛的还不是输入而是**怎么算对**——下一步我们来动手术。第三步把自定义指标装进评估循环——注册一个答对一半也给分的指标默认的acc是严格字符串匹配预测和参考答案完全一致才得 1 分。客服质检的真实需求往往是容错匹配——回复包含关键实体 退款 且不含否定词 拒绝就判定为合格。这种指标内置循环里没有得自己造。机制一register_metric注册指标函数指标注册的入口在lm_eval/api/registry.py装饰器的三个关键参数参数含义示例metric指标注册名YAML 里引用它acc_contains_keywordhigher_is_better分数越高越好影响排序与展示Trueaggregation逐样本分数如何聚合成总分mean取平均from lm_eval.api.registry import register_metric register_metric( metricacc_contains_keyword, # 注册名 higher_is_betterTrue, aggregationmean, # 逐样本得分取平均 ) def acc_contains_keyword(items): 带容错的关键词匹配预测里包含参考答案关键实体即得分 # items: (gold, prediction) 元组列表 golds, preds zip(*items) scores [] for gold, pred in zip(golds, preds): gold str(gold).strip().lower() pred str(pred).strip().lower() # 规则包含实体给 0.5完全一致给 1否则 0 if pred gold: scores.append(1.0) elif any(tok in pred for tok in gold.split()) and len(gold.split()) 1: scores.append(0.5) else: scores.append(0.0) return sum(scores) / len(scores) if scores else 0.0 内置指标acc、acc_norm、brier_score、f1等都在lm_eval/api/metrics.py里用同样的register_metric方式定义——你写的自定义指标和它们是平级的框架不会区别对待。机制二override_metric运行时替换指标注册完还不够你得让具体的任务实例用上这个指标。Task类提供了override_metric方法lm_eval/api/task.pyfrom lm_eval.tasks import TaskManager tm TaskManager() task_dict tm.load_task_or_group(customer_qa) # 加载你的任务 for task_name, task in task_dict[tasks].items(): task.override_metric(metric_nameacc_contains_keyword) # 注意此时任务已实例化要走 evaluate 而非 simple_evaluate from lm_eval import evaluator from lm_eval.models.huggingface import HFLM results evaluator.evaluate( lmHFLM(pretrainedyour-model), task_dicttask_dict, )默认 vs 自定义差异一目了然维度默认循环自定义评估循环指标来源内置acc/f1等register_metric注册 override_metric替换判定粒度全对才得分可做部分得分、关键词匹配、规则逻辑与业务对齐需要业务迁就指标指标迁就业务实现成本零一个函数 一行 override到这里你的质检评估循环已经有了自己的打分标准。但下一个现实问题马上来了团队里那个模型不是 HuggingFace 格式是内部推理引擎输出的结果——怎么接进来第四步接非标准模型——用 evaluate 函数接管推理环节这是自定义评估循环最能打的一步。CLI 方式要求模型必须被框架识别而代码级的evaluate只要求你传入一个实现了LM接口的对象lm_eval/api/model.py。方案 A写一个 LM 子类核心是实现两个方法框架的所有评估都建立在它们之上from lm_eval.api.model import LM class MyInternalEngine(LM): 内部推理引擎适配器 def __init__(self, engine_url: str): super().__init__() self.url engine_url def _loglikelihood_tokens(self, requests, disable_tqdmFalse): 批量计算给定 token 序列的 log-likelihoodMCQ 类任务需要 # 请求打到内部引擎返回 [(logprob, is_greedy), ...] ... def _generate_until(self, requests): 批量生成文本生成式任务需要 # 返回 [generated_text, ...] ...方案 B借用现成适配器更省力项目examples/transformer-lens.py提供了一个现成思路如果你的模型能包一层 HuggingFace 风格接口就无需从零实现 LM 接口——直接包成 HF 兼容模型喂给HFLM。TransformerLens 的HookedTransformer就是这么接入的from lm_eval.models.huggingface import HFLM class HFLikeModelAdapter(nn.Module): 把自定义模型包装成 HF 兼容接口 def __init__(self, model): super().__init__() self.model model self.tokenizer model.tokenizer self.config AutoConfig.from_pretrained(model.cfg.tokenizer_name) self.device model.cfg.device def forward(self, input_idsNone, attention_maskNone, **kwargs): output self.model(input_ids, attention_maskattention_mask, **kwargs) if not hasattr(output, logits): output.logits output return output results evaluator.simple_evaluate( modelHFLM(pretrainedHFLikeModelAdapter(my_model), tokenizermy_tokenizer), tasks[customer_qa], )方案 A vs 方案 B 怎么选你的模型有标准 forward 接口 → 方案 B成本最低模型是纯 API / 纯 C 引擎没有 PyTorch 前向 → 方案 A需要实现两个核心方法两者都走evaluate/simple_evaluate评估循环的其他环节指标、聚合、报告完全复用。到这里你的质检评估循环已经集齐了三大组件任务可控输入、指标自定义打分、模型任意接入。剩下的是让它在真实规模下跑得动、跑得快、跑得稳。第五步给评估循环加油门和保险——缓存、批处理与分布式当评估任务从 10 条样本膨胀到几万条循环的性能和稳定性就变成了主要矛盾。油门一两级缓存避免重复计算框架提供两级缓存可以叠加使用缓存层级参数缓存内容收益请求缓存cache_requestsTrue请求构建结果输入组装、少样本拼接省 CPU 与 IO模型缓存use_cachecache.db模型推理输出SQLite 数据库省 GPU 算力# 第一次跑慢写缓存 results evaluator.simple_evaluate( modelgpt2, tasks[customer_qa], cache_requestsTrue, use_cacheeval_cache.db, ) # 第二次跑同样的 (模型, 任务, 参数) 组合命中缓存秒出结果 results2 evaluator.simple_evaluate( modelgpt2, tasks[customer_qa], cache_requestsTrue, use_cacheeval_cache.db, )⚠️ 模型缓存按参数指纹命中。换了模型或改了gen_kwargs指纹就变不会误用旧结果——这是它比你自己写字典缓存安全的地方。油门二自动批处理results evaluator.simple_evaluate( modellarge-model, tasks[mmlu], batch_sizeauto, # 自动探测最优批大小 max_batch_size32, # 防止 OOM 的上限 )batch_sizeauto会让循环自动探测显存能容纳的最大批大小max_batch_size是安全阀防止探测过程直接吃爆显存。保险分布式多卡评估大规模评估时用torch.distributed.run拉起多进程评估循环内部会自动按 rank 分片CUDA_VISIBLE_DEVICES0,1,2,3 python -m torch.distributed.run --nproc_per_node4 \ -m lm_eval --model hf --model_args pretrainedyour-model \ --tasks customer_qa --batch_size auto至此你的质检评估循环已经具备生产环境的三要素正确自定义指标、灵活任意模型、高效缓存分布式。但正式上线前还有几个坑值得提前绕开。避坑清单5 个最常见的自定义评估循环翻车点1. YAML 里的\n死活不生效现象until里的换行终止符没起作用生成结果异常。原因单引号字符串不处理转义序列\n被解析成字面字符\和n。正确做法使用双引号——until: [\n]。这是docs/footguns.md里排第一的经典陷阱。2. 开启 chat 模板后loglikelihood 分数集体变化现象同样一个任务apply_chat_templateTrue前后分数明显不同甚至任务不兼容。原因chat 模板会改写输入格式改变 loglikelihood 的上下文严格匹配类任务对格式极其敏感。正确做法MCQ/loglikelihood 任务如需 chat 格式同时设置fewshot_as_multiturnTrue并明确模板格式本身是评估的一部分——对比实验时保持一致。3. 想用samples精确定位样本结果报错现象ValueError: Either limit or samples must be None。原因两个参数互斥源码里显式校验。正确做法调试时二选一——快速试跑用limit精确复现用samples。4.batch_sizeauto直接 OOM现象显存爆炸进程被杀。原因自动探测没有上限约束。正确做法总是搭配max_batch_sizeAPI 类模型则建议固定小批大小如 1-4。5. 自定义指标注册了但结果里看不到现象跑了半天报告里没有你的指标。原因注册 ≠ 使用。register_metric只是把指标放进注册表任务实例默认仍用 YAML 里配置的指标。正确做法记得调用task.override_metric(metric_name你的指标名)且走evaluate流程传入已实例化的任务。收尾你的评估循环从此由你定义回顾这条实战主线你其实完成了一次拆解-重构从simple_evaluate的一行调用出发拆出任务、指标、模型三个扩展点再用evaluate把它们重新组装成一套为业务定制的质检评估循环——可控的输入limit/samples/num_fewshot、自定义的打分register_metric/override_metric、任意来源的模型LM 子类或 HF 适配、以及生产级的性能缓存/批处理/分布式。下一步的进阶路线建议你按这个顺序走读源码lm_eval/evaluator.py的evaluate函数是整套循环的中枢逐行读一遍胜过十篇教程lm_eval/api/metrics.py里的内置指标是你写自定义指标的最佳范本。看示例examples/transformer-lens.py演示了最完整的非标准模型接入路径直接改改就能套用。读官方文档docs/API_guide.mdTemplateAPI 模型接入、docs/footguns.md持续更新的踩坑手册、docs/task_guide.mdYAML 任务配置是三个必读入口。跑一遍git clone https://gitcode.com/GitHub_Trending/lm/lm-evaluation-harness把本文的代码片段在本地逐个跑通再动手改造自己的循环。自定义评估循环的意义不在炫技而在于让评测从给领导看的一个数变成能指导模型迭代的一台仪器。当你把评估指标改得和业务目标一致的那一刻评测才算真正开始为模型优化服务。【免费下载链接】lm-evaluation-harnessA framework for few-shot evaluation of language models.项目地址: https://gitcode.com/GitHub_Trending/lm/lm-evaluation-harness创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考