Welcome to MMEval’s documentation!¶
介绍¶
MMEval 是一个机器学习算法评测库,提供高效准确的分布式评测以及多种机器学习框架后端支持,具有以下特点:
提供丰富的计算机视觉各细分方向评测指标(自然语言处理方向的评测指标正在支持中)
支持多种分布式通信库,实现高效准确的分布式评测。
支持多种机器学习框架,根据输入自动分发对应实现。
安装与使用¶
安装¶
MMEval 依赖 Python 3.6+,可以通过 pip 来安装 MMEval。安装 MMEval 的过程中会安装一些 MMEval 运行时的依赖库:
pip install mmeval
如果要安装 MMEval 中所有评测指标都需要的依赖,可以通过以下命令安装:
pip install 'mmeval[all]'
使用¶
MMEval 中的评测指标提供两种使用方式,以 Accuracy 为例:
from mmeval import Accuracy
import numpy as np
accuracy = Accuracy()
第一种是直接调用实例化的 Accuracy 对象,计算评测指标:
labels = np.asarray([0, 1, 2, 3])
preds = np.asarray([0, 2, 1, 3])
accuracy(preds, labels)
# {'top1': 0.5}
第二种是累积多个批次的数据后,计算评测指标:
for i in range(10):
labels = np.random.randint(0, 4, size=(100, ))
predicts = np.random.randint(0, 4, size=(100, ))
accuracy.add(predicts, labels)
accuracy.compute()
# {'top1': ...}
支持矩阵¶
支持的分布式通信后端¶
MPI4Py | torch.distributed | Horovod | paddle.distributed | oneflow.comm |
---|---|---|---|---|
MPI4PyDist | TorchCPUDist TorchCUDADist |
TFHorovodDist | PaddleDist | OneFlowDist |
支持的评测指标及机器学习框架¶
注解
下表列出 MMEval 已实现的评测指标与对应的机器学习框架支持情况,打勾表示能够直接接收对应框架的数据类型(如 Tensor)进行计算。
注解
MMEval 在 PyTorch 1.6+,TensorFlow 2.4+, Paddle 2.2+ 和 OneFlow 0.8+ 测试通过。
评测指标 | numpy.ndarray | torch.Tensor | tensorflow.Tensor | paddle.Tensor | oneflow.Tensor |
---|---|---|---|---|---|
Accuracy | ✔ | ✔ | ✔ | ✔ | ✔ |
SingleLabelMetric | ✔ | ✔ | ✔ | ||
MultiLabelMetric | ✔ | ✔ | ✔ | ||
AveragePrecision | ✔ | ✔ | ✔ | ||
MeanIoU | ✔ | ✔ | ✔ | ✔ | ✔ |
VOCMeanAP | ✔ | ||||
OIDMeanAP | ✔ | ||||
COCODetection | ✔ | ||||
ProposalRecall | ✔ | ||||
F1Score | ✔ | ✔ | ✔ | ||
HmeanIoU | ✔ | ||||
PCKAccuracy | ✔ | ||||
MpiiPCKAccuracy | ✔ | ||||
JhmdbPCKAccuracy | ✔ | ||||
EndPointError | ✔ | ✔ | ✔ | ||
AVAMeanAP | ✔ | ||||
StructuralSimilarity | ✔ | ||||
SignalNoiseRatio | ✔ | ||||
PeakSignalNoiseRatio | ✔ | ||||
MeanAbsoluteError | ✔ | ||||
MeanSquaredError | ✔ |
自定义评测指标¶
在 MMEval 中实现一个自定义评测指标,需要继承 BaseMetric 并且实现 add
和 compute_metric
方法。
在评测过程中,评测指标需要在调用 add
后更新 _results
以存储中间结果。在最后进行指标计算的时候,将会对 _results
进行进程同步后调用 compute_metric
进行指标的计算。
以实现 Accuracy
指标为例:
import numpy as np
from mmeval.core import BaseMetric
class Accuracy(BaseMetric):
def add(self, predictions, labels):
self._results.append((predictions, labels))
def compute_metric(self, results):
predictions = np.concatenate(
[res[0] for res in results])
labels = np.concatenate(
[res[1] for res in results])
correct = (predictions == labels)
accuracy = sum(correct) / len(predictions)
return {'accuracy': accuracy}
使用 Accuracy
:
# stateless call
accuracy = Accuracy()
metric_results = accuracy(predictions=[1, 2, 3, 4], labels=[1, 2, 3, 1])
print(metric_results)
# {'accuracy': 0.75}
# Accumulate batch
for i in range(10):
predicts = np.random.randint(0, 4, size=(10,))
labels = predicts = np.random.randint(0, 4, size=(10,))
accuracy.add(predicts, labels)
metric_results = accuracy.compute()
accuracy.reset() # clear the intermediate results
使用分布式评测¶
分布式评测一般采用数据并行的策略,每个进程执行相同的程序来处理不同的数据。
MMEval 中已支持的分布式通信后端可以通过 list_all_backends 查看:
import mmeval
print(mmeval.core.dist.list_all_backends())
# ['non_dist', 'mpi4py', 'tf_horovod', 'torch_cpu', 'torch_cuda', ...]
本节将以 CIFAR-10 数据集的评测为例,分别介绍如何使用 MMEval 结合 torch.distributed
和 MPI4Py
进行分布式评测,相关代码可以在 mmeval/examples/cifar10_dist_eval 中找到。
评测数据与模型准备¶
首先我们需要加载 CIFAR-10 测试数据,我们可以使用 Torchvison
提供的数据集类。
另外,为了能够在分布式评测中将数据集根据进程数量进行切分,我们需要引入 DistributedSampler
。
import torchvision as tv
from torch.utils.data import DataLoader, DistributedSampler
def get_eval_dataloader(rank=0, num_replicas=1):
dataset = tv.datasets.CIFAR10(
root='./', train=False, download=True,
transform=tv.transforms.ToTensor())
dist_sampler = DistributedSampler(
dataset, num_replicas=num_replicas, rank=rank)
data_loader = DataLoader(dataset, batch_size=1, sampler=dist_sampler)
return data_loader, len(dataset)
其次,我们需要准备待评测的模型,这里我们使用 Torchvision
中的 resnet18
。
import torch
import torchvision as tv
def get_model(pretrained_model_fpath=None):
model = tv.models.resnet18(num_classes=10)
if pretrained_model_fpath is not None:
model.load_state_dict(torch.load(pretrained_model_fpath))
return model.eval()
单进程评测¶
有了待评测的数据集与模型,就可以使用 mmeval.Accuracy 指标对模型预测结果进行评测,下面是一个单进程评测的示例:
import tqdm
import torch
from mmeval import Accuracy
eval_dataloader, total_num_samples = get_eval_dataloader()
model = get_model()
# 实例化 `Accuracy`,计算 top1 与 top3 准确率
accuracy = Accuracy(topk=(1, 3))
with torch.no_grad():
for images, labels in tqdm.tqdm(eval_dataloader):
predicted_score = model(images)
# 累计批次数据,中间结果将保存在 `accuracy._results` 中
accuracy.add(predictions=predicted_score, labels=labels)
# 调用 `accuracy.compute` 进行指标计算
print(accuracy.compute())
# 调用 `accuracy.reset` 清除保存在 `accuracy._results` 中的中间结果
accuracy.reset()
使用 torch.distributed 进行分布式评测¶
在 MMEval
中为 torch.distributed
实现了两个分布式通信后端,分别是 TorchCPUDist 和 TorchCUDADist。
为 MMEval
设置分布式通信后端的方式有两种:
from mmeval.core import set_default_dist_backend
from mmeval import Accuracy
# 1. 设置全局默认分布式通信后端
set_default_dist_backend('torch_cpu')
# 2. 初始化评测指标时候通过 `dist_backend` 传参
accuracy = Accuracy(dist_backend='torch_cpu')
结合上述单进程评测的代码,再加入分布式环境启动以及初始化即可实现分布式评测。
import tqdm
import torch
from mmeval import Accuracy
def eval_fn(rank, process_num):
# 分布式环境初始化
torch.distributed.init_process_group(
backend='gloo',
init_method=f'tcp://127.0.0.1:2345',
world_size=process_num,
rank=rank)
eval_dataloader, total_num_samples = get_eval_dataloader(rank, process_num)
model = get_model()
# 实例化 Accuracy 并设置分布式通信后端
accuracy = Accuracy(topk=(1, 3), dist_backend='torch_cpu')
with torch.no_grad():
for images, labels in tqdm.tqdm(eval_dataloader, disable=(rank!=0)):
predicted_score = model(images)
accuracy.add(predictions=predicted_score, labels=labels)
# 通过 size 指定数据集样本数量,以便去除 DistributedSampler 补齐的重复样本。
print(accuracy.compute(size=total_num_samples))
accuracy.reset()
if __name__ == "__main__":
# 分布式进程数量
process_num = 3
# 采用 spawn 的方式启动分布式
torch.multiprocessing.spawn(
eval_fn, nprocs=process_num, args=(process_num, ))
使用 MPI4Py 进行分布式评测¶
MMEval
将分布式通信功能抽象解耦了,因此虽然上述例子使用的是 PyTorch 模型和数据加载,我们仍然可以使用除 torch.distributed
以外的分布式通信后端来实现分布式评测,下面将展示如何使用 MPI4Py
作为分布式通信后端来进行分布式评测。
首先需要安装 MPI4Py
以及 openmpi
,建议使用 conda
进行安装:
conda install openmpi
conda install mpi4py
然后将上述代码修改为使用 MPI4Py
做为分布式通信后端:
# cifar10_eval_mpi4py.py
import tqdm
from mpi4py import MPI
import torch
from mmeval import Accuracy
def eval_fn(rank, process_num):
eval_dataloader, total_num_samples = get_eval_dataloader(rank, process_num)
model = get_model()
accuracy = Accuracy(topk=(1, 3), dist_backend='mpi4py')
with torch.no_grad():
for images, labels in tqdm.tqdm(eval_dataloader, disable=(rank!=0)):
predicted_score = model(images)
accuracy.add(predictions=predicted_score, labels=labels)
print(accuracy.compute(size=total_num_samples))
accuracy.reset()
if __name__ == "__main__":
comm = MPI.COMM_WORLD
eval_fn(comm.Get_rank(), comm.Get_size())
使用 mpirun
作为分布式评测启动方式:
# 使用 mpirun 启动 3 个进程
mpirun -np 3 python3 cifar10_eval_mpi4py.py
MMCls¶
MMEval 中的 BaseMetric 参照了 mmengine.evaluator 模块的设计,在此基础上引入了分布式通信后端的组件,以满足多样的分布式通信库需求。
因此 MMEval 天然的支持基于 OpenMMLab 2.0 算法库的评测,在 OpenMMLab 2.0 算法库中使用 MMEval 的评测指标无需多做修改。
以在 MMCls 中使用 mmeval.Accuracy 为例,只需要在 config 中配置好使用的 Metric 为 Accuracy 即可:
val_evaluator = dict(type='Accuracy', topk=(1, ))
test_evaluator = val_evaluator
MMEval 对 OpenMMLab 2.0 算法库评测的支持正在逐步完善中,已支持的评测指标可以在支持矩阵 中查看。
TensorPack¶
TensorPack 是一个基于 TensorFlow 的深度学习训练库,具有高效与灵活的特点。
在 TensorPack 代码仓库中,提供了许多经典模型与任务的示例,本小节展示如何在 TensorPack-FasterRCNN 中使用 mmeval.COCODetection 进行评测,相关代码可以在 mmeval/examples/tensorpack 中找到。
首先需要安装 TensorFlow 与 TensorPack,然后按照 TensorPack-FasterRCNN 示例中的准备步骤,安装依赖和准备 COCO 数据集,以及下载需要评测的预训练模型权重。
TensorPack-FasterRCNN 自带了评测功能,可以通过以下命令执行评测:
./predict.py --evaluate output.json --load /path/to Trained-Model-Checkpoint --config SAME-AS-TRAINING
MMEval 为 TensorPack-FasterRCNN 提供了适配 mmeval.COCODetection 的评测脚本,需要将该脚本放至 TensorPack-FasterRCNN 示例目录下,然后通过以下命令执行评测:
# 单卡评测
python tensorpack_mmeval.py --load <model_path> --config SAME-AS-TRAINING
# 支持基于 MPI4Py 的分布式评测,通过 mpirun 启动多卡评测
mpirun -np 8 python tensorpack_mmeval.py --load <model_path> --config SAME-AS-TRAINING
我们在 COCO-MaskRCNN-R50C41x 配置上测试了该评测脚本,与 TensorPack-FasterRCNN 报告的评测结果一致。
Model | mAP (box) | mAP (mask) | Configurations |
---|---|---|---|
COCO-MaskRCNN-R50C41x | 36.2 | 31.8 | MODE_FPN=False |
PaddleSeg¶
PaddleSeg 是一个基于 Paddle 的语义分割算法库,支持许多和语义分割相关的下游任务。
本小节展示如何在 PaddleSeg 中使用 mmeval.MeanIoU 进行评测,相关代码可以在 mmeval/examples/paddleseg 中找到。
首先需要安装 Paddle 与 PaddleSeg,可以参照 PaddleSeg 中的安装文档进行。另外需要下载待评测的预训练模型,以及根据配置文件准备数据集。
PaddleSeg 算法库中提供了进行模型评测的脚本,可以通过以下命令对模型进行评测:
python val.py --config <config_path> --model_path <model_path>
需要注意,PaddleSeg 算法仓库中的 val.py
评测脚本只支持单卡评测,尚未支持多卡评测。
MMEval 为 PaddleSeg 提供了适配 mmeval.MeanIoU 的评测脚本,可以通过以下命令执行评测:
# 单卡评测
python ppseg_mmeval.py --config <config_path> --model_path <model_path>
# 单机多卡评测
python ppseg_mmeval.py --config <config_path> --model_path <model_path> --launcher paddle --num_process <num_gpus>
我们在 fastfcn_resnet50_os8_ade20k_480x480_120k 配置上测试了该评测脚本,与 PaddleSeg 中的 val.py
得到的评测结果一致。
Config | Weights | mIoU | aAcc | Kappa | mDice |
---|---|---|---|---|---|
fastfcn_resnet50_os8_ade20k_480x480_120k | model.pdparams | 0.4373 | 0.8074 | 0.7928 | 0.5772 |
BaseMetric 设计¶
在评测过程中,通常会以数据并行的形式,在每张卡上推理部分数据集的结果,以加快评测速度。
而在每个数据子集上计算得到的评测结果,通常不能通过简单的求平均来与整个数据集的评测结果进行等价。因此通常的做法是在分布式评测过程中,将每张卡得到的推理结果或者指标计算中间结果保存下来,在所有进程中进行 all-gather 操作,最后再计算整个评测数据集的指标结果。
上述操作在 MMEval 中由 BaseMetric 来完成,其接口设计如下图所示:
其中 add
与 compute_metric
方法为需要用户继承实现的接口,具体可以参考自定义评测指标。
通过 BaseMetric 接口可以看出,BaseMetric 主要功能是提供分布式评测,其基本流程为:
用户调用
add
方法,将推理结果或者指标计算中间结果保存在BaseMetric._results
列表中。用户调用
compute
方法,BaseMetric
将_results
列表中的数据进行进程间同步并调用用户定义的compute_metric
方法进行指标的计算。
除此之外,BaseMetric 还考虑到数据并行过程中,为了保证所有进程中的数据样本数量一致,部分进程会有补齐重复数据样本的情况,比如 PyTorch 中的 DistributedSampler
,这会影响到指标计算的正确性。
为了应对这个问题,BaseMetric.compute 可以接收一个 size
参数,表示整个评测数据集的真实样本数量,在 _results
进程同步之后,调用 compute_metric
方法之前,根据 dist_collect_mode
去除用来补齐的重复样本,以实现正确的指标计算。
注解
为了能够在分布式评测时候将补齐的重复样本删除掉,存储在 _results
列表的中间值需要和评测数据集样本是一一对应的关系。
分布式通信后端¶
MMEval 在分布式评测过程中所需的分布式通信需求,主要有以下两个:
将各个进程中保存的评测指标计算中间结果 all-gather
将 rank 0 进程计算得到的指标结果 broadcast 给所有进程
为了能够灵活的支持多种分布式通信库,MMEval 将上述分布式通信需求抽象定义了一个分布式通信接口 BaseDistBackend:
实现一个分布式通信后端,需要继承 BaseDistBackend 并且实现上述接口,其中:
is_initialized,标识当前是否已经完成分布式通信环境的初始化。
rank,当前进程所在进程组的序号。
world_size,进程数量。
all_gather_object,对任意可以被 Pickle 序列化的 Python 对象进行 all_tather 操作。
broadcast_object,对任意可以被 Pickle 序列化的 Python 对象进行广播操作。
以实现 MPI4PyDist 为例:
from mpi4py import MPI
class MPI4PyDist(BaseDistBackend):
"""A distributed communication backend for mpi4py."""
@property
def is_initialized(self) -> bool:
"""Returns True if the distributed environment has been initialized."""
return 'OMPI_COMM_WORLD_SIZE' in os.environ
@property
def rank(self) -> int:
"""Returns the rank index of the current process group."""
comm = MPI.COMM_WORLD
return comm.Get_rank()
@property
def world_size(self) -> int:
"""Returns the world size of the current process group."""
comm = MPI.COMM_WORLD
return comm.Get_size()
def all_gather_object(self, obj: Any) -> List[Any]:
"""All gather the given object from the current process group and
returns a list consisting gathered object of each process."""
comm = MPI.COMM_WORLD
return comm.allgather(obj)
def broadcast_object(self, obj: Any, src: int = 0) -> Any:
"""Broadcast the given object from source process to the current
process group."""
comm = MPI.COMM_WORLD
return comm.bcast(obj, root=src)
MMEval 中已经预置实现了一些分布式通信后端,具体可以在支持矩阵中查看。
基于参数类型注释的多分派¶
MMEval 希望能够支持多种机器学习框架,一个最为简单的方案是让所有评测指标的计算都支持 NumPy 即可。
这样做可以实现大部分评测需求,因为所有机器学习框架的 Tensor 数据类型都可以转为 numpy.ndarray。
但是在某些情况下可能会存在一些问题:
NumPy 有一些常用算子尚未实现,如 topk,会影响评测指标的计算速度。
大量的 Tensor 从 CUDA 设备搬运到 CPU 内存会比较耗时。
另外,如果希望评测指标的计算过程是可导的,那么就需要用各自机器学习框架的 Tensor 数据类型进行计算。
为了应对上述问题,MMEval 的评测指标提供了一些特定机器学习框架的指标计算实现,具体可以在 支持矩阵 中查看。
同时,为了应对不同指标计算方式的分发问题,MMEval 采用了基于类型注释的动态多分派机制,可以根据输入的数据类型,动态的选择不同的计算方式。
一个基于类型注释的多分派简单示例如下:
from mmeval.core import dispatch
@dispatch
def compute(x: int, y: int):
print('this is int')
@dispatch
def compute(x: str, y: str):
print('this is str')
compute(1, 1)
# this is int
compute('1', '1')
# this is str
目前,我们使用 plum-dispatch 来实现 MMEval 中的分发机制,在 plum-dispatch 基础上,做了一些速度上的优化,并且扩展支持了 typing.ForwardRef
。
警告
受限于 Python 动态类型的特性,在运行时确定一个变量的具体类型可能会比较耗时,尤其是碰到一些大的嵌套结构数据。因此基于类型注释的动态多分派机制可能会存在一些性能问题,更多信息可以参考:wesselb/plum/issues/53
mmeval.core¶
mmeval.core
base_metric¶
Base class for metric. |
mmeval.core.dist_backends¶
mmeval.core.dist_backends
dist_backends¶
The base backend of distributed communication used by mmeval Metric. |
|
A base backend of Tensor base distributed communication like PyTorch. |
|
A dummy distributed communication for non-distributed environment. |
|
A distributed communication backend for mpi4py. |
|
A cpu distributed communication backend for torch.distributed. |
|
A cuda distributed communication backend for torch.distributed. |
|
A distributed communication backend for horovod.tensorflow. |
|
A distributed communication backend for paddle.distributed. |
|
A distributed communication backend for oneflow. |
mmeval.fileio¶
mmeval.fileio
File Backend¶
Abstract class of storage backends. |
|
Raw local storage backend. |
|
HTTP and HTTPS storage bachend. |
|
Lmdb storage backend. |
|
Memcached storage backend. |
|
Petrel storage backend (for internal usage). |
Register a backend. |
File Handler¶
A base class for file handler. |
|
A Json handler that parse json data from file object. |
|
A Pickle handler that parse pickle data from file object. |
|
A Yaml handler that parse yaml data from file object. |
A decorator that register a handler for some file extensions. |
File IO¶
Load data from json/yaml/pickle files. |
|
Check whether a file path exists. |
|
Read bytes from a given |
|
Return a file backend based on the prefix of uri or backend_args. |
|
Download data from |
|
Read text from a given |
|
Check whether a file path is a directory. |
|
Check whether a file path is a file. |
|
Concatenate all file paths. |
|
Scan a directory to find the interested directories or files in arbitrary order. |
Parse File¶
Load a text file and parse the content as a dict. |
|
Load a text file and parse the content as a list of strings. |
mmeval.metrics¶
mmeval.metrics
Metrics¶
Top-k accuracy evaluation metric. |
|
alias of |
|
alias of |
|
Calculate the average precision with respect of classes. |
|
MeanIoU evaluation metric. |
|
COCO object detection task evaluation metric. |
|
Proposals recall evaluation metric. |
|
Pascal VOC evaluation metric. |
|
Open Images Dataset detection evaluation metric. |
|
Compute F1 scores. |
|
HmeanIoU metric. |
|
EndPointError evaluation metric. |
|
PCK accuracy evaluation metric, which is widely used in pose estimation. |
|
PCKh accuracy evaluation metric for MPII dataset. |
|
PCK accuracy evaluation metric for Jhmdb dataset. |
|
AVA evaluation metric. |
|
Calculate StructuralSimilarity (structural similarity). |
|
Signal-to-Noise Ratio. |
|
Peak Signal-to-Noise Ratio. |
|
Mean Absolute Error metric for image. |
|
Mean Squared Error metric for image. |
|
Bilingual Evaluation Understudy metric. |
|
Sum of Absolute Differences metric for image. |
|
Gradient error for evaluating alpha matte prediction. |
|
Mean Squared Error metric for image matting. |
|
Connectivity error for evaluating alpha matte prediction. |
|
DOTA evaluation metric. |
|
Calculate Rouge Score used for automatic summarization. |
|
Calculate Natural Image Quality Evaluator(NIQE) metric. |
|
Perplexity measures how well a language model predicts a text sample. |
|
Calculate the char level recall & precision. |
|
EPE evaluation metric. |
|
AUC evaluation metric. |
|
NME evaluation metric. |
|
Calculate the word level accuracy. |