Evals用户手册:轻松构建自定义评估任务

【免费下载链接】evals Evals is a framework for evaluating LLMs and LLM systems, and an open-source registry of benchmarks. 【免费下载链接】evals 项目地址: https://gitcode.com/gh_mirrors/ev/evals

1. Evals框架核心概念与架构

Evals是一个用于评估大型语言模型(LLM)和LLM系统的开源框架,提供了丰富的基准测试工具和可扩展架构。本手册将指导您从零开始构建自定义评估任务,深入理解评估流程的每一个环节。

1.1 核心组件与工作流程

Evals框架采用模块化设计,主要包含以下核心组件:

mermaid

  • 评估类(Eval): 核心逻辑实现,继承自evals.Eval抽象基类
  • 数据集(JSONL): 存储评估用例,支持训练集( few-shot示例)和测试集
  • 配置文件(YAML): 注册评估任务元数据和参数
  • 指标(Metrics): 结果评估标准,如准确率、F1分数等
  • 记录器(Recorder): 记录评估过程和结果,支持本地文件和数据库存储

1.2 评估类型与适用场景

Evals提供多种预定义评估模板,满足不同场景需求:

评估类型 基类 适用场景 核心方法
基础匹配 evals.elsuite.basic.match.Match 简单答案匹配 record_and_check_match
模糊匹配 evals.elsuite.basic.fuzzy_match.FuzzyMatch 容错答案比较 fuzzy_match
包含匹配 evals.elsuite.basic.includes.Includes 关键词检测 includes
多项选择 evals.elsuite.multiple_choice.MultipleChoice 选择题评估 generate_choice_prompt
模型分级 evals.elsuite.modelgraded.classify.ModelBasedClassify 主观质量评估 model_grade

2. 构建自定义评估任务的完整流程

2.1 环境准备与项目结构

首先确保已按照README.md完成环境搭建,推荐使用Python 3.9+版本。自定义评估任务的标准项目结构如下:

evals/
├── elsuite/                  # 评估逻辑实现
│   └── custom/               # 自定义评估目录
│       └── my_eval.py        # 评估类实现
├── registry/                 # 注册配置
│   ├── evals/                # 评估配置
│   │   └── my_eval.yaml      # 评估元数据
│   └── data/                 # 数据集
│       └── my_eval/          # 数据文件目录
│           ├── train.jsonl   # 训练集(few-shot)
│           └── test.jsonl    # 测试集
└── examples/                 # 使用示例
    └── my_eval_demo.ipynb    # Jupyter演示

2.2 步骤一:创建评估数据集

评估数据采用JSON Lines格式(JSONL),每行包含一个评估样本。以下是一个简单的算术评估数据集示例:

创建训练集 (train.jsonl):

echo -e '{"input": [{"role": "user", "content": "2+2="}, {"role": "assistant", "content": "4"}]}\n{"input": [{"role": "user", "content": "3*5="}, {"role": "assistant", "content": "15"}]}\n' > evals/registry/data/my_eval/train.jsonl

创建测试集 (test.jsonl):

echo -e '{"input": [{"role": "user", "content": "48+2="}], "ideal": "50"}\n{"input": [{"role": "user", "content": "5*20="}], "ideal": "100"}\n{"input": [{"role": "user", "content": "120/3="}], "ideal": "40"}\n' > evals/registry/data/my_eval/test.jsonl

JSONL文件格式说明:

  • input: 模型输入,支持纯文本字符串或聊天格式数组
  • ideal: 预期答案,支持单个字符串或多个可能答案的数组
  • 可包含自定义字段,如难度级别、类别标签等

2.3 步骤二:实现评估类

评估类是自定义评估的核心,需要继承evals.Eval抽象基类并实现关键方法。以下是一个完整的算术评估实现:

# evals/elsuite/custom/my_eval.py
import random
import evals
import evals.metrics
from evals.prompt.base import is_chat_prompt

class ArithmeticEval(evals.Eval):
    """算术能力评估类,测试模型基本数学运算能力"""
    
    def __init__(
        self,
        completion_fns,
        test_jsonl,
        train_jsonl=None,
        num_few_shot=0,
        *args, **kwargs
    ):
        super().__init__(completion_fns, *args, **kwargs)
        self.test_jsonl = test_jsonl
        self.train_jsonl = train_jsonl
        self.num_few_shot = num_few_shot
        if self.num_few_shot > 0:
            assert train_jsonl is not None, "few-shot评估需要提供训练集"
            self.train_samples = evals.get_jsonl(self.train_jsonl)
    
    def eval_sample(self, test_sample, rng: random.Random):
        """评估单个样本
        
        Args:
            test_sample: 测试样本字典
            rng: 随机数生成器,用于few-shot示例选择
        """
        # 构建提示
        prompt = []
        
        # 添加few-shot示例
        if self.num_few_shot > 0:
            # 随机选择few-shot示例
            few_shot_samples = rng.sample(self.train_samples, self.num_few_shot)
            for sample in few_shot_samples:
                prompt.extend(sample["input"])
        
        # 添加测试问题
        prompt.extend(test_sample["input"])
        
        # 获取模型输出
        result = self.completion_fn(
            prompt=prompt,
            temperature=0.0,  # 确定性输出
            max_tokens=10     # 限制输出长度
        )
        sampled = result.get_completions()[0].strip()
        
        # 检查结果并记录
        return evals.record_and_check_match(
            prompt=prompt,
            sampled=sampled,
            expected=test_sample["ideal"]
        )
    
    def run(self, recorder):
        """运行完整评估流程
        
        Args:
            recorder: 记录器实例,用于记录评估结果
        """
        # 加载测试样本
        test_samples = evals.get_jsonl(self.test_jsonl)
        
        # 评估所有样本
        self.eval_all_samples(recorder, test_samples)
        
        # 计算并返回指标
        events = recorder.get_events("match")
        return {
            "accuracy": evals.metrics.get_accuracy(events),
            "bootstrap_std": evals.metrics.get_bootstrap_accuracy_std(events),
            "total": len(events)
        }

关键方法解析:

  • __init__: 初始化评估参数,加载必要资源
  • eval_sample: 评估单个样本,实现提示构建、模型调用和结果检查
  • run: 评估入口,加载数据、批量评估样本并计算指标

2.4 步骤三:注册评估任务

创建YAML配置文件注册评估任务,使Evals框架能够识别和调用:

# evals/registry/evals/my_eval.yaml
arithmetic:
  id: arithmetic.custom.v1
  metrics: [accuracy]
  description: 评估模型基本算术运算能力,包括加减乘除
  
arithmetic.custom.v1:
  class: evals.elsuite.custom.my_eval.ArithmeticEval
  args:
    test_jsonl: evals/registry/data/my_eval/test.jsonl
    train_jsonl: evals/registry/data/my_eval/train.jsonl
    num_few_shot: 2  # 使用2个few-shot示例
  metadata:
    version: 1.0
    author: your_name
    tags: [math, arithmetic, basic]

配置文件说明:

  • id: 评估任务唯一标识符
  • class: 评估类的完整路径
  • args: 传递给评估类构造函数的参数
  • metrics: 主要评估指标列表,第一个为主要指标
  • metadata: 自定义元数据,用于文档和分类

2.5 步骤四:运行与分析评估

使用oaieval命令行工具运行自定义评估:

# 基础用法
oaieval gpt-3.5-turbo arithmetic

# 详细日志模式
oaieval gpt-3.5-turbo arithmetic --verbose

# 指定输出目录
oaieval gpt-3.5-turbo arithmetic --log_to_file ./eval_results/arithmetic.jsonl

命令行参数说明:

  • 第一个参数:模型名称,如gpt-3.5-turbotext-davinci-003
  • 第二个参数:评估任务ID,即YAML中定义的评估名称
  • --verbose: 显示详细评估过程
  • --log_to_file: 指定结果输出文件路径

典型输出示例:

[eval.py:32] Evaluating 3 samples
[eval.py:138] Running in threaded mode with 1 threads!
100%|██████████████████████████████████████████| 3/3 [00:02<00:00,  1.23it/s]
[record.py:320] Final report: {'accuracy': 1.0, 'bootstrap_std': 0.0, 'total': 3}. Logged to /tmp/evallogs/run_20231115_103045_gpt-3.5-turbo_arithmetic.jsonl
[oaieval.py:172] accuracy: 1.0

2.6 步骤五:结果分析与可视化

评估完成后,可通过记录的JSONL文件进行深入分析:

# 分析脚本示例
import json
import matplotlib.pyplot as plt
from collections import defaultdict

# 加载评估结果
results = []
with open("/tmp/evallogs/run_20231115_103045_gpt-3.5-turbo_arithmetic.jsonl", "r") as f:
    for line in f:
        results.append(json.loads(line))

# 提取匹配事件
match_events = [r for r in results if r["type"] == "match"]

# 按问题类型统计准确率
category_stats = defaultdict(lambda: {"correct": 0, "total": 0})
for event in match_events:
    problem = event["data"]["prompt"][-1]["content"]
    if "+" in problem:
        category = "addition"
    elif "-" in problem:
        category = "subtraction"
    elif "*" in problem:
        category = "multiplication"
    elif "/" in problem:
        category = "division"
    else:
        category = "other"
    
    category_stats[category]["total"] += 1
    if event["data"]["correct"]:
        category_stats[category]["correct"] += 1

# 绘制分类准确率条形图
categories = list(category_stats.keys())
accuracies = [category_stats[c]["correct"]/category_stats[c]["total"] for c in categories]

plt.figure(figsize=(10, 6))
plt.bar(categories, accuracies)
plt.title("Arithmetic Evaluation Accuracy by Operation Type")
plt.ylabel("Accuracy")
plt.ylim(0, 1.0)
plt.grid(axis="y", linestyle="--", alpha=0.7)
plt.show()

3. 高级技巧与最佳实践

3.1 复杂评估场景实现

3.1.1 多轮对话评估

对于需要上下文记忆的任务,可实现多轮对话评估:

def eval_sample(self, test_sample, rng: random.Random):
    # 初始化对话历史
    conversation = []
    
    # 处理多轮交互
    for turn in test_sample["turns"]:
        # 添加用户输入
        conversation.append({"role": "user", "content": turn["input"]})
        
        # 获取模型回复
        result = self.completion_fn(
            prompt=conversation,
            temperature=0.7,
            max_tokens=150
        )
        response = result.get_completions()[0]
        
        # 添加模型回复到对话历史
        conversation.append({"role": "assistant", "content": response})
        
        # 检查当前轮次答案(如果有)
        if "ideal" in turn:
            evals.record_and_check_match(
                prompt=conversation,
                sampled=response,
                expected=turn["ideal"],
                event_type=f"match_turn_{turn['turn_id']}"
            )
    
    return True
3.1.2 自定义指标实现

当内置指标无法满足需求时,可实现自定义指标:

# 自定义指标实现
def mean_reciprocal_rank(ranked_results):
    """计算平均倒数排名(MRR)"""
    reciprocal_ranks = []
    for result in ranked_results:
        for rank, item in enumerate(result["ranked_items"]):
            if item["correct"]:
                reciprocal_ranks.append(1.0 / (rank + 1))
                break
        else:
            reciprocal_ranks.append(0.0)
    return sum(reciprocal_ranks) / len(reciprocal_ranks)

# 在评估类中使用
def run(self, recorder):
    # 评估逻辑...
    
    # 计算自定义指标
    ranked_events = recorder.get_events("ranked_match")
    return {
        "accuracy": evals.metrics.get_accuracy(match_events),
        "mrr": mean_reciprocal_rank(ranked_events),
    }

3.2 性能优化策略

  • 批量处理: 使用eval_all_samplesbatch_size参数启用批量评估
  • 缓存机制: 对静态数据集启用缓存,避免重复加载
  • 并行评估: 通过num_workers参数启用多线程评估
  • 资源控制: 设置合理的max_tokenstimeout避免资源浪费
# 优化的run方法示例
def run(self, recorder):
    samples = self.get_samples()
    
    # 启用批量处理和多线程
    self.eval_all_samples(
        recorder, 
        samples,
        batch_size=8,          # 批量大小
        num_workers=4          # 工作线程数
    )
    
    # 结果处理...

3.3 常见问题与解决方案

问题 原因 解决方案
评估速度慢 单线程处理大量样本 启用多线程num_workers>1
内存占用高 一次性加载过大数据集 实现流式数据加载
结果不稳定 随机因素影响 设置固定随机种子,多次评估取平均
模型调用成本高 重复调用相同提示 实现提示缓存机制
复杂逻辑调试难 评估流程复杂 添加详细日志,使用单元测试

3.4 单元测试与质量保证

为确保评估逻辑正确性,应编写单元测试:

# evals/elsuite/custom/tests/test_my_eval.py
import unittest
import random
from evals.elsuite.custom.my_eval import ArithmeticEval
from evals.completion_fns.mock import MockCompletionFn

class TestArithmeticEval(unittest.TestCase):
    def setUp(self):
        # 创建模拟模型
        self.mock_completion_fn = MockCompletionFn(
            return_value="50"  # 固定返回"50"
        )
        
        # 创建评估实例
        self.eval = ArithmeticEval(
            completion_fns=[self.mock_completion_fn],
            test_jsonl="evals/registry/data/my_eval/test.jsonl",
            train_jsonl="evals/registry/data/my_eval/train.jsonl",
            num_few_shot=2
        )
        
        # 固定随机种子
        self.rng = random.Random(42)
    
    def test_eval_sample(self):
        # 测试单个样本评估
        sample = {
            "input": [{"role": "user", "content": "48+2="}],
            "ideal": "50"
        }
        
        result = self.eval.eval_sample(sample, self.rng)
        self.assertTrue(result)  # 应返回True(正确匹配)
    
    def test_eval_sample_incorrect(self):
        # 测试错误答案场景
        self.mock_completion_fn.return_value = "49"  # 返回错误答案
        
        sample = {
            "input": [{"role": "user", "content": "48+2="}],
            "ideal": "50"
        }
        
        result = self.eval.eval_sample(sample, self.rng)
        self.assertFalse(result)  # 应返回False(错误匹配)

if __name__ == "__main__":
    unittest.main()

4. 总结与进阶学习

通过本手册,您已掌握使用Evals框架构建自定义评估任务的完整流程,包括数据集创建、评估类实现、任务注册和结果分析。Evals框架的灵活性使您能够评估LLM在各种场景下的性能,从简单的答案匹配到复杂的多轮对话任务。

4.1 进阶学习资源

  • 官方示例: 查看evals/examples/目录下的Jupyter notebooks
  • 内置评估: 研究evals/elsuite/目录下的现有评估实现
  • API文档: 详细了解evals/api.py中的工具函数
  • 社区贡献: 参与GitHub仓库讨论,分享您的自定义评估

4.2 下一步行动

  1. 扩展算术评估,添加更复杂的数学问题类型
  2. 实现自定义指标,如计算错误类型分布
  3. 构建领域特定评估,如代码生成、医疗问答等
  4. 贡献您的评估到Evals社区,帮助改进LLM评估标准

通过不断构建和改进评估任务,您不仅能更好地理解LLM的能力边界,还能为AI模型的负责任发展做出贡献。立即开始创建您的第一个自定义评估,探索LLM的真实能力!

【免费下载链接】evals Evals is a framework for evaluating LLMs and LLM systems, and an open-source registry of benchmarks. 【免费下载链接】evals 项目地址: https://gitcode.com/gh_mirrors/ev/evals

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐