在这里插入图片描述

新手模型微调认知篇(六):模型评测原理与实践

本文整理自实际微调教学过程中的问答,适合零基础入门者阅读。


Q1:微调完模型后,怎么知道模型变好了还是变差了?

A: 这是很多初学者容易忽略的问题。最常见(但也最不靠谱)的做法是:随便问几个问题,肉眼看回答是否"感觉"变好了。

问题在于:

  • 样本太少:5 个问题 vs 训练集可能有 10 万条
  • 没有量化:只能"感觉",无法比较
  • 有随机性do_sample=True 每次生成不同结果
  • 不知道"好"在哪里:是更准确?更流畅?还是格式更好?

正确的做法是系统性评测:

┌─────────────────────────────────────────┐
│  系统性评测                               │
│  ├─ 客观题:自动打分(数学、翻译、推理)    │
│  ├─ 主观题:人工或 LLM Judge 评估         │
│  ├─ 多任务分维度统计                       │
│  └─ 输出量化对比表                         │
└─────────────────────────────────────────┘

Q2:有哪些常用的评测指标?

A: 根据任务类型不同,评测指标也不同。

1. 精确匹配(Exact Match)

适用于有唯一正确答案的任务:数学题、推理题。

# 例子:数学题
问题:计算 37 × 48
参考答案:1776
模型回答:答案是 1776

评测:提取数字 → 1776 == 1776 → ✓ 正确

优点: 客观、无歧义
缺点: 只适用于有标准答案的任务

2. BLEU(Bilingual Evaluation Understudy)

适用于翻译任务。比较模型输出和参考答案的 n-gram 重合度。

# 例子:翻译题
问题:请将"知识就是力量"翻译成英文
参考答案:Knowledge is power
模型回答:Knowledge is power.

BLEU 计算(简化版):
- 1-gram 匹配:"Knowledge", "is", "power"- 4-gram 匹配:"Knowledge is power"- 分数:接近 1.0(完全匹配)

公式(简化理解):

BLEU = (匹配的 n-gram 数 / 总 n-gram 数) × 短句惩罚

优点: 自动化、可复现
缺点: 只比较字面,不理解语义(“Knowledge is power” 和 “Power comes from knowledge” 语义相同但 BLEU 很低)

3. ROUGE-L(Recall-Oriented Understudy for Gisting Evaluation)

适用于摘要、开放问答等生成任务。基于最长公共子序列(LCS)。

# 例子:开放问答
问题:如何缓解焦虑?
参考答案:缓解焦虑可以尝试运动、冥想、倾诉等方法
模型回答:焦虑可以通过运动、冥想和与他人交流来缓解

ROUGE-L 计算:
- 最长公共子序列:"运动" + "冥想" + "缓解"(顺序一致)
- 分数:约 0.6(部分重合)

优点: 对中文友好,考虑词序
缺点: 仍然是字面匹配,不理解语义

4. Perplexity(困惑度)

适用于语言模型整体质量评估。衡量模型对文本的"惊讶程度"。

Perplexity = 2^(-平均对数似然)

困惑度越低 → 模型对文本的预测越准确 → 模型质量越好

例子:
- GPT-2: Perplexity ≈ 20-30
- GPT-3: Perplexity ≈ 15-20
- 微调后模型: Perplexity 应该比原始模型低(在目标任务上)

优点: 不需要参考答案,只看模型对文本的预测能力
缺点: 不能反映生成质量(困惑度低的模型可能只是更保守)


Q3:业界主流的评测基准有哪些?

A: 评测基准(Benchmark)是标准化的测试集,方便不同模型之间横向对比。

1. MMLU(Massive Multitask Language Understanding)

用途: 通用知识评测
覆盖: 57 个学科(数学、物理、历史、医学、法律等)
题型: 四选一多选题

Question: Which of the following is NOT a characteristic of TCP?
A. Connection-oriented
B. Reliable delivery
C. Best-effort delivery
D. Flow control
Answer: C

评测方式: 比较模型对 A/B/C/D 四个选项的概率,取最高者。

优点: 不需要文本解析,直接看 token 概率
缺点: 只能评测知识记忆,不能评测推理和生成能力

2. GSM8K(Grade School Math 8K)

用途: 数学推理评测
覆盖: 8500 道小学到初中难度的数学应用题
题型: 开放问答,有唯一答案

Question: Janet's ducks lay 16 eggs per day. She eats 3 for breakfast
every morning and bakes muffins for her friends every day with 4 eggs.
She sells the remainder at the farmers' market daily for $2 per fresh
duck egg. How many dollars does she make per day at the farmers' market?

Answer: Let's think step by step.
Janet sells 16 - 3 - 4 = 9 duck eggs a day.
She makes 9 * 2 = $18 a day.
#### 18

评测方式: 提取 #### 后的数字,和标准答案比较。

优点: 可以评测多步推理能力
缺点: 需要约束输出格式(用 #### 标记答案)

3. HumanEval

用途: 代码生成评测
覆盖: 164 道编程题(主要是 Python)
题型: 给定函数签名和 docstring,生成实现代码

def has_close_elements(numbers: List[float], threshold: float) -> bool:
    """Check if in given list of numbers, are any two numbers closer to
    each other than given threshold.
    
    >>> has_close_elements([1.0, 2.0, 3.0], 0.5)
    False
    >>> has_close_elements([1.0, 2.8, 3.0, 4.0, 5.0, 2.0], 0.3)
    True
    """
    # 模型生成代码...

评测方式: 在沙箱中执行生成的代码,跑测试用例,看是否通过。

优点: 最客观——代码能跑通就是对的
缺点: 只适用于有测试用例的代码题

4. MT-Bench

用途: 对话能力评测
覆盖: 80 道开放式对话题(写作、角色扮演、推理等)
题型: 开放问答,无标准答案

评测方式: 用 GPT-4 作为 Judge,对比两个模型的回答,打分。

Judge Prompt:
  请比较以下两个回答,从 helpfulness、harmlessness、honesty 三个维度打分。
  
  问题:{question}
  回答A:{answer_a}
  回答B:{answer_b}
  
  输出:{"winner": "A" or "B" or "tie", "reason": "..."}

优点: 能评测语义质量(不只是字面匹配)
缺点: 依赖 Judge 模型的质量,且 Judge 可能有偏见

5. OpenCompass

用途: 综合评测(国内最主流)
覆盖: 50+ 个评测集,包括 MMLU、GSM8K、HumanEval 等
特点: 支持一键评测多个模型,输出排行榜

# 使用 OpenCompass 评测
python run.py --models llama3-8b qwen-7b --datasets mmlu gsm8k humaneval

优点: 一站式评测,覆盖全面
缺点: 配置复杂,资源消耗大


Q4:如何提取模型输出中的答案?

A: 这是评测中最容易出问题的环节。

问题场景

模型输出:
  这道题需要先分析。
  首先,37 × 48 可以拆解为 (40-3) × 48 = 40×48 - 3×48
  40 × 48 = 1920
  3 × 48 = 144
  所以 1920 - 144 = 1776
  答案是 1776

如果直接取第一行 → 提取失败
如果取最后一行 → 成功提取 1776

行业做法:在 Prompt 中约束输出格式

# GSM8K 标准做法
prompt = f"""
{question}

请逐步推理,然后在最后一行用 #### 标记最终答案。
例如:#### 42
"""

# 提取答案
def extract_answer(text):
    match = re.search(r"####\s*(\d+)", text)
    if match:
        return int(match.group(1))
    # 兜底:取最后一个数字
    numbers = re.findall(r"\d+", text)
    return int(numbers[-1]) if numbers else None

多种兜底策略

def robust_extract(text, task_type):
    """多策略提取答案"""
    
    # 策略1:优先匹配显式标记
    if "####" in text:
        match = re.search(r"####\s*([^\n]+)", text)
        if match:
            return match.group(1).strip()
    
    if "The answer is" in text:
        match = re.search(r"The answer is\s*([^\n.]+)", text)
        if match:
            return match.group(1).strip()
    
    # 策略2:取最后一行的数字
    lines = text.strip().split("\n")
    for line in reversed(lines):
        numbers = re.findall(r"-?\d+\.?\d*", line)
        if numbers:
            return float(numbers[-1])
    
    # 策略3:全文最后一个数字
    numbers = re.findall(r"-?\d+\.?\d*", text)
    return float(numbers[-1]) if numbers else None

Q5:评测时有哪些常见坑?

A: 评测看似简单,但细节很多。

1. 必须关闭采样(do_sample=False

# ✗ 错误:每次生成不同结果
outputs = model.generate(..., do_sample=True, temperature=0.7)

# ✓ 正确:贪婪解码,结果可复现
outputs = model.generate(..., do_sample=False)

原因: do_sample=True 每次生成不同文本,评测分数会波动,无法对比。

2. 测试集不能出现在训练集中

# ✗ 错误:训练集和测试集有重复
train_data = load_data("all.json")
test_data = load_data("all.json")  # 测试集和训练集相同!

# ✓ 正确:严格划分
train_data = load_data("train.json")  # 98%
val_data = load_data("val.json")      # 1%
test_data = load_data("test.json")    # 1%

检查方法: 对比训练集和测试集的哈希值,确保无重复。

3. 要同时评估"退化"情况

微调可能让模型在目标任务上变好,但在其他能力上变差(灾难性遗忘)。

评测设计:
  目标任务(数学、翻译等):应该提升
  通用能力(常识、推理等):不应该严重退化

方法: 加入一组"通用能力"测试题(模型没训练过的领域),确认没有严重退化。

4. 不要只看平均分

平均分可能掩盖问题:

模型A:数学 80%,翻译 60%,推理 70% → 平均 70%
模型B:数学 95%,翻译 20%,推理 95% → 平均 70%

两个模型平均分相同,但能力分布完全不同。

方法: 分任务类型统计,看能力分布。

5. 注意评测数据的分布偏差

如果测试集 80% 是加法题,20% 是乘法题:
- 模型只学会了加法 → 准确率 80%
- 但乘法题准确率可能只有 0%

平均分看起来不错,但实际能力有严重偏差。

方法: 确保测试集覆盖所有题型,且分布均匀。


Q6:如何提升评测的可靠性?

A: 从低成本到高成本,有几种方法。

Level 1:约束输出格式(零成本)

# 在 Prompt 中明确要求输出格式
prompt = f"""
{instruction}

请逐步推理,然后在最后一行用「答案:X」的格式给出最终答案。
"""

# 提取时直接匹配
match = re.search(r"答案:\s*(.+)", text)
answer = match.group(1).strip() if match else None

优点: 零成本,提取准确率高
缺点: 需要修改 Prompt,可能影响模型表现

Level 2:多种兜底策略(低成本)

def extract_with_fallback(text):
    # 策略1:显式标记
    match = re.search(r"答案:\s*(.+)", text)
    if match:
        return match.group(1).strip()
    
    # 策略2:最后一行数字
    last_line = text.strip().split("\n")[-1]
    numbers = re.findall(r"-?\d+\.?\d*", last_line)
    if numbers:
        return float(numbers[-1])
    
    # 策略3:全文最后一个数字
    all_numbers = re.findall(r"-?\d+\.?\d*", text)
    return float(all_numbers[-1]) if all_numbers else None

优点: 鲁棒性高
缺点: 兜底策略可能不准确

Level 3:LLM Judge(中等成本)

def eval_with_judge(predicted, reference, instruction):
    """用强模型评判弱模型的回答"""
    judge_prompt = f"""
    请判断以下回答的质量,评分 1-5 分。
    
    问题:{instruction}
    参考答案:{reference}
    模型回答:{predicted}
    
    评分标准:
    5分:答案正确且完整
    4分:答案基本正确,有小瑕疵
    3分:部分正确
    2分:大部分错误
    1分:完全错误
    
    输出 JSON:{{"score": N, "reason": "..."}}
    """
    
    # 调用 GPT-4 / Claude API
    response = call_llm_api(judge_prompt)
    return json.loads(response)

优点: 能评测语义质量,不只是字面匹配
缺点: 需要调用 API,有成本;Judge 模型可能有偏见

Level 4:人工评估(高成本)

招募标注员,按维度打分:
- 准确性:回答是否正确
- 完整性:是否涵盖关键信息
- 流畅性:表达是否自然
- 相关性:是否回答了用户的问题

优点:最可靠
缺点:成本高、速度慢

适用场景: 最终版本上线前的质量把关


Q7:评测脚本应该输出什么样的报告?

A: 一份好的评测报告应该包含:

1. 分任务类型的对比表

═══════════════════════════════════════════════════════
  微调模型评估报告(Llama-3.2-1B + LoRA)
═══════════════════════════════════════════════════════
  任务类型    │  原始模型  │  微调模型  │  提升幅度
─────────────┼───────────┼───────────┼──────────
  数学准确率   │  45.2%    │  82.3%    │  +37.1%  ✓
  翻译 BLEU   │  0.31     │  0.58     │  +0.27   ✓
  代码可执行率  │  62.0%    │  78.5%    │  +16.5%  ✓
  推理准确率   │  38.7%    │  71.2%    │  +32.5%  ✓
  知识 QA (LLM)│  3.2/5    │  4.1/5    │  +0.9    ✓
  创意写作(LLM)│  3.5/5    │  3.8/5    │  +0.3    ~
  生活建议(LLM)│  3.8/5    │  4.0/5    │  +0.2    ~
─────────────┴───────────┴───────────┴──────────
  加权总分     │  52.1     │  78.4     │  +26.3
═══════════════════════════════════════════════════════

2. 每个维度的详细统计

数学题详细统计:
  总题数:120
  原始模型正确:54 (45.2%)
  微调模型正确:99 (82.3%)
  平均耗时:原始 1.2s,微调 1.3s
  
  错误类型分析:
    - 计算错误:原始 35%,微调 8%
    - 理解错误:原始 20%,微调 10%

3. 退化检测

通用能力评测(模型未训练过的领域):
  常识推理:原始 72%,微调 68% → 退化 4%(可接受)
  历史知识:原始 65%,微调 63% → 退化 2%(可接受)
  
结论:微调在目标任务上显著提升,通用能力轻微退化但在可接受范围内。

Q8:有没有现成的评测框架可以直接用?

A: 有,但需要权衡。

1. lm-evaluation-harness(EleutherAI)

# 安装
pip install lm-eval

# 评测 Llama-3 在 MMLU 和 GSM8K 上的表现
lm_eval --model hf \
    --model_args pretrained=meta-llama/Llama-3-8B \
    --tasks mmlu,gsm8k \
    --device cuda:0 \
    --batch_size 8

优点: 覆盖 600+ 个 benchmark,社区活跃
缺点: 配置复杂,需要 GPU 资源

2. OpenCompass(上海 AI Lab)

# 安装
git clone https://github.com/open-compass/opencompass
cd opencompass
pip install -e .

# 评测
python run.py --models llama3-8b --datasets mmlu gsm8k

优点: 国内最主流,中文 benchmark 丰富
缺点: 配置复杂,文档不够友好

3. DeepEval(Confident AI)

# 安装
pip install deepeval

# 类似 pytest 的评测体验
from deepeval import assert_test
from deepeval.test_case import LLMTestCase
from deepeval.metrics import AnswerRelevancyMetric

def test_model_answer():
    test_case = LLMTestCase(
        input="什么是机器学习?",
        actual_output=model.generate("什么是机器学习?"),
        expected_output="机器学习是人工智能的一个分支..."
    )
    metric = AnswerRelevancyMetric(threshold=0.7)
    assert_test(test_case, [metric])

优点: 类似 pytest,易上手
缺点: 商业产品,部分功能收费

4. 自己写(推荐初学者)

# 优点:
# - 完全控制评测逻辑
# - 可以针对自己的数据定制
# - 学习评测原理

# 缺点:
# - 需要自己实现指标
# - 容易踩坑(如答案提取、采样设置)

建议: 先自己写一个简单的评测脚本(如本文档配套的 04_eval.py),理解原理后再用成熟框架。


Q9:评测结果应该怎么看?

A: 评测结果需要结合多个维度解读。

1. 看相对提升,不要只看绝对值

场景1:
  原始模型:数学 45%,翻译 30%
  微调模型:数学 82%,翻译 58%
  → 数学提升 37%,翻译提升 28% → 微调效果显著 ✓

场景2:
  原始模型:数学 85%,翻译 80%
  微调模型:数学 88%,翻译 82%
  → 提升很小 → 可能微调数据不够好,或者原始模型已经很强

2. 看能力分布,不要只看平均分

模型A:数学 95%,翻译 20%,推理 95% → 平均 70%
模型B:数学 70%,翻译 70%,推理 70% → 平均 70%

两个模型平均分相同,但:
- 模型A 是"偏科生",数学和推理强,翻译弱
- 模型B 是"均衡生",各维度差不多

选择哪个模型取决于你的应用场景。

3. 看退化情况

如果微调后:
  目标任务:+30% → 很好
  通用能力:-20% → 灾难性遗忘严重

需要权衡:
- 如果只用于目标任务 → 可以接受退化
- 如果需要通用能力 → 需要调整微调策略(如混合通用数据)

4. 看统计显著性

如果只评测了 10 道题:
  原始模型:7/10 (70%)
  微调模型:8/10 (80%)
  → 提升 10%,但样本太少,可能只是随机波动

建议至少评测 100+ 道题,且做多次实验取平均。

Q10:评测的下一步是什么?

A: 评测不是终点,而是迭代的起点。

1. 分析错误案例

# 找出模型答错的题目
wrong_cases = []
for item in test_data:
    predicted = model.generate(item["instruction"])
    if not is_correct(predicted, item["output"]):
        wrong_cases.append({
            "instruction": item["instruction"],
            "expected": item["output"],
            "actual": predicted
        })

# 分析错误模式
for case in wrong_cases:
    print(case)
    # 可能的发现:
    # - 模型在多位数乘法上总是错
    # - 模型对"如果...那么..."句型理解不好
    # - 模型对中文成语翻译不准确

2. 针对性改进

发现:模型在多位数乘法上准确率低
改进:
  - 在训练数据中增加多位数乘法的样本
  - 或者在 Prompt 中加入 CoT(Chain-of-Thought)引导

发现:模型对开放题回答过于简短
改进:
  - 在训练数据中增加更详细的回答
  - 或者在 Prompt 中要求"请详细回答"

3. 持续评测

每次微调后都跑一遍评测,记录历史:

版本    │ 数学  │ 翻译  │ 推理  │ 通用
────────┼───────┼───────┼───────┼──────
v1.0    │ 45%   │ 30%   │ 38%   │ 70%
v1.1    │ 82%   │ 58%   │ 71%   │ 68%  ← 第一次微调
v1.2    │ 85%   │ 62%   │ 73%   │ 65%  ← 增加数据后
v1.3    │ 88%   │ 65%   │ 75%   │ 60%  ← 继续微调(通用能力退化)

结论: v1.3 在目标任务上最好,但通用能力退化严重,可能 v1.2 是更好的平衡点。


总结

模型评测是微调流程中不可或缺的一环:

  1. 为什么评测: 量化微调效果,避免"感觉"判断
  2. 用什么指标: 精确匹配(数学)、BLEU(翻译)、ROUGE(摘要)、Perplexity(整体)
  3. 主流基准: MMLU(知识)、GSM8K(数学)、HumanEval(代码)、MT-Bench(对话)
  4. 常见坑: 忘记关闭采样、测试集泄漏、只看平均分
  5. 提升可靠性: 约束输出格式、多种兜底策略、LLM Judge
  6. 怎么看结果: 看相对提升、能力分布、退化情况、统计显著性

评测不是终点,而是迭代的起点——分析错误、针对性改进、持续评测,才能让模型越来越好。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐