深色模式
微调后评估与回归
摘要:微调优化的只是单一分布,最大风险是「任务变好、其他崩坏」。本文给出三层评测(通用基准 MMLU/HellaSwag 防遗忘、MT-Bench 防指令漂移、自建 eval 验任务)、把评测接进 CI 的 eval gate、私有 golden set 防污染,以及回归判定与回滚。工具:
lm-evaluation-harness(EleutherAI)、FastChat/MT-Bench、lighteval。版本:([版本相关])。
适用版本与前提
- 工具:
lm-eval(EleutherAI lm-evaluation-harness)、lm-eval0.4.x、fschat(MT-Bench judge)、lighteval - 模型:任意 HuggingFace 格式 checkpoint,含 LoRA 适配器(可直接评,无需合并)
- 环境:Python 3.12 + CUDA 12 示例([版本相关],以官方安装说明为准)
核心概念:为什么必须评测
「训出来了」≠「变好了」
训练 loss 下降、RL reward 上升都可能伴随真实质量下降(奖励黑客、熵崩塌)。只有 held-out 评测能区分「真改进」与「过拟合/退化」。微调典型失败模式:MMLU 相对基座掉 > 3 分 = 知识被压缩;MT-Bench 降但训练 loss 更低 = 指令遵循漂移;自建 eval 过但用户投诉 = 分布不匹配。
三层评测覆盖不同失败模式:
| 层 | 工具/集 | 检测什么 |
|---|---|---|
| 通用基准 | MMLU / HellaSwag / TruthfulQA | 灾难性遗忘(知识/常识回归) |
| 指令遵循 | MT-Bench(GPT-4 判分) | 多轮指令遵循漂移 |
| 任务评测 | 自建 golden set | 你的真实任务是否真变好 |
架构与原理:eval gate
可复现是底线
分数只有在「同一 harness 版本、同一 prompt、同一 few-shot、同一 metric、同一 dtype」下才可比。每个评测集需 pin 版本与 task revision;自建集用私有 golden set(不会被训练数据污染)。对一个未去污染的基准,分数视为「未知」而非「高」。
生产实践
评测门禁(eval gate)
- 通用基准:MMLU 5-shot(对齐原论文)、HellaSwag/ARC/TruthfulQA 监控。MMLU 相对基座跌幅 > 3 分需警惕,> 5 分基本判定遗忘严重。
- MT-Bench:80 道多轮题由强模型判分,检测指令遵循;需 API key(judge 一般调用 GPT-4 级模型)。
- 自建 eval:用你的真实输入/期望输出构造,定义成功率/忠实度阈值(社区实践常设 faithfulness ≥ 0.90、answer relevance ≥ 0.85 之类门禁,[阈值依业务定])。
- CI 化:把评测写成 pytest 风格(DeepEval/Ragas/promptfoo 等),回归即阻断发布。监控生产侧质量与反馈,对 faithfulness 下降而非 GPU 利用率报警。
操作步骤:跑 MMLU 对比
bash
# 安装(版本以官方为准,[版本相关])
pip install lm-eval
lm_eval --version # 期望 0.4.x;[未实测具体输出]
# 微调后 checkpoint
lm_eval --model hf \
--model_args pretrained=/path/to/checkpoint,dtype=bfloat16 \
--tasks mmlu --num_fewshot 5 \
--batch_size auto --output_path ./results/ft.json
# 基座(算 delta)
lm_eval --model hf \
--model_args pretrained=meta-llama/Llama-3.1-8B,dtype=bfloat16 \
--tasks mmlu --num_fewshot 5 \
--batch_size auto --output_path ./results/base.json1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
python
# 逐项对比 MMLU 跌幅,标注 >3 分的学科
import json
ft = json.load(open("./results/ft.json"))
base = json.load(open("./results/base.json"))
for task, r in ft["results"].items():
b = base["results"].get(task, {}).get("acc,none", 0)
f = r.get("acc,none", 0)
if abs(f - b) > 0.03:
print(f"{task}: {f-b:+.3f} (base={b:.3f}, ft={f:.3f})") # [示例逻辑,实际输出依运行]1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
直接评 LoRA 适配器
lm-eval 支持直接评 PEFT 适配器,无需合并:
bash
lm_eval --model hf \
--model_args pretrained=meta-llama/Llama-3.1-8B-Instruct,peft=/path/to/lora-adapter \
--tasks mmlu1
2
3
2
3
用 vLLM 后端可大幅加速生成类任务:--model vllm --model_args pretrained=./merged,tensor_parallel_size=4。
验证
bash
# 1) 先 --limit 10 验证抽取与判分逻辑正确,再跑全量
# 2) --check_integrity 校验基准数据未被破坏
# 3) 对比 base vs ft 的 MMLU/HellaSwag;领域基准应升、通用基准跌幅受控
# 4) 自建 golden set 跑通过率,与历史基线比对1
2
3
4
2
3
4
回滚与清理
评测不通过的处理
- 评测门禁未通过 = 不发布;回退到上一通过版本或回到训练/改数据阶段。
- 评测产物(分数 JSON、样本)需与 checkpoint 版本一同留存,便于审计与复现。
- 删除实验 checkpoint 前确认其未通过门禁且无人引用;大目录删除注意 IO。
故障排查
- 分数不可比:确认 harness 版本、few-shot、dtype、batch 一致;vLLM 与 HF 后端偶有差异,用
model_comparator.py校验。 - MMLU 虚高:疑似训练数据污染评测集,用私有 golden set 复核,并对语料重新去污染。
- MT-Bench 判分不稳:judge 模型版本固定;对边界样本人工抽检。
- 自建 eval 全过但线上差:评测集与线上分布不匹配,扩充边界/失败样本。
安全与合规
评测中的安全与数据边界
- 评测数据污染:公开基准可能已泄入训练数据导致虚高;必须用私有 golden set 兜底,并去污染。
- 评测集 PII:自建 golden set 若含真实用户数据,需脱敏与访问控制,等同训练数据要求。
- judge 调用合规:MT-Bench 调外部 judge API 会把题目发出去,确认不含机密;或改用本地 judge 模型。
- 越权:评测平台若多租户,checkpoint 与评测结果需按租户隔离,避免跨团队可读。
成本与性能(估算,[未实测])
| 评测项 | 规模 | 成本 |
|---|---|---|
| MMLU(全 57 科)单卡跑 | 7B,bf16 | 约数十分钟~1h GPU |
| MT-Bench | 80 题 + judge API | API 调用费为主,几美元内 |
| 自建 golden set | 依规模 | 计算可忽略 |
成本备注
三层评测合计通常 < 30 分钟 GPU + 少量 judge API 费,相比一次微调(数小时~数天)成本极低,却能在上线前拦住绝大多数回归。评测应作为 CI 门禁常态化,而非上线后补救。GPU 利用率与 judge 延迟是主要耗时点。[时长为估算,非实测]