📚 学习教程

【高级应用】Day11:模型评估与微调效果验证–科学评估你的微调模型

· 2026-04-12 · 12 阅读

【高级应用】Day11:模型评估与微调效果验证–科学评估你的微调模型

👤 龙主编 📅 2026-04-12 👁️ 12 阅读 💬 0 评论

章节导语

微调做完了,怎么知道效果好不好?这不是简单跑个分数能回答的问题。

很多团队在微调后只看Loss曲线,觉得Loss降了就成功了。但上线后用户反馈却很糟糕——回答生硬、不够专业、格式不稳定。问题出在哪?评估不充分。

模型评估是一个系统性问题。准确性只是最基础的维度,流畅性、相关性、格式正确性、是否有偏见、是否安全……每个维度都可能成为木桶的短板。

本文系统讲解模型评估的完整方法论,包括评估指标设计、测试集构建、自动评估、人工评估、A/B测试,以及如何根据评估结果迭代优化模型。每个概念都配有实战代码,帮助你建立完整的评估体系。

一、前置说明

1.1 学习路径

阶段 内容
前置 Fine-tuning微调(Day13)
本篇 模型评估方法

1.2 读者需要的基础

  • 机器学习基础:知道什么是训练/测试集、过拟合、泛化
  • Python基础:能处理数据和调用API
  • 微调概念:知道微调是什么、用在哪里

1.3 学习目标

学完本文,你将能够:

  • 理解模型评估的多维度体系
  • 设计完整的模型评估方案
  • 构建有代表性的测试集
  • 使用多种指标评估模型
  • 对比不同模型的优劣
  • 根据评估结果指导模型迭代

二、评估的核心概念

2.1 为什么评估重要

模型评估不是”跑完就算”,而是贯穿整个AI应用生命周期的关键活动。

不评估就上线,风险极大。可能的问题包括:

  • 场景覆盖不足:模型对某些场景效果极差,但直到用户投诉才发现
  • 输出不稳定:同样的问题,每次回答格式都不一样
  • 存在偏见:对特定群体产生歧视性输出
  • 安全性问题:输出有害内容、敏感信息
  • 性能不达标:响应时间太长、并发能力不足

一个真实的案例:某公司微调了一个客服机器人,上线前只测试了准确率。上线后发现,对”怎么退货”这个高频问题,机器人会随机回复几种不同的流程,用户体验很差。原因是训练数据中退货流程有多个版本,模型学到了所有版本但没有学到哪种是最优的。

这就是评估不充分的后果。所以我们强调:评估要在训练前就设计好,而不是训练后补做

2.2 评估类型的全景

评估类型
图1:模型评估体系

评估可以分为三种主要类型,各有优劣,结合使用效果最好:

类型 内容 优点 缺点 适用阶段
自动评估 BLEU、ROUGE、精确匹配等 成本低、速度快、可复现 不能评估语义和流畅性 开发期迭代
LLM Judge 用GPT-4评估输出质量 能评估语义相关性 成本较高、有主观性 上线前验收
人工评估 专家打分、用户反馈 最接近真实场景 成本高、周期长、主观偏差 最终验收+上线后监控

2.3 评估指标全景

不同类型的任务,需要不同的评估指标。常见的维度包括:

  • 准确性:答案是否正确,这是最基础的指标
  • 相关性:回答是否切题,有没有答非所问
  • 完整性:是否完整回答了问题的所有方面
  • 流畅性:语言是否通顺,有无语法错误
  • 安全性:是否有害内容、敏感信息、偏见
  • 格式正确性:输出格式是否符合要求
  • 一致性:同样问题是否得到同样回答
  • 专业性:术语使用是否准确、恰当

需要强调的是:没有完美的指标,也没有万能的指标。一个指标只能反映模型在某个维度的表现,需要综合多个指标才能全面评估模型。

三、测试集构建

3.1 测试集原则

测试集的质量直接决定了评估的可信度。好的测试集必须具备以下特性:

第一,代表性。测试集的分布要能代表真实场景的分布。比如客服场景,70%是退换货问题,20%是物流查询,10%是投诉。测试集也应该保持类似的比例。

第二,多样性。不同类型的问题都要覆盖。既有简单的事实性问题,也有复杂的推理问题,还要包含边界情况。

第三,无污染。测试集不能和训练数据重叠。如果测试集中的样本在训练时见过,评估结果就会虚高,不能反映真实效果。

第四,可维护性。测试集要随着业务变化定期更新。比如新增了产品线,测试集就要相应增加相关用例。

3.2 测试用例设计方法

测试用例分层
图2:测试用例分层设计

测试用例按照难度可以分为三层:

基础层(Easy):简单直接的问题,模型应该能完美回答。比如”你们几点营业”。这类问题用于验证模型的基本能力是否具备。

进阶层(Medium):需要一定推理或包含多个要素的问题。比如”我上周买的还没到,怎么查物流”。这类问题测试模型的综合能力。

挑战层(Hard):复杂、模糊、或有陷阱的问题。比如用户表述不清、包含多个意图、或者有歧义的问题。这类问题用于测试模型的鲁棒性。

3.3 边界测试用例

除了正常用例,还要测试边界情况。这些往往是问题的高发区:

  • 空输入:用户什么都没输入,或者只输入了标点符号
  • 超长输入:超过模型上下文限制的文本
  • 特殊字符:emoji、HTML标签、代码片段等
  • 对抗样本:故意刁难、试探边界的问题
  • 模糊问题:表述不清晰,需要猜测意图的问题
  • 多语言混合:中英文混杂、专业术语缩写

这些边界情况虽然出现频率低,但处理不好会严重影响用户体验,甚至引发安全事件。

四、自动评估指标

4.1 文本相似度指标

文本相似度是最常用的自动评估指标,包括BLEU、ROUGE、精确匹配等。

BLEU(Bilingual Evaluation Understudy)最初是用于机器翻译的评估指标,通过计算生成文本和参考文本的N-gram重叠度来评估质量。BLEU分数越高,表示生成文本和参考文本越相似。但BLEU有明显的局限性:它只看词汇重叠,不考虑语义。

ROUGE(Recall-Oriented Understudy for Gisting Evaluation)则更注重召回率,衡量生成文本中有多少内容被参考文本覆盖。ROUGE-L使用最长公共子序列计算,ROUGE-1使用单词级重叠,ROUGE-2使用bigram重叠。

精确匹配(Exact Match)是最严格的标准,只有当生成文本与参考文本完全一致时才是1,否则是0。这种指标适用于格式固定、答案唯一的场景,比如JSON输出。

在实际应用中,我们通常同时计算多个指标。单一指标可能产生误导——比如模型输出了完全不同的表述但意思相近,BLEU可能很低但人工评估却很好。

4.2 分类任务评估

对于分类任务,常用的指标包括准确率、精确率、召回率、F1分数。

准确率(Accuracy)是最直觉的指标,表示预测正确的样本占总样本的比例。但当类别不平衡时,准确率会失真——比如99%是负样本,模型全部预测负样本也能达到99%准确率。

精确率(Precision)是”预测为正的样本中,真正是正的比例”。高精确率意味着误报少。

召回率(Recall)是”真正是正的样本中,被预测为正的比例”。高召回率意味着漏报少。

F1分数是精确率和召回率的调和平均,综合反映两个指标。

在实际评估中,我们不仅要关注整体指标,还要看每个类别的指标。有些类别可能表现很差,但被整体指标掩盖了。

4.3 格式验证

对于需要结构化输出的场景,格式验证是必不可少的评估维度。

常见的格式要求包括:输出必须是有效的JSON、必须包含某些字段、字段值必须在指定范围内。这些都可以用规则引擎自动检查。

格式错误不仅影响使用,还可能导致系统崩溃。比如期望解析JSON但收到了纯文本,后续程序可能直接报错。

五、LLM Judge评估

5.1 什么是LLM Judge

LLM Judge是用强模型(如GPT-4)来评估弱模型输出的质量。这种方法近年来被广泛采用,因为它能够捕捉语义层面的质量差异,而这是传统自动指标做不到的。

LLM Judge的核心思想是:让GPT-4扮演评估者,对模型输出从多个维度打分。GPT-4有强大的语言理解和推理能力,能够判断回答是否相关、是否完整、是否有帮助。

5.2 LLM Judge的优势与局限

相比纯自动评估,LLM Judge能评估更多维度,包括语义相关性、回答的专业性、逻辑连贯性等。但它也有局限:

  • 成本较高:每个样本都需要调用GPT-4
  • 主观性:不同时间、不同提示词可能给出不同评分
  • 可能存在偏见:GPT-4可能偏好冗长、正式的回复

实际应用中,建议先用自动指标筛选出有问题的样本,再用LLM Judge重点评估。

5.3 对比评估

除了给单个输出打分,还可以用LLM Judge对比两个模型的输出。让模型A和模型B分别回答同一个问题,然后让GPT-4评判哪个回答更好。

对比评估特别适合以下场景:

  • 对比基线模型和微调模型的效果差异
  • 对比不同Prompt策略的效果
  • 验证微调是否真的带来了提升

六、综合评估系统

6.1 评估管道的架构

一个完整的评估管道应该包含以下环节:

第一步:准备测试集。测试集要覆盖各种场景、各种难度级别。

第二步:批量推理。用待评估的模型对测试集中的每个样本生成回答。

第三步:自动评估。计算BLEU、ROUGE、准确率等指标。

第四步:格式验证。检查输出是否符合要求的格式。

第五步:LLM Judge评估。让GPT-4对代表性样本进行评分。

第六步:人工抽检。抽取部分样本由人工评估,作为LLM Judge的校验。

第七步:汇总报告。生成可视化报告,发现问题。

6.2 评估结果的解读

得到评估结果后,关键是怎么解读。

看趋势,不看单点数值。单次评估的意义有限,重要的是看模型迭代过程中指标的变化趋势。如果每次迭代都有提升,说明优化方向是对的。

看分布,不只看平均。平均分可能被极端值拉偏。看指标在不同分位数的表现——P50、P90、P99——能更全面地了解模型状态。

找短板,不只关注总分。木桶效应决定了系统性能取决于最短板。如果某个类别、某个维度的指标特别差,优先解决这个短板。

七、实战评估案例

7.1 客服机器人评估实战

以客服机器人为例,看一个完整的评估流程。

背景:某电商平台的客服机器人,需要回答用户关于订单、物流,退换货等问题。机器人基于GPT-3.5微调,训练数据来自历史客服对话。

评估设计

  • 测试集:200个真实用户query,按类型分为退货(30%)、物流(25%)、咨询(25%)、投诉(20%)
  • 难度分布:Easy 30%、Medium 50%、Hard 20%
  • 评估指标:准确性、相关性、完整性、格式正确性、禁止性回复检测

评估结果

  • 整体准确率78%,但类别差异大:咨询类92%、投诉类只有56%
  • 格式正确率95%,表现良好
  • 禁止性回复检测:3%的回复包含”保证”、”100%”等词
  • LLM Judge评分:专业性4.1/5、礼貌性4.5/5、完整性3.8/5

问题发现

  • 投诉类问题表现差,分析发现训练数据中投诉类样本少且质量差
  • 完整性不足,回答经常漏掉重要信息,比如退款到账时间
  • 存在合规风险,有3%的回复包含可能违规的表述

优化建议

  • 增加投诉类训练数据,特别是复杂场景
  • 优化Prompt,增加回答完整性的引导
  • 添加合规检查模块,自动过滤违规表述

八、评估报告与迭代

8.1 评估报告的结构

一份完整的评估报告应该包含以下内容:

摘要:用2-3句话概括本次评估的核心发现和结论。

测试集说明:测试集的大小、分布、来源,让读者判断评估的代表性。

评估指标结果:每个指标的具体数值,最好和上一次评估做对比。

问题分析:发现了哪些问题,严重程度如何。

改进建议:针对问题提出具体的优化方向。

附录:原始数据、详细评分、典型case等。

8.2 评估驱动迭代

评估的最终目的是指导模型迭代。每次迭代后重新评估,对比效果变化,验证优化是否有效。

建议建立评估仪表盘,实时监控关键指标。指标下降时及时告警,指标持续提升说明优化方向正确。

同时要建立回归测试机制。每次更新模型后,必须跑完所有测试用例,确保没有引入新问题。

九、测试集构建代码

9.1 测试集构建器

import json
import random
from typing import List, Dict
from dataclasses import dataclass

@dataclass
class TestCase:
    """测试用例"""
    input_text: str           # 输入
    expected_output: str      # 期望输出
    category: str            # 分类
    difficulty: str          # 难度:easy/medium/hard
    metadata: dict           # 其他信息

class TestSetBuilder:
    """测试集构建器
    
    从原始数据中构建有代表性的测试集
    """
    
    def __init__(self):
        self.test_cases = []
    
    def add_case(self, input_text: str, expected: str, category: str, difficulty: str = "medium"):
        """添加测试用例"""
        case = TestCase(
            input_text=input_text,
            expected_output=expected,
            category=category,
            difficulty=difficulty,
            metadata={}
        )
        self.test_cases.append(case)
    
    def split_by_difficulty(self, train_ratio: float = 0.8) -> tuple:
        """按难度分层采样划分训练/测试集"""
        by_difficulty = {}
        for case in self.test_cases:
            if case.difficulty not in by_difficulty:
                by_difficulty[case.difficulty] = []
            by_difficulty[case.difficulty].append(case)
        
        train_set = []
        test_set = []
        
        for difficulty, cases in by_difficulty.items():
            random.shuffle(cases)
            split_idx = int(len(cases) * train_ratio)
            train_set.extend(cases[:split_idx])
            test_set.extend(cases[split_idx:])
        
        return train_set, test_set
    
    def split_by_category(self, test_categories: List[str], test_ratio: float = 0.2) -> tuple:
        """按类别划分:某些类别全在测试集"""
        test_set = []
        train_set = []
        
        for case in self.test_cases:
            if case.category in test_categories:
                test_set.append(case)
            else:
                train_set.append(case)
        
        return train_set, test_set
    
    def export(self, file_path: str, format: str = "jsonl"):
        """导出测试集"""
        if format == "jsonl":
            with open(file_path, 'w', encoding='utf-8') as f:
                for case in self.test_cases:
                    data = {
                        "input": case.input_text,
                        "expected": case.expected_output,
                        "category": case.category,
                        "difficulty": case.difficulty
                    }
                    f.write(json.dumps(data, ensure_ascii=False) + '\n')
        
        elif format == "json":
            data = [{
                "input": c.input_text,
                "expected": c.expected_output,
                "category": c.category,
                "difficulty": c.difficulty
            } for c in self.test_cases]
            with open(file_path, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=2)
        
        print(f"✅ 导出{len(self.test_cases)}个测试用例到{file_path}")

# 使用
if __name__ == "__main__":
    builder = TestSetBuilder()
    
    # 添加各类别测试用例
    categories = ["退货", "物流", "支付", "优惠", "账户"]
    difficulties = ["easy", "medium", "hard"]
    
    for i in range(50):
        cat = random.choice(categories)
        diff = random.choice(difficulties)
        
        builder.add_case(
            input_text=f"用户问题{i+1}({cat}类,{diff}难度)",
            expected=f"标准回答{i+1}",
            category=cat,
            difficulty=diff
        )
    
    # 分层划分
    train, test = builder.split_by_difficulty(train_ratio=0.8)
    
    print(f"训练集: {len(train)}个")
    print(f"测试集: {len(test)}个")
    
    # 按类别导出(物流类全在测试集)
    train2, test2 = builder.split_by_category(test_categories=["物流"], test_ratio=0.2)
    print(f"\n按类别划分后:")
    print(f"训练集: {len(train2)}个")
    print(f"测试集: {len(test2)}个")

十、自动评估指标代码

10.1 文本评估指标计算器

from rouge_score import rouge_scorer
from sklearn.metrics import precision_score, recall_score, f1_score
import Levenshtein

class TextMetrics:
    """文本评估指标计算器"""
    
    def __init__(self):
        self.rouge_scorer = rouge_scorer.RougeScorer(
            ['rouge1', 'rouge2', 'rougeL'],
            use_stemmer=True
        )
    
    def rouge_score(self, prediction: str, reference: str) -> dict:
        """计算ROUGE分数
        
        ROUGE-N: N-gram共现
        ROUGE-L: 最长公共子序列
        """
        scores = self.rouge_scorer.score(reference, prediction)
        
        return {
            'rouge1': scores['rouge1'].fmeasure,
            'rouge2': scores['rouge2'].fmeasure,
            'rougeL': scores['rougeL'].fmeasure
        }
    
    def exact_match(self, prediction: str, reference: str) -> float:
        """精确匹配率"""
        return 1.0 if prediction.strip() == reference.strip() else 0.0
    
    def edit_distance_ratio(self, prediction: str, reference: str) -> float:
        """编辑距离比率(相似度)"""
        distance = Levenshtein.distance(prediction, reference)
        max_len = max(len(prediction), len(reference))
        return 1.0 - (distance / max_len) if max_len > 0 else 1.0
    
    def word_overlap(self, prediction: str, reference: str) -> dict:
        """词级别重叠率"""
        pred_words = set(prediction.lower().split())
        ref_words = set(reference.lower().split())
        
        if not ref_words:
            return {'precision': 0, 'recall': 0, 'f1': 0}
        
        overlap = pred_words & ref_words
        
        precision = len(overlap) / len(pred_words) if pred_words else 0
        recall = len(overlap) / len(ref_words) if ref_words else 0
        f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0
        
        return {'precision': precision, 'recall': recall, 'f1': f1}
    
    def evaluate_single(self, prediction: str, reference: str) -> dict:
        """综合评估单个样本"""
        return {
            'rouge': self.rouge_score(prediction, reference),
            'exact_match': self.exact_match(prediction, reference),
            'edit_similarity': self.edit_distance_ratio(prediction, reference),
            'word_overlap': self.word_overlap(prediction, reference)
        }
    
    def evaluate_batch(self, predictions: list, references: list) -> dict:
        """批量评估"""
        results = {
            'rouge1': [], 'rouge2': [], 'rougeL': [],
            'exact_match': [],
            'edit_similarity': [],
            'word_f1': []
        }
        
        for pred, ref in zip(predictions, references):
            metrics = self.evaluate_single(pred, ref)
            results['rouge1'].append(metrics['rouge']['rouge1'])
            results['rouge2'].append(metrics['rouge']['rouge2'])
            results['rougeL'].append(metrics['rouge']['rougeL'])
            results['exact_match'].append(metrics['exact_match'])
            results['edit_similarity'].append(metrics['edit_similarity'])
            results['word_f1'].append(metrics['word_overlap']['f1'])
        
        # 计算平均值
        avg_results = {}
        for key, values in results.items():
            avg_results[f'avg_{key}'] = sum(values) / len(values) if values else 0
        
        return avg_results

# 使用
if __name__ == "__main__":
    calculator = TextMetrics()
    
    pred = "这是一个测试回答,包含了一些信息。"
    ref = "这是一个测试回答,包含了相关信息。"
    
    # 单个评估
    result = calculator.evaluate_single(pred, ref)
    print("单个样本评估:")
    print(f"  ROUGE-L: {result['rouge']['rougeL']:.4f}")
    print(f"  精确匹配: {result['exact_match']:.4f}")
    print(f"  编辑相似度: {result['edit_similarity']:.4f}")
    
    # 批量评估
    preds = ["回答1", "回答2", "回答3"]
    refs = ["标准答案1", "标准答案2", "标准答案3"]
    
    batch_result = calculator.evaluate_batch(preds, refs)
    print("\n批量评估平均:")
    for key, value in batch_result.items():
        print(f"  {key}: {value:.4f}")

十一、LLM Judge评估代码

11.1 LLM Judge评估器

from openai import OpenAI
import os
from typing import List, Dict

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

class LLMJudge:
    """LLM Judge评估器
    
    使用强模型评估输出质量
    """
    
    def __init__(self, judge_model: str = "gpt-4"):
        self.judge_model = judge_model
    
    def create_judge_prompt(self, question: str, answer: str, criteria: List[str]) -> str:
        """创建评判Prompt"""
        criteria_text = "\n".join([f"{i+1}. {c}" for i, c in enumerate(criteria)])
        
        prompt = f"""请评估以下问答系统的回答质量。

问题:{question}

回答:{answer}

评估标准:
{criteria_text}

请为每个标准给出1-5分的评分(1=很差,5=很好),并说明理由。

输出格式:
标准1-评分: X分
标准1-理由: ...
标准2-评分: X分
标准2-理由: ...
总体评分: X分"""
        
        return prompt
    
    def judge_single(self, question: str, answer: str, criteria: List[str] = None) -> Dict:
        """评判单个样本
        
        默认评估维度:
        - 准确性:回答是否正确
        - 完整性:是否完整回答问题
        - 流畅性:语言是否通顺
        - 有用性:是否对用户有帮助
        """
        if criteria is None:
            criteria = ["准确性", "完整性", "流畅性", "有用性"]
        
        prompt = self.create_judge_prompt(question, answer, criteria)
        
        response = client.chat.completions.create(
            model=self.judge_model,
            messages=[{"role": "user", "content": prompt}]
        )
        
        result_text = response.choices[0].message.content
        
        # 解析评分
        scores = {}
        for criterion in criteria:
            pattern = f"{criterion}-评分:\\s*(\\d)"
            import re
            match = re.search(pattern, result_text)
            if match:
                scores[criterion] = int(match.group(1))
        
        # 解析总体评分
        overall_match = re.search(r"总体评分:\\s*(\\d)", result_text)
        overall = int(overall_match.group(1)) if overall_match else 3
        
        return {
            'scores': scores,
            'overall': overall,
            'reasoning': result_text
        }
    
    def judge_batch(self, questions: List[str], answers: List[str], 
                   criteria: List[str] = None) -> List[Dict]:
        """批量评判"""
        results = []
        
        for i, (q, a) in enumerate(zip(questions, answers)):
            print(f"评判 {i+1}/{len(questions)}...")
            result = self.judge_single(q, a, criteria)
            results.append(result)
        
        return results

# 使用
if __name__ == "__main__":
    judge = LLMJudge()
    
    question = "如何退货?"
    answer = "您可以在APP中进入'我的订单',选择订单后点击'申请退货',填写原因后提交即可。"
    
    result = judge.judge_single(question, answer)
    
    print("评估结果:")
    for criterion, score in result['scores'].items():
        print(f"  {criterion}: {score}分")
    print(f"  总体: {result['overall']}分")
    print(f"\n理由:\n{result['reasoning']}")

十二、综合评估系统代码

12.1 综合评估管道

class ComprehensiveEvaluator:
    """综合评估系统
    
    整合多种评估方法
    """
    
    def __init__(self):
        self.text_metrics = TextMetrics()
        self.format_validator = None
        self.llm_judge = LLMJudge()
    
    def set_format_spec(self, format_spec: dict):
        """设置格式规范"""
        self.format_validator = FormatValidator(format_spec)
    
    def evaluate(self, question: str, prediction: str, reference: str = None,
                enable_auto: bool = True, enable_llm: bool = True) -> Dict:
        """综合评估
        
        参数:
            question: 问题
            prediction: 模型预测输出
            reference: 参考答案(可选)
            enable_auto: 是否启用自动评估
            enable_llm: 是否启用LLM评估
        """
        results = {
            'question': question,
            'prediction': prediction,
            'auto_metrics': {},
            'format_valid': None,
            'llm_judge': None
        }
        
        # 自动评估指标
        if enable_auto and reference:
            results['auto_metrics'] = self.text_metrics.evaluate_single(
                prediction, reference
            )
        
        # 格式验证
        if self.format_validator:
            format_result = self.format_validator.validate(prediction)
            results['format_valid'] = format_result['valid']
            results['format_errors'] = format_result.get('errors', [])
        
        # LLM Judge评估
        if enable_llm:
            results['llm_judge'] = self.llm_judge.judge_single(
                question, prediction
            )
        
        return results
    
    def evaluate_dataset(self, dataset: List[Dict], 
                        enable_auto: bool = True,
                        enable_llm: bool = True) -> Dict:
        """评估整个数据集"""
        results = []
        
        for i, item in enumerate(dataset):
            print(f"评估 {i+1}/{len(dataset)}...")
            
            question = item.get('question', '')
            prediction = item.get('prediction', item.get('output', ''))
            reference = item.get('reference', item.get('expected', ''))
            
            result = self.evaluate(
                question, prediction, reference,
                enable_auto, enable_llm
            )
            results.append(result)
        
        # 汇总统计
        summary = self._summarize(results)
        
        return {
            'detailed_results': results,
            'summary': summary
        }
    
    def _summarize(self, results: List[Dict]) -> Dict:
        """汇总结果"""
        summary = {
            'total': len(results),
            'auto_metrics_avg': {},
            'format_accuracy': 0,
            'llm_judge_avg': {}
        }
        
        # 统计自动指标
        if results[0].get('auto_metrics'):
            for key in ['rouge1', 'rouge2', 'rougeL']:
                values = [r['auto_metrics'].get('rouge', {}).get(key, 0) 
                         for r in results if r.get('auto_metrics')]
                summary['auto_metrics_avg'][key] = sum(values) / len(values) if values else 0
        
        # 格式准确率
        format_valid_count = sum(1 for r in results if r.get('format_valid') == True)
        summary['format_accuracy'] = format_valid_count / len(results) if results else 0
        
        # LLM Judge平均
        if results[0].get('llm_judge'):
            overall_scores = [r['llm_judge']['overall'] for r in results if r.get('llm_judge')]
            summary['llm_judge_avg']['overall'] = sum(overall_scores) / len(overall_scores) if overall_scores else 0
        
        return summary

# 使用
if __name__ == "__main__":
    evaluator = ComprehensiveEvaluator()
    
    # 设置输出格式要求
    evaluator.set_format_spec({
        "type": "json",
        "required_fields": ["answer", "confidence"]
    })
    
    # 评估数据
    dataset = [
        {
            "question": "退货政策是什么?",
            "prediction": '{"answer": "7天内可退货", "confidence": 0.9}',
            "reference": "支持7天无理由退货"
        },
        {
            "question": "发货时间?",
            "prediction": '{"answer": "24小时内发货", "confidence": 0.8}',
            "reference": "24小时发货"
        }
    ]
    
    results = evaluator.evaluate_dataset(dataset, enable_auto=True, enable_llm=True)
    
    print("评估汇总:")
    print(f"  总样本: {results['summary']['total']}")
    print(f"  格式准确率: {results['summary']['format_accuracy']:.2%}")
    print(f"  LLM评分平均: {results['summary']['llm_judge_avg'].get('overall', 'N/A')}")

十三、总结与练习

13.1 要点回顾

  • 评估要前置:在训练前就设计好评估方案
  • 多维度评估:准确性、流畅性、安全性、格式缺一不可
  • 测试集质量:代表性、多样性、无污染、可维护
  • 自动+人工:自动指标快速迭代,LLM Judge深度评估
  • 趋势比单点重要:持续监控、回归测试、迭代优化

13.2 评估检查清单

□ 测试集覆盖主要场景
□ 包含边界/对抗样本
□ 自动指标评估完成
□ 格式验证通过
□ LLM Judge评估完成(如适用)
□ 人工评估验证(如适用)
□ 生成评估报告
□ 发现问题并提出改进

13.3 延伸阅读

13.4 课后练习

基础题:构建一个包含100个测试用例的测试集,包含至少3个场景类别。

进阶题:实现LLM Judge批量评估,对比两个模型的效果差异。

挑战题:设计一个持续评估系统,在模型上线后持续监控效果指标。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

微信公众号二维码

扫码关注公众号

QQ
QQ二维码

扫码添加QQ