lm-evaluation-harness 自定义评估循环实战从跑通到深度定制的完整指南【免费下载链接】lm-evaluation-harnessA framework for few-shot evaluation of language models.项目地址: https://gitcode.com/GitHub_Trending/lm/lm-evaluation-harness当默认评测流程满足不了你的需求我曾在一次模型交付前遇到一个尴尬场景团队微调了一个面向内部客服的 7B 模型想用 lm-evaluation-harness 评测它在指令遵循 多轮对话上的表现。可跑出来的结果只有标准的acc、exact_match这类数值完全看不出模型在真实对话场景中的问题更麻烦的是评测数据集的 prompt 模板是固定的我们想换成自己业务里的对话格式却找不到改动的入口。如果你也遇到过类似的困惑——框架能跑通但评测结果跟实际业务对不上、自定义指标无从下手、想接入自家模型却不知道从哪写起——那么这篇 lm-evaluation-harness 自定义评估循环实战文章就是为你准备的。本文不打算泛泛介绍框架而是聚焦评估循环这一条主线它内部到底是怎么转的、哪些环节可以替换、如何用十几行代码把标准流程改造成自己的评测管线。先拆开评估循环看清三个可替换的接口lm-evaluation-harness 的评估核心藏在 lm_eval/evaluator.py 里两个函数撑起全局simple_evaluate负责一键评测加载模型、加载任务、跑完出报告evaluate是它的底层实现负责真正的请求调度。你可以把评估循环想象成一条餐饮流水线备料任务把数据集文档加工成模型输入doc_to_text、doc_to_target同时决定每道题要模型怎么回答loglikelihood打分还是generate_until生成。下锅把一批批请求喂给模型得到原始输出——这一步由模型类LM接口实现。上菜对输出做后处理Filter、算指标metric_list里的 metric aggregation、聚合成报告。这套设计里最值钱的一点是三个环节都留了官方后门。任务定义是 YAML 配置改模板不改代码指标通过 lm_eval/api/registry.py 的装饰器注册新增一个指标就像往菜单里加一道菜模型通过实现LM抽象类接入框架不关心你的模型是 HuggingFace 还是自研的。下面我们一条条走通。三步跑通自定义评估循环从 CLI 到 Python APICLI 是日常评测最快的方式但一旦要定制就得切换到你自己的 Python 脚本里调用simple_evaluate。最小可行路径只需要三步。第一步确认框架可用并准备一个小模型。用仓库根目录的README.md安装依赖后先用一个轻量模型验证环境# 用 14M 参数的 Pythia 在 10% 数据上冒烟测试 lm_eval --model hf \ --model_args pretrainedEleutherAI/pythia-14m \ --tasks arc_easy --limit 0.1看到命令行输出一张结果表说明链路通了。此时 CLI 能满足跑标准评测但接下来的定制需求它做不了我们进入第二步。第二步用 Python API 替换 CLI。新建my_eval.py用simple_evaluate复刻上面的评测。它的签名可以在 lm_eval/evaluator.py 里查到这里只看几个关键参数参数作用默认值model/model_args模型名或模型参数串必填tasks任务名列表[arc_easy, hellaswag]必填num_fewshot少样本示例数量Nonelimit每个任务取多少样本1表示百分比Nonebatch_size批大小支持auto自动探测Noneuse_cacheSQLite 缓存路径缓存模型推理结果Nonecache_requests缓存备料阶段构建的请求Falseapply_chat_template是否套用模型的 chat 模板Falsepredict_only只出模型输出、不算指标Falsefrom lm_eval import evaluator results evaluator.simple_evaluate( modelhf, model_argspretrainedEleutherAI/pythia-14m, tasks[arc_easy], limit0.1, # 先小规模验证 batch_sizeauto, # 自动找最大不爆显存的批大小 ) print(results[results][arc_easy])第三步验证可复现。打印出的results[results][arc_easy]里除了指标值还有stderrbootstrap 标准差。框架默认用固定随机种子random_seed、numpy_random_seed、torch_random_seed见simple_evaluate签名同一份代码跑两遍结果应当一致——这点对后面做实验对比至关重要。走到这里你的自定义评估循环已经跑通了——虽然内容还和 CLI 一样但控制权已经回到你手里接下来所有定制都发生在这一层。深入实战一给评测装上业务指标和自定义任务默认指标不够用是评测和业务脱节的根源。框架在 lm_eval/api/metrics.py 里用两个装饰器管理指标生态register_metric注册逐样本指标比如accregister_aggregation注册聚合函数比如mean。一次评测 对每个样本算 metric再把所有样本的结果用 aggregation 汇总。假设我们的客服模型需要回答必须包含工单号才算对可以在脚本里注册一个新指标from lm_eval.api.registry import register_metric, register_aggregation def contains_ticket(predictions, references): 逐样本判断预测里是否出现标准答案中的工单号 hits [1.0 if ref in pred else 0.0 for pred, ref in zip(predictions, references)] return hits # 返回每个样本的得分 register_aggregation(mean) def agg_mean(items): return sum(items) / len(items) register_metric( metriccontains_ticket, # 指标名 aggregatemean, # 配套聚合 higher_is_betterTrue, # 数值越大越好 )注册之后怎么让它生效两条路一是改任务的 YAML推荐可复用在metric_list里加一行二是运行时用Task.override_metric()动态替换lm_eval/api/task.py 第 535 行适合临时对比。以arc_easy的配置文件 lm_eval/tasks/arc/arc_easy.yaml 为参照它的metric_list长这样metric_list: - metric: acc aggregation: mean higher_is_better: true - metric: acc_norm aggregation: mean higher_is_better: true想改 prompt 模板或数据字段同样在 YAML 里动doc_to_text定义问题格式支持 Jinja2 模板doc_to_choice定义候选答案doc_to_target定义标准答案字段。大多数定制需求根本不需要写 Python。上面这张图直观展示了 few-shot 模板的构造逻辑——任务描述 若干示例 待回答问题num_fewshot控制示例数量fewshot_as_multiturn决定示例是拼进单轮还是组织成多轮对话深入实战二把自研模型塞进评估循环如果模型不是 HuggingFace 格式就不能直接用--model hf。框架为此定义了LM抽象类lm_eval/api/model.py核心就两个方法_loglikelihood_tokens给一组 token 打分用于选择题/困惑度和_generate_until生成文本直到停用词用于生成式任务。仓库里有个现成范例 examples/transformer-lens.py它把 TransformerLens 的HookedTransformer包了一层HFLikeModelAdapter再交给HFLM使用。这个思路值得借鉴——与其从零实现LM不如把自家模型包装成长得像 HuggingFace的样子直接复用HFLM里已写好的批处理、缓存、设备管理逻辑。包装要点就三个class MyAdapter(nn.Module): def __init__(self, model, tokenizer): super().__init__() self.model model self.tokenizer tokenizer # 需要暴露 .config至少包含 max_length 等 def forward(self, input_idsNone, attention_maskNone, **kwargs): output self.model(input_idsinput_ids, attention_maskattention_mask) if not hasattr(output, logits): output.logits output # 保证返回对象带 .logits return output def to(self, *args, **kwargs): # 设备迁移委托给真实模型 return self.model.to(*args, **kwargs)包装完成后用HFLM(pretrainedadapter, tokenizertokenizer)喂给simple_evaluate即可。如果你的模型完全没法走这条捷径再考虑直接继承LM实现那两个核心方法——但先别急多数情况包装法都能解决。性能调优缓存用对评测省一半时间评测最耗时的是重复构建请求和大规模推理。框架提供两层缓存use_cache把模型推理结果存进 SQLite同一批请求第二次直接读库cache_requestsTrue缓存备料阶段构建的请求。调试 prompt 模板时建议只开cache_requests不开use_cache否则改了模板但请求 hash 相同你会拿到旧结果还以为是新数据。改完模板想强制刷新用rewrite_requests_cacheTrue。results evaluator.simple_evaluate( modelhf, model_argspretrainedEleutherAI/pythia-14m, tasks[arc_easy], cache_requestsTrue, use_cacheresults_cache.db, # 模型推理缓存 )避坑复盘我在自定义评估循环里踩过的四个坑坑一YAML 里的\n被吃掉了。在generation_kwargs里写until: [\n]时YAML 单引号不会解析转义符导致模型一直生成到超长截断。排查方法用--write_out把实际喂给模型的 prompt 写出来看。正确写法是双引号until: [\n]或者用 YAML 的|块字符串。这个坑在 docs/footguns.md 有完整记录。坑二开了apply_chat_template后分数莫名暴跌。chat 模板会在输入前后加特殊 token直接改变 loglikelihood 计算的对齐方式。如果你给的是 base 模型别开这个参数给对话模型评测且开了它记得同步设fewshot_as_multiturnTrue否则少样本示例的拼接方式跟训练分布不一致结果不可信。坑三limit和samples同时用。samples参数允许你精确指定测第几条样本如{mmlu_astronomy: [0, 3, 6]}但它与limit互斥同时传会报错。小规模回归测试建议用samples固定样本集保证每次改代码后对比的是同一批题。坑四只想要模型输出、不要指标时忘了predict_only。跑推理脚本比如对接下游分析时如果不设predict_onlyTrue框架会白跑一遍指标计算并可能因缺少标注字段直接崩。设成True后results[samples]里就是每个样本的resps原始输出。下一步行动从跑通到跑出自己的评测体系到这一步你已经有能力把 lm-evaluation-harness 从一个评测工具改造成贴合业务的评测管线用 YAML 定制任务模板、用装饰器注册业务指标、用包装类接入自研模型、用缓存把迭代成本压下来。建议的下一步按这个顺序推进用lm_eval --tasks list看看框架内置了哪些任务先挑一个结构最接近业务场景的 YAML复制改造成自己的任务用--write_out检查实际 prompt跑通第一个自定义任务把注册的指标和任务固化成一个my_tasks/目录通过task_manager挂载让团队所有人都能lm_eval --tasks my_custom_task复用。想深入研究官方文档在仓库的 docs/ 目录下任务配置看 docs/task_guide.md 和 docs/config_files.md模型接入看 docs/model_guide.mdAPI 速查看 docs/API_guide.md。而 lm_eval/evaluator.py 本身是最好的教材——把simple_evaluate从头到尾读一遍你对整个评估循环的理解会超过市面上绝大多数教程。【免费下载链接】lm-evaluation-harnessA framework for few-shot evaluation of language models.项目地址: https://gitcode.com/GitHub_Trending/lm/lm-evaluation-harness创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考