Shortcuts

Welcome to MMEval’s documentation!

介绍

MMEval 是一个机器学习算法评测库,提供高效准确的分布式评测以及多种机器学习框架后端支持,具有以下特点:

  • 提供丰富的计算机视觉各细分方向评测指标(自然语言处理方向的评测指标正在支持中)

  • 支持多种分布式通信库,实现高效准确的分布式评测。

  • 支持多种机器学习框架,根据输入自动分发对应实现。

mmeval-arch

安装与使用

安装

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 并且实现 addcompute_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.distributedMPI4Py 进行分布式评测,相关代码可以在 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 实现了两个分布式通信后端,分别是 TorchCPUDistTorchCUDADist

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 来完成,其接口设计如下图所示:

classDiagram class BaseMetric BaseMetric : +{BaseDistBackend} dist_comm BaseMetric : +str dist_collect_mode BaseMetric : +dict dataset_meta BaseMetric : #list _results BaseMetric : +reset() BaseMetric : +compute() BaseMetric : +{abstractmethod} add() BaseMetric : +{abstractmethod} compute_metric()

其中 addcompute_metric 方法为需要用户继承实现的接口,具体可以参考自定义评测指标

通过 BaseMetric 接口可以看出,BaseMetric 主要功能是提供分布式评测,其基本流程为:

  1. 用户调用 add 方法,将推理结果或者指标计算中间结果保存在 BaseMetric._results 列表中。

  2. 用户调用 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

classDiagram class BaseDistBackend BaseDistBackend : +bool is_initialized BaseDistBackend : +int rank BaseDistBackend : +int world_size BaseDistBackend : +all_gather_object() BaseDistBackend : +broadcast_object()

实现一个分布式通信后端,需要继承 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

BaseMetric

Base class for metric.

dist

list_all_backends

Returns a list of all distributed backend names.

set_default_dist_backend

Set the given distributed backend as the default distributed backend.

get_dist_backend

Returns distributed backend by the given distributed backend name.

dispatch

dispatch

A Dispatcher inherited from plum.Dispatcher that resolve typing.ForwardRef.

mmeval.core.dist_backends

mmeval.core.dist_backends

dist_backends

BaseDistBackend

The base backend of distributed communication used by mmeval Metric.

TensorBaseDistBackend

A base backend of Tensor base distributed communication like PyTorch.

NonDist

A dummy distributed communication for non-distributed environment.

MPI4PyDist

A distributed communication backend for mpi4py.

TorchCPUDist

A cpu distributed communication backend for torch.distributed.

TorchCUDADist

A cuda distributed communication backend for torch.distributed.

TFHorovodDist

A distributed communication backend for horovod.tensorflow.

PaddleDist

A distributed communication backend for paddle.distributed.

OneFlowDist

A distributed communication backend for oneflow.

mmeval.fileio

File Backend

BaseStorageBackend

Abstract class of storage backends.

LocalBackend

Raw local storage backend.

HTTPBackend

HTTP and HTTPS storage bachend.

LmdbBackend

Lmdb storage backend.

MemcachedBackend

Memcached storage backend.

PetrelBackend

Petrel storage backend (for internal usage).

register_backend

Register a backend.

File Handler

BaseFileHandler

A base class for file handler.

JsonHandler

A Json handler that parse json data from file object.

PickleHandler

A Pickle handler that parse pickle data from file object.

YamlHandler

A Yaml handler that parse yaml data from file object.

register_handler

A decorator that register a handler for some file extensions.

File IO

load

Load data from json/yaml/pickle files.

exists

Check whether a file path exists.

get

Read bytes from a given filepath with ‘rb’ mode.

get_file_backend

Return a file backend based on the prefix of uri or backend_args.

get_local_path

Download data from filepath and write the data to local path.

get_text

Read text from a given filepath with ‘r’ mode.

isdir

Check whether a file path is a directory.

isfile

Check whether a file path is a file.

join_path

Concatenate all file paths.

list_dir_or_file

Scan a directory to find the interested directories or files in arbitrary order.

Parse File

dict_from_file

Load a text file and parse the content as a dict.

list_from_file

Load a text file and parse the content as a list of strings.

mmeval.metrics

mmeval.metrics

Metrics

Accuracy

Top-k accuracy evaluation metric.

SingleLabelMetric

alias of mmeval.metrics.precision_recall_f1score.SingleLabelPrecisionRecallF1score

MultiLabelMetric

alias of mmeval.metrics.precision_recall_f1score.MultiLabelPrecisionRecallF1score

AveragePrecision

Calculate the average precision with respect of classes.

MeanIoU

MeanIoU evaluation metric.

COCODetection

COCO object detection task evaluation metric.

ProposalRecall

Proposals recall evaluation metric.

VOCMeanAP

Pascal VOC evaluation metric.

OIDMeanAP

Open Images Dataset detection evaluation metric.

F1Score

Compute F1 scores.

HmeanIoU

HmeanIoU metric.

EndPointError

EndPointError evaluation metric.

PCKAccuracy

PCK accuracy evaluation metric, which is widely used in pose estimation.

MpiiPCKAccuracy

PCKh accuracy evaluation metric for MPII dataset.

JhmdbPCKAccuracy

PCK accuracy evaluation metric for Jhmdb dataset.

AVAMeanAP

AVA evaluation metric.

StructuralSimilarity

Calculate StructuralSimilarity (structural similarity).

SignalNoiseRatio

Signal-to-Noise Ratio.

PeakSignalNoiseRatio

Peak Signal-to-Noise Ratio.

MeanAbsoluteError

Mean Absolute Error metric for image.

MeanSquaredError

Mean Squared Error metric for image.

BLEU

Bilingual Evaluation Understudy metric.

SumAbsoluteDifferences

Sum of Absolute Differences metric for image.

GradientError

Gradient error for evaluating alpha matte prediction.

MattingMeanSquaredError

Mean Squared Error metric for image matting.

ConnectivityError

Connectivity error for evaluating alpha matte prediction.

DOTAMeanAP

DOTA evaluation metric.

ROUGE

Calculate Rouge Score used for automatic summarization.

NaturalImageQualityEvaluator

Calculate Natural Image Quality Evaluator(NIQE) metric.

Perplexity

Perplexity measures how well a language model predicts a text sample.

CharRecallPrecision

Calculate the char level recall & precision.

KeypointEndPointError

EPE evaluation metric.

KeypointAUC

AUC evaluation metric.

KeypointNME

NME evaluation metric.

WordAccuracy

Calculate the word level accuracy.

mmeval.utils

mmeval.utils

misc

try_import

Try to import a module.

has_method

Check whether the object has a method.

is_seq_of

Check whether it is a sequence of some type.

is_list_of

Check whether it is a list of some type.

is_tuple_of

Check whether it is a tuple of some type.

is_filepath

Check if the given object is Path-like.

Changelog of v0.x

Indices and tables

Read the Docs v: stable
Versions
latest
stable
Downloads
pdf
html
epub
On Read the Docs
Project Home
Builds

Free document hosting provided by Read the Docs.