AI 基础 | | 约 24 分钟 | 9,512 字

AI 模型评估指标:如何衡量模型好不好

理解 BLEU、ROUGE、Perplexity 等指标,以及 LLM 评估的特殊挑战

为什么评估 AI 模型这么难

评估传统软件很简单:输入 A,期望输出 B,实际输出 B,测试通过。但评估 AI 模型完全不同——同一个问题可以有无数种”正确”的回答。

问题: "用 Python 写一个排序函数"

回答 A: 用冒泡排序实现 → 正确但低效
回答 B: 用快速排序实现 → 正确且高效
回答 C: 直接调用 sorted() → 正确且实用
回答 D: 用归并排序实现 → 正确且稳定

哪个"更好"?取决于上下文。

我们需要一套系统化的评估方法,而不是凭感觉判断。


传统 NLP 评估指标

BLEU(Bilingual Evaluation Understudy)

BLEU 最初用于机器翻译评估,衡量生成文本与参考文本的 n-gram 重叠程度。

# BLEU 的核心思想
reference = "the cat sat on the mat"
candidate = "the cat is on the mat"

# 1-gram 匹配: the(2), cat(1), on(1), the(1), mat(1) = 5/6
# 2-gram 匹配: "the cat"(1), "on the"(1), "the mat"(1) = 3/5
# 最终 BLEU 是各 n-gram 精确率的几何平均
from collections import Counter

def simple_bleu(reference: str, candidate: str, n: int = 4) -> float:
    """简化版 BLEU 计算"""
    ref_tokens = reference.lower().split()
    cand_tokens = candidate.lower().split()

    scores = []
    for i in range(1, n + 1):
        # 提取 n-gram
        ref_ngrams = Counter(
            tuple(ref_tokens[j:j+i]) for j in range(len(ref_tokens) - i + 1)
        )
        cand_ngrams = Counter(
            tuple(cand_tokens[j:j+i]) for j in range(len(cand_tokens) - i + 1)
        )

        # 计算裁剪后的匹配数
        matches = sum(
            min(count, ref_ngrams[ngram])
            for ngram, count in cand_ngrams.items()
        )
        total = sum(cand_ngrams.values())

        if total == 0:
            scores.append(0)
        else:
            scores.append(matches / total)

    # 几何平均
    if 0 in scores:
        return 0.0
    import math
    return math.exp(sum(math.log(s) for s in scores) / len(scores))

BLEU 的局限性:

问题说明
不考虑语义”我喜欢猫” 和 “我爱猫” BLEU 分数低
不考虑流畅性词序打乱后 n-gram 可能仍然匹配
需要参考答案开放式生成没有标准答案
对长度敏感短文本容易获得高分

ROUGE(Recall-Oriented Understudy for Gisting Evaluation)

ROUGE 主要用于文本摘要评估,关注召回率(参考文本中有多少内容被生成文本覆盖)。

def rouge_n(reference: str, candidate: str, n: int = 1) -> dict:
    """计算 ROUGE-N"""
    ref_tokens = reference.lower().split()
    cand_tokens = candidate.lower().split()

    # 提取 n-gram
    ref_ngrams = Counter(
        tuple(ref_tokens[i:i+n]) for i in range(len(ref_tokens) - n + 1)
    )
    cand_ngrams = Counter(
        tuple(cand_tokens[i:i+n]) for i in range(len(cand_tokens) - n + 1)
    )

    # 计算匹配
    matches = sum(
        min(ref_ngrams[ng], cand_ngrams[ng]) for ng in ref_ngrams
    )

    precision = matches / sum(cand_ngrams.values()) if cand_ngrams else 0
    recall = matches / sum(ref_ngrams.values()) if ref_ngrams else 0
    f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0

    return {"precision": precision, "recall": recall, "f1": f1}

ROUGE 的变体:

变体方法适用场景
ROUGE-11-gram 匹配词级别覆盖
ROUGE-22-gram 匹配短语级别覆盖
ROUGE-L最长公共子序列句子级别结构
ROUGE-SSkip-bigram允许跳跃的词对匹配

Perplexity(困惑度)

Perplexity 衡量模型对文本的”惊讶程度”。困惑度越低,说明模型对文本的预测越准确。

import math

def perplexity(log_probs: list[float]) -> float:
    """
    计算困惑度
    log_probs: 每个 token 的对数概率
    """
    avg_log_prob = sum(log_probs) / len(log_probs)
    return math.exp(-avg_log_prob)

# 示例
# 模型对 "我 喜欢 编程" 的预测概率
log_probs = [-1.2, -0.8, -2.1]  # 对数概率
ppl = perplexity(log_probs)
print(f"Perplexity: {ppl:.2f}")
# 较低的值 = 模型对这段文本更"有信心"

Perplexity 的直觉理解:

Perplexity = 10  → 模型在每个位置平均有 10 个等概率的选择
Perplexity = 100 → 模型在每个位置平均有 100 个等概率的选择
Perplexity 越低 → 模型越"确定"下一个词是什么 → 语言建模能力越强

LLM 评估的特殊挑战

传统指标对 LLM 来说远远不够。LLM 的输出是开放式的,评估维度也更加复杂。

评估维度

维度描述传统指标能衡量吗
事实准确性信息是否正确部分(需要事实核查)
逻辑一致性推理是否合理
有用性回答是否解决了问题
安全性是否包含有害内容
创造性回答是否有创意
格式遵循是否按要求的格式输出部分
语言质量表达是否流畅自然部分

人工评估

人工评估仍然是 LLM 评估的金标准,但成本高、速度慢。

# 人工评估模板
evaluation_template = {
    "task": "评估 AI 回答的质量",
    "criteria": [
        {
            "name": "准确性",
            "description": "回答中的信息是否准确",
            "scale": "1-5",
            "guidelines": {
                1: "包含严重错误",
                3: "基本准确,有小错误",
                5: "完全准确"
            }
        },
        {
            "name": "有用性",
            "description": "回答是否真正帮助了用户",
            "scale": "1-5",
            "guidelines": {
                1: "完全没有帮助",
                3: "部分有帮助",
                5: "非常有帮助"
            }
        },
        {
            "name": "安全性",
            "description": "回答是否安全无害",
            "scale": "1-5",
            "guidelines": {
                1: "包含有害内容",
                3: "基本安全",
                5: "完全安全"
            }
        },
    ]
}

LLM-as-Judge(用 LLM 评估 LLM)

用一个强大的 LLM 来评估另一个 LLM 的输出,是目前最流行的自动化评估方法。

import anthropic

client = anthropic.Anthropic()

def llm_judge(question: str, answer: str, criteria: list[str]) -> dict:
    """使用 Claude 作为评估者"""
    criteria_text = "\n".join(f"- {c}" for c in criteria)

    judge_prompt = f"""请评估以下 AI 回答的质量。

## 用户问题
{question}

## AI 回答
{answer}

## 评估标准
{criteria_text}

请为每个标准打分(1-5),并给出简短理由。
以 JSON 格式输出:
{{
  "scores": {{
    "标准名": {{"score": 分数, "reason": "理由"}},
    ...
  }},
  "overall": 总分,
  "summary": "总体评价"
}}"""

    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=1024,
        messages=[{"role": "user", "content": judge_prompt}]
    )

    return parse_json(response.content[0].text)

LLM-as-Judge 的注意事项:

问题描述缓解方法
位置偏见倾向于选择第一个/最后一个选项随机化选项顺序
冗长偏见倾向于给更长的回答更高分在评估标准中明确”简洁性”
自我偏见倾向于给自己生成的内容更高分使用不同模型评估
一致性同一内容多次评估结果不同多次评估取平均

主流 Benchmark

通用能力评估

Benchmark评估内容题目数量特点
MMLU57 个学科的多选题~15,000知识广度
HellaSwag常识推理~10,000日常推理
ARC科学推理~7,800科学知识
TruthfulQA事实准确性~800抗幻觉能力
Winogrande常识理解~44,000语言理解

代码能力评估

Benchmark评估内容语言特点
HumanEval函数级代码生成Python164 题,经典
MBPP基础编程问题Python974 题
MultiPL-E多语言代码生成18 种语言HumanEval 多语言版
SWE-bench真实 GitHub Issue 修复Python最接近实际开发

数学推理评估

Benchmark评估内容难度
GSM8K小学数学应用题基础
MATH竞赛级数学高级
AIME美国数学邀请赛极高

如何解读 Benchmark 分数

模型 A 在 MMLU 上得分 90%
模型 B 在 MMLU 上得分 85%

这意味着模型 A 更好吗?不一定。

需要考虑:
1. MMLU 只测试多选题,不测试开放式生成
2. 分数差异可能来自特定学科
3. Benchmark 可能被"刷分"(训练数据污染)
4. 实际使用体验可能完全不同

构建自己的评估流水线

对于实际项目,通用 Benchmark 往往不够。我们需要针对自己的场景构建评估流水线。

第一步:定义评估数据集

# 评估数据集结构
eval_dataset = [
    {
        "id": "001",
        "category": "客服问答",
        "input": "你们的退货政策是什么?",
        "reference": "我们支持 7 天无理由退货...",
        "criteria": ["准确性", "完整性", "语气"],
        "metadata": {"difficulty": "easy", "topic": "退货"}
    },
    {
        "id": "002",
        "category": "技术支持",
        "input": "我的订单显示异常,怎么办?",
        "reference": None,  # 开放式问题,没有标准答案
        "criteria": ["有用性", "步骤清晰度", "语气"],
        "metadata": {"difficulty": "medium", "topic": "订单"}
    },
]

第二步:实现评估器

interface EvalResult {
  id: string;
  model: string;
  scores: Record<string, number>;
  latency: number;
  tokenCount: number;
}

async function evaluateModel(
  model: ModelClient,
  dataset: EvalItem[],
  judge: JudgeClient
): Promise<EvalResult[]> {
  const results: EvalResult[] = [];

  for (const item of dataset) {
    const startTime = Date.now();

    // 生成回答
    const response = await model.generate(item.input);
    const latency = Date.now() - startTime;

    // 自动评估
    const scores: Record<string, number> = {};

    // 如果有参考答案,计算 ROUGE
    if (item.reference) {
      const rouge = calculateRouge(item.reference, response);
      scores["rouge_l"] = rouge.f1;
    }

    // LLM-as-Judge 评估
    const judgeResult = await judge.evaluate(
      item.input,
      response,
      item.criteria
    );
    Object.assign(scores, judgeResult.scores);

    results.push({
      id: item.id,
      model: model.name,
      scores,
      latency,
      tokenCount: estimateTokens(response),
    });
  }

  return results;
}

第三步:对比分析

def compare_models(results_a: list, results_b: list) -> dict:
    """对比两个模型的评估结果"""
    comparison = {
        "model_a": {"avg_scores": {}, "avg_latency": 0},
        "model_b": {"avg_scores": {}, "avg_latency": 0},
        "winner_by_category": {},
    }

    # 按类别汇总
    for category in set(r["category"] for r in results_a):
        cat_a = [r for r in results_a if r["category"] == category]
        cat_b = [r for r in results_b if r["category"] == category]

        avg_a = sum(r["overall_score"] for r in cat_a) / len(cat_a)
        avg_b = sum(r["overall_score"] for r in cat_b) / len(cat_b)

        comparison["winner_by_category"][category] = {
            "model_a": avg_a,
            "model_b": avg_b,
            "winner": "A" if avg_a > avg_b else "B",
        }

    return comparison

第四步:持续监控

部署前评估 → 基线分数

上线后监控 → 用户反馈 + 自动评估

定期回归测试 → 确保模型更新不降低质量

A/B 测试 → 新模型 vs 旧模型的真实对比

评估的常见陷阱

  1. Benchmark 过拟合:模型在 Benchmark 上表现好,实际使用体验差
  2. 评估数据泄露:评估数据出现在训练集中
  3. 单一指标迷信:只看一个分数,忽略其他维度
  4. 忽略延迟和成本:准确率高但延迟 10 秒的模型可能不适合实时场景
  5. 静态评估:只评估一次,不持续监控

总结

评估 AI 模型没有银弹,但我们可以建立一套系统化的方法:

  • 传统指标(BLEU、ROUGE、Perplexity)适合特定任务的快速评估
  • LLM-as-Judge 是目前最实用的自动化评估方法
  • 公开 Benchmark 用于横向对比,但不要迷信分数
  • 针对自己的场景构建评估流水线才是最重要的

不能衡量的东西就无法改进。建立评估体系的过程,本身就是深入理解问题的过程。

评论

加载中...

相关文章

分享:

评论

加载中...