快速浏览
🤗 Evaluate 提供对各种评估工具的访问。它涵盖了文本、计算机视觉、音频等各种模态,以及评估模型或数据集的工具。这些工具分为三个类别。
评估类型
典型的机器学习管道有不同的方面可以进行评估,对于每个方面,🤗 Evaluate 都提供了一个工具
- 指标:指标用于评估模型的性能,通常包括模型的预测以及一些真实标签。您可以在 evaluate-metric 中找到所有集成的指标。
- 比较:比较用于比较两个模型。例如,可以通过比较它们的预测与真实标签,并计算它们的吻合度来实现。您可以在 evaluate-comparison 中找到所有集成的比较。
- 测量:数据集与训练它的模型同等重要。使用测量,可以调查数据集的属性。您可以在 evaluate-measurement 中找到所有集成的测量。
这些评估模块中的每一个都作为空间存在于 Hugging Face Hub 上。它们附带一个交互式小部件和一个文档卡片,记录其使用和限制。例如 accuracy
每个指标、比较和测量都是一个单独的 Python 模块,但为了使用它们中的任何一个,只有一个入口点:evaluate.load()!
Load
任何指标、比较或测量都使用 evaluate.load
函数加载
>>> import evaluate
>>> accuracy = evaluate.load("accuracy")
如果您想确保加载正确的评估类型(尤其是在存在名称冲突的情况下),可以明确传递类型
>>> word_length = evaluate.load("word_length", module_type="measurement")
社区模块
除了 🤗 Evaluate 中实现的模块外,您还可以通过指定指标实现的仓库 ID 来加载任何社区模块
>>> element_count = evaluate.load("lvwerra/element_count", module_type="measurement")
有关上传自定义指标的信息,请参阅 创建和共享指南。
列出可用模块
使用 list_evaluation_modules(),您可以检查 Hub 上有哪些模块可用。您也可以过滤特定模块,如果您需要,可以跳过社区指标。您还可以看到其他信息,例如点赞数
>>> evaluate.list_evaluation_modules(
... module_type="comparison",
... include_community=False,
... with_details=True)
[{'name': 'mcnemar', 'type': 'comparison', 'community': False, 'likes': 1},
{'name': 'exact_match', 'type': 'comparison', 'community': False, 'likes': 0}]
模块属性
所有评估模块都带有一系列有用的属性,这些属性有助于使用存储在 EvaluationModuleInfo 对象中的模块。
属性 | 描述 |
---|---|
description |
评估模块的简短描述。 |
citation |
可用的情况下,用于引用的 BibTex 字符串。 |
features |
一个 Features 对象,定义输入格式。 |
inputs_description |
这等效于模块的文档字符串。 |
homepage |
模块的主页。 |
license |
模块的许可证。 |
codebase_urls |
指向模块代码的链接。 |
reference_urls |
其他参考 URL。 |
让我们看几个例子。首先,让我们看看 description
属性在准确度指标中的使用。
>>> accuracy = evaluate.load("accuracy")
>>> accuracy.description
Accuracy is the proportion of correct predictions among the total number of cases processed. It can be computed with:
Accuracy = (TP + TN) / (TP + TN + FP + FN)
Where:
TP: True positive
TN: True negative
FP: False positive
FN: False negative
您可以看到它描述了指标在理论上的工作原理。如果您在工作中使用此指标,尤其是如果您想在学术出版物中引用它,您需要正确地引用它。为此,您可以查看 citation
属性。
>>> accuracy.citation
@article{scikit-learn,
title={Scikit-learn: Machine Learning in {P}ython},
author={Pedregosa, F. and Varoquaux, G. and Gramfort, A. and Michel, V.
and Thirion, B. and Grisel, O. and Blondel, M. and Prettenhofer, P.
and Weiss, R. and Dubourg, V. and Vanderplas, J. and Passos, A. and
Cournapeau, D. and Brucher, M. and Perrot, M. and Duchesnay, E.},
journal={Journal of Machine Learning Research},
volume={12},
pages={2825--2830},
year={2011}
}
在我们能够将指标或其他评估模块应用于用例之前,我们需要知道指标的输入格式是什么。
>>> accuracy.features
{
'predictions': Value(dtype='int32', id=None),
'references': Value(dtype='int32', id=None)
}
请注意,特征始终描述单个输入元素的类型。通常我们会添加元素列表,因此您始终可以认为 features
中的类型周围有一个列表。Evaluate 接受各种输入格式(Python 列表、NumPy 数组、PyTorch 张量等),并将它们转换为适合存储和计算的格式。
计算
现在我们知道了评估模块的工作原理以及应该输入什么,我们想要实际使用它!在计算实际分数时,有两种主要方法可以做到。
- 一次性
- 增量式
在增量式方法中,必要的输入使用 EvaluationModule.add() 或 EvaluationModule.add_batch() 添加到模块中,并在最后使用 EvaluationModule.compute() 计算分数。或者,可以一次将所有输入传递给 compute()
。让我们看看这两种方法。
如何计算
计算评估模块分数的最简单方法是直接使用必要的输入调用 compute()
。只需将 features
中显示的输入传递给 compute()
方法。
>>> accuracy.compute(references=[0,1,0,1], predictions=[1,0,0,1])
{'accuracy': 0.5}
评估模块以字典形式返回结果。但是,在某些情况下,您会迭代地或以分布式方式构建预测,在这种情况下,add()
或 add_batch()
很有用。
计算单个指标或一批指标
在许多评估管道中,您会迭代地构建预测,例如在 for 循环中。在这种情况下,您可以将预测存储在一个列表中,并在最后将它们传递给 compute()
。使用 add()
和 add_batch()
,您可以绕过单独存储预测的步骤。如果您只是在一次创建单个预测,您可以使用 add()
。
>>> for ref, pred in zip([0,1,0,1], [1,0,0,1]):
>>> accuracy.add(references=ref, predictions=pred)
>>> accuracy.compute()
{'accuracy': 0.5}
收集所有预测后,您可以调用 compute()
来根据所有存储的值计算分数。当以批次获取预测和参考时,您可以使用 add_batch()
,它会添加一批元素以供以后处理。其余部分与 add()
的工作方式相同。
>>> for refs, preds in zip([[0,1],[0,1]], [[1,0],[0,1]]):
>>> accuracy.add_batch(references=refs, predictions=preds)
>>> accuracy.compute()
{'accuracy': 0.5}
当您需要以批次从您的模型中获取预测时,这尤其有用。
>>> for model_inputs, gold_standards in evaluation_dataset:
>>> predictions = model(model_inputs)
>>> metric.add_batch(references=gold_standards, predictions=predictions)
>>> metric.compute()
分布式评估
在分布式环境中计算指标可能很棘手。指标评估在不同的数据集子集上,在单独的 Python 进程或节点中执行。通常,当指标分数是可加的 (f(AuB) = f(A) + f(B)
) 时,您可以使用分布式归约操作来收集数据集每个子集的分数。但是,当指标不可加 (f(AuB) ≠ f(A) + f(B)
) 时,情况就不那么简单了。例如,您不能将每个数据集子集的 F1 分数之和作为您的 **最终指标**。
克服此问题的常见方法是退回到单进程评估。指标在单个 GPU 上进行评估,这会变得效率低下。
🤗 Evaluate 通过仅在第一个节点上计算最终指标来解决此问题。预测和参考分别为每个节点计算和提供。这些暂时存储在 Apache Arrow 表中,避免混乱 GPU 或 CPU 内存。当您准备好 compute()
最终指标时,第一个节点能够访问所有其他节点上存储的预测和参考。一旦它收集了所有预测和参考,compute()
将执行最终指标评估。
此解决方案允许 🤗 Evaluate 执行分布式预测,这对于分布式环境中的评估速度很重要。同时,您也可以使用复杂的不可加指标,而不会浪费宝贵的 GPU 或 CPU 内存。
组合多个评估
通常,人们不仅想要评估单个指标,还想要评估一系列不同的指标,这些指标反映了模型的不同方面。例如,对于分类,除了准确率之外,通常还需要计算 F1 分数、召回率和精确率,以更好地了解模型性能。当然,您可以加载一堆指标并依次调用它们。但是,更便捷的方法是使用 combine() 函数将它们捆绑在一起。
>>> clf_metrics = evaluate.combine(["accuracy", "f1", "precision", "recall"])
combine
函数接受指标名称列表和实例化的模块。compute
调用会计算每个指标。
>>> clf_metrics.compute(predictions=[0, 1, 0], references=[0, 1, 1])
{
'accuracy': 0.667,
'f1': 0.667,
'precision': 1.0,
'recall': 0.5
}
保存并推送到 Hub
保存和共享评估结果是重要的一步。我们提供 evaluate.save() 函数来轻松保存指标结果。您可以传递特定的文件名或目录。在后一种情况下,结果将保存在使用自动创建的文件名创建的文件中。除了目录或文件名之外,该函数还接受任何键值对作为输入,并将它们存储在 JSON 文件中。
>>> result = accuracy.compute(references=[0,1,0,1], predictions=[1,0,0,1])
>>> hyperparams = {"model": "bert-base-uncased"}
>>> evaluate.save("./results/"experiment="run 42", **result, **hyperparams)
PosixPath('results/result-2022_05_30-22_09_11.json')
JSON 文件的内容如下所示。
{
"experiment": "run 42",
"accuracy": 0.5,
"model": "bert-base-uncased",
"_timestamp": "2022-05-30T22:09:11.959469",
"_git_commit_hash": "123456789abcdefghijkl",
"_evaluate_version": "0.1.0",
"_python_version": "3.9.12 (main, Mar 26 2022, 15:51:15) \n[Clang 13.1.6 (clang-1316.0.21.2)]",
"_interpreter_path": "/Users/leandro/git/evaluate/env/bin/python"
}
除了指定的字段之外,它还包含用于重现结果的有用系统信息。
除了在本地存储结果之外,您还应该在 Hub 上的模型存储库中报告结果。使用 evaluate.push_to_hub() 函数,您可以轻松地将评估结果报告给模型存储库。
evaluate.push_to_hub(
model_id="huggingface/gpt2-wikitext2", # model repository on hub
metric_value=0.5, # metric value
metric_type="bleu", # metric name, e.g. accuracy.name
metric_name="BLEU", # pretty name which is displayed
dataset_type="wikitext", # dataset name on the hub
dataset_name="WikiText", # pretty name
dataset_split="test", # dataset split used
task_type="text-generation", # task id, see https://github.com/huggingface/datasets/blob/master/src/datasets/utils/resources/tasks.json
task_name="Text Generation" # pretty name for task
)
评估器
The evaluate.evaluator() 提供自动化评估,只需要模型、数据集和指标,与 EvaluationModule
中需要模型预测的指标相比。因此,在给定指标的情况下,更容易在数据集上评估模型,因为推理是在内部处理的。为了实现这一点,它使用了 transformers
中的 pipeline 抽象。但是,只要它遵循 pipeline
接口,您也可以使用自己的框架。
要使用 evaluator
进行评估,让我们加载一个 transformers
pipeline(但您也可以为任何框架传递自己的自定义推理类,只要它遵循 pipeline 调用 API)和一个在 IMDb 上训练的模型,以及 IMDb 测试集和准确率指标。
from transformers import pipeline
from datasets import load_dataset
from evaluate import evaluator
import evaluate
pipe = pipeline("text-classification", model="lvwerra/distilbert-imdb", device=0)
data = load_dataset("imdb", split="test").shuffle().select(range(1000))
metric = evaluate.load("accuracy")
然后,您可以创建一个用于文本分类的评估器,并将这三个对象传递给 compute()
方法。使用标签映射,evaluate
提供了一种方法来将 pipeline 输出与数据集中标签列对齐。
>>> task_evaluator = evaluator("text-classification")
>>> results = task_evaluator.compute(model_or_pipeline=pipe, data=data, metric=metric,
... label_mapping={"NEGATIVE": 0, "POSITIVE": 1},)
>>> print(results)
{'accuracy': 0.934}
仅计算指标的值通常不足以了解模型是否比另一个模型表现得更好。通过自举,evaluate
计算置信区间和标准误差,这有助于估计分数的稳定性。
>>> results = eval.compute(model_or_pipeline=pipe, data=data, metric=metric,
... label_mapping={"NEGATIVE": 0, "POSITIVE": 1},
... strategy="bootstrap", n_resamples=200)
>>> print(results)
{'accuracy':
{
'confidence_interval': (0.906, 0.9406749892841922),
'standard_error': 0.00865213251082787,
'score': 0.923
}
}
评估器期望数据输入中包含 "text"
和 "label"
列。如果您的数据集不同,您可以使用关键字 input_column="text"
和 label_column="label"
来提供列。目前仅支持 "text-classification"
,未来将添加更多任务。
可视化
当比较多个模型时,有时仅通过查看模型的分数很难发现它们性能的差异。而且,通常没有一个最佳模型,而是存在一些权衡,例如,延迟和准确率之间存在权衡,因为较大的模型可能具有更好的性能,但速度也更慢。我们正在逐步添加不同的可视化方法,例如绘图,以使选择最适合用例的模型变得更容易。
例如,如果您有一个来自多个模型(作为字典)的结果列表,您可以将它们输入 radar_plot()
函数中。
import evaluate
from evaluate.visualization import radar_plot
>>> data = [
{"accuracy": 0.99, "precision": 0.8, "f1": 0.95, "latency_in_seconds": 33.6},
{"accuracy": 0.98, "precision": 0.87, "f1": 0.91, "latency_in_seconds": 11.2},
{"accuracy": 0.98, "precision": 0.78, "f1": 0.88, "latency_in_seconds": 87.6},
{"accuracy": 0.88, "precision": 0.78, "f1": 0.81, "latency_in_seconds": 101.6}
]
>>> model_names = ["Model 1", "Model 2", "Model 3", "Model 4"]
>>> plot = radar_plot(data=data, model_names=model_names)
>>> plot.show()
这使您能够直观地比较 4 个模型,并根据一个或多个指标选择最适合您的模型。
在任务套件上运行评估
在各种不同的任务上评估模型以了解其下游性能可能很有用。The EvaluationSuite 允许在任务集合上评估模型。任务可以构建为 (evaluator、数据集、指标) 元组,并传递给一个 EvaluationSuite,该 suite 存储在 Hugging Face Hub 上作为 Space,或本地存储为 Python 脚本。有关当前支持的任务列表,请参阅 evaluator 文档。
EvaluationSuite
脚本可以按如下方式定义,并支持用于数据预处理的 Python 代码。
import evaluate
from evaluate.evaluation_suite import SubTask
class Suite(evaluate.EvaluationSuite):
def __init__(self, name):
super().__init__(name)
self.suite = [
SubTask(
task_type="text-classification",
data="imdb",
split="test[:1]",
args_for_task={
"metric": "accuracy",
"input_column": "text",
"label_column": "label",
"label_mapping": {
"LABEL_0": 0.0,
"LABEL_1": 1.0
}
}
),
SubTask(
task_type="text-classification",
data="sst2",
split="test[:1]",
args_for_task={
"metric": "accuracy",
"input_column": "sentence",
"label_column": "label",
"label_mapping": {
"LABEL_0": 0.0,
"LABEL_1": 1.0
}
}
)
]
可以通过加载 EvaluationSuite
并使用模型或 pipeline 调用 run()
方法来运行评估。
>>> from evaluate import EvaluationSuite
>>> suite = EvaluationSuite.load('mathemakitten/sentiment-evaluation-suite')
>>> results = suite.run("huggingface/prunebert-base-uncased-6-finepruned-w-distil-mnli")
准确率 | 总时间(秒) | 每秒样本数 | 延迟(秒) | 任务名称 |
---|---|---|---|---|
0.3 | 4.62804 | 2.16074 | 0.462804 | imdb |
0 | 0.686388 | 14.569 | 0.0686388 | sst2 |