LLaMA-Factory 自定义评估指标:一份能直接落地的实践笔记
【免费下载链接】LlamaFactoryUnified Efficient Fine-Tuning of 100+ LLMs & VLMs (ACL 2024)项目地址: https://gitcode.com/GitHub_Trending/ll/LlamaFactory
你刚用 LLaMA-Factory 微调完模型,想验证它在自己的任务上表现如何,可现成的评测基准只给你一个叫"准确率"的数——任务不是选择题的时候,就卡住了。这篇聊聊怎么基于 LLaMA-Factory 的评估框架扩展自定义评估指标,并直接上手。
为什么值得折腾
自己评估模型很痛。直接跑公开基准,拿到的是一个和"业务场景好不好"无关的准确率;自己写打分脚本,又要重新实现一遍仓库里已有的东西:few-shot 样本格式化、chat template 编码、batch padding、再算分,全是重复劳动。
LLaMA-Factory 的评估框架把这条链路封装好了:EvalTemplate负责 prompt 格式化(含 few-shot 示例拼接),Evaluator负责推理和算分,参数由EvaluationArguments统一管。关键是这条链路是可插拔的——改模板注册函数、或者子类化评估器,就能长出你自己的评估流程。
还有个前提要知道:这套框架的"打分方式"是直接取模型在 A/B/C/D 答案 token 上的概率,不做文本生成。记住这一点,后面自定义的流程就好理解了。
核心机制,一张图看懂
整个评估模块就三样东西,各在一个文件里:
EvaluationArguments→src/llamafactory/hparams/evaluation_args.py:一次评估的参数容器。定义task(基准名,格式为task_split)、n_shot(few-shot 示例数,默认 5)、lang(模板语言)、save_dir(结果保存路径)等字段。EvalTemplate→src/llamafactory/eval/template.py:评估 prompt 的模板。system、choice、answer三个字段分别控制系统提示、选项行格式和答案引导语。Evaluator→src/llamafactory/eval/evaluator.py:执行器。依次加载数据集、格式化、批量推理,最后按学科类别算准确率,写入results.json和results.log。
最值得盯着看的是Evaluator里的"打分"部分,只有几行,却是整个指标体系的锚点:
# src/llamafactory/eval/evaluator.py,简化 logits = self.model(**batch_input).logits word_probs = logits[range(batch), lengths - 1] # 每个样本最后一个有效 token choice_probs = softmax(word_probs[:, choice_inputs]) # A/B/C/D 的概率 preds = argmax(choice_probs) # 概率最大的字母即预测答案说白了,它不是让模型"写出"答案,而是问"模型接下来最想吐 A、B、C、D 哪个",取概率最高者。所以指标就是outputs == labels的字符比较,快且稳定——但也意味着:想换指标,就得改这里,或者在它后面加。
走一遍:从配置到出结果
用"注册新评估模板 + 加自定义指标打分"走一遍全流程。数据侧遵循框架约定:每条样本是带question和 A/B/C/D/answer字段的 dict,任务目录里还要有mapping.json声明学科名和类别,格式与 CEval、MMLU 一致,具体以仓库官方数据为准。
先注册模板。在src/llamafactory/eval/template.py末尾调一次注册函数即可:
_register_eval_template( name="mytask", # 对应 lang 参数取值 system="You are answering questions about {subject}.\n\n", # {subject} 是学科占位符 choice="\n{choice}. {content}", answer="\nAnswer:", )这段只是告诉框架"当 lang=mytask 时,prompt 该怎么拼"。{choice}和{content}分别填充选项字母和选项内容,现成的en、zh两个模板就是这么注册的。
然后跑一次默认评估,确认链路通了。Evaluator的构造函数接收一个参数 dict,字段对应hparams里的各 dataclass,具体以最新文档为准:
from llamafactory.eval.evaluator import Evaluator Evaluator({ "model_name_or_path": "your-finetuned-model", # 待评估模型 "task": "my_benchmark_val", # 任务名,必须是 task_split 形式 "task_dir": "evaluation", # 数据集位置 "n_shot": 5, # few-shot 示例数 "lang": "mytask", # 刚注册的模板 "save_dir": "./eval_out/mytask", }).eval()这段跑完就是完整流程:加载模型、用mytask模板格式化、批量推理,把results.log(分类别准确率)和results.json(每个样本的预测字母)写进save_dir。看到这两个文件,说明链路通了,剩下的只是换"怎么算分"。
加自定义指标,推荐走子类化。_save_results是评估流程末尾的回调,拿到的两个参数——分类别对错数组和逐样本预测——就是天然的扩展点:
class MyEvaluator(Evaluator): def _save_results(self, category_corrects, results): super()._save_results(category_corrects, results) # 先照旧保存准确率 # 你的自定义指标:用 category_corrects(分类别对错数组) # 和 results(逐样本预测)计算,比如分类别加权分、严格匹配率 # 算完按需追加写入 save_dir 下的结果文件改完后实例化MyEvaluator跑同一组参数,结果目录里就同时有原准确率和你的自定义指标。
这几个坑,大概率会踩
llamafactory-cli eval直接抛NotImplementedError: Evaluation will be deprecated in the future.→ CLI 入口已标记弃用,但 eval 模块本身还在维护 → 改在 Python 里直接调Evaluator(args).eval()。- 启动时报
ValueError: 'save_dir' already exists→EvaluationArguments的__post_init__明确禁止覆盖已存在目录 → 换个新目录,或先清掉旧结果再跑。 - 指标为 0 或低得离谱 → 准确率是
outputs == labels的单字符比较,answer 字段写成"正确答案是B"或带空格就永远不等 → 确认 answer 字段是 A-D 单个字母。 IndexError: list index out of range→ 任务名按_拆成 task 和 split(如mmlu_val),只给名字没有第二段 → 永远用task_split形式写task。
收个尾
把自定义评估指标的扩展点收拢一下:
- 模板扩展点在
src/llamafactory/eval/template.py末尾的_register_eval_template,四个参数写完即注册。 - 指标扩展点在
Evaluator的算分环节之后,最实用的钩子是_save_results。 - 参数集中在
EvaluationArguments:task必须task_split形式,save_dir不能已存在。
评估机制和参数的最新状态以项目官方文档为准,具体以最新仓库代码为准。有指标扩展的想法,欢迎到项目仓库提 issue 或 PR。下一篇可以接着聊 few-shot 示例的选择与评测集构造。
【免费下载链接】LlamaFactoryUnified Efficient Fine-Tuning of 100+ LLMs & VLMs (ACL 2024)项目地址: https://gitcode.com/GitHub_Trending/ll/LlamaFactory
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考