Evals用户手册:轻松构建自定义评估任务
Evals是一个用于评估大型语言模型(LLM)和LLM系统的开源框架,提供了丰富的基准测试工具和可扩展架构。本手册将指导您从零开始构建自定义评估任务,深入理解评估流程的每一个环节。### 1.1 核心组件与工作流程Evals框架采用模块化设计,主要包含以下核心组件:```mermaidflowchart TDA[数据集(JSONL)] -->|加载数据| B[评估类(Eva...
Evals用户手册:轻松构建自定义评估任务
1. Evals框架核心概念与架构
Evals是一个用于评估大型语言模型(LLM)和LLM系统的开源框架,提供了丰富的基准测试工具和可扩展架构。本手册将指导您从零开始构建自定义评估任务,深入理解评估流程的每一个环节。
1.1 核心组件与工作流程
Evals框架采用模块化设计,主要包含以下核心组件:
- 评估类(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-turbo、text-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_samples的batch_size参数启用批量评估 - 缓存机制: 对静态数据集启用缓存,避免重复加载
- 并行评估: 通过
num_workers参数启用多线程评估 - 资源控制: 设置合理的
max_tokens和timeout避免资源浪费
# 优化的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 下一步行动
- 扩展算术评估,添加更复杂的数学问题类型
- 实现自定义指标,如计算错误类型分布
- 构建领域特定评估,如代码生成、医疗问答等
- 贡献您的评估到Evals社区,帮助改进LLM评估标准
通过不断构建和改进评估任务,您不仅能更好地理解LLM的能力边界,还能为AI模型的负责任发展做出贡献。立即开始创建您的第一个自定义评估,探索LLM的真实能力!
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)