提供了评估各个数据集(COCO, VOC,cityspace)的测试脚本,也有一些对于其他项目整合的高层api
框架支持的操作
利用如下的命令去测试数据集:
# single-gpu testing
python tools/test.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] [--eval ${EVAL_METRICS}] [--show]
# multi-gpu testing
./tools/dist_test.sh ${CONFIG_FILE} ${CHECKPOINT_FILE} ${GPU_NUM} [--out ${RESULT_FILE}] [--eval ${EVAL_METRICS}]
可选择的参数有:
RESULT_FILE
:如果指定这个参数那么输出的结果将会保存在一个pkl文件中,如果不指定结果就不会保存EVAL_METRICS
: 这个参数依赖于数据集,对于COCO,proposal_fast
, proposal
, bbox
, segm
,对于VOC数据集,mAP
, recall
, 对于cityspace, 除了支持所有的COCOmetrics还有,cityscapes
--show
:如果指定这个参数,检测的结果就会显示在一个窗口中,这个设置只在单gpu测试时可用,同时确保GUI可用,否则可能会遇到这个错误 cannot connect to X server
在检测整个数据集时,不要指定--show
这个参数
实施举例:
假设你已经下载预训练权重到/checkpoint
python tools/test.py configs/faster_rcnn_r50_fpn_1x.py \
checkpoints/faster_rcnn_r50_fpn_1x_20181010-3d1b3351.pth \
--show
python tools/test.py configs/pascal_voc/faster_rcnn_r50_fpn_1x_voc.py \
checkpoints/SOME_CHECKPOINT.pth \
--eval mAP
./tools/dist_test.sh configs/mask_rcnn_r50_fpn_1x.py \
checkpoints/mask_rcnn_r50_fpn_1x_20181010-069fa190.pth \
8 --out results.pkl --eval bbox segm
./tools/dist_test.sh configs/mask_rcnn_r50_fpn_1x.py \
checkpoints/mask_rcnn_r50_fpn_1x_20181010-069fa190.pth \
8 --format_only --options "jsonfile_prefix=./mask_rcnn_test-dev_results"
最终会得到两个json文件:mask_rcnn_test-dev_results.bbox.json
and mask_rcnn_test-dev_results.segm.json
.
./tools/dist_test.sh configs/cityscapes/mask_rcnn_r50_fpn_1x_cityscapes.py \
checkpoints/mask_rcnn_r50_fpn_1x_cityscapes_20200227-afe51d5a.pth \
8 --format_only --options "outfile_prefix=./mask_rcnn_cityscapes_test_results"
框架提供了一个webcam demo展示这个检测结果
python demo/webcam_demo.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--device ${GPU_ID}] [--camera-id ${CAMERA-ID}] [--score-thr ${SCORE_THR}]
例子:
python demo/webcam_demo.py configs/faster_rcnn_r50_fpn_1x.py \
checkpoints/faster_rcnn_r50_fpn_1x_20181010-3d1b3351.pth
这是一个构建模型测试图片的例子
from mmdet.apis import init_detector, inference_detector, show_result
import mmcv
config_file = 'configs/faster_rcnn_r50_fpn_1x.py'
checkpoint_file = 'checkpoints/faster_rcnn_r50_fpn_1x_20181010-3d1b3351.pth'
# build the model from a config file and a checkpoint file
model = init_detector(config_file, checkpoint_file, device='cuda:0')
# test a single image and show the results
img = 'test.jpg' # or img = mmcv.imread(img), which will only load it once
result = inference_detector(model, img)
# visualize the results in a new window
show_result(img, result, model.CLASSES)
# or save the visualization results to image files
show_result(img, result, model.CLASSES, out_file='result.jpg')
# test a video and show the results
video = mmcv.VideoReader('video.mp4')
for frame in video:
result = inference_detector(model, frame)
show_result(frame, result, model.CLASSES, wait_time=1)
另外有一个ipython版本demo/inference_demo.ipynb.
异步接口允许不阻塞GPU绑定的推理代码上的CPU,并为单线程应用程序提供更好的CPU/GPU利用率。可以在不同的输入数据样本之间或某个推理管道的不同模型之间并发地进行推理。
利用tests/async_benchmark.py
来对比同步与一部接口的速度
import asyncio
import torch
from mmdet.apis import init_detector, async_inference_detector, show_result
from mmdet.utils.contextmanagers import concurrent
async def main():
config_file = 'configs/faster_rcnn_r50_fpn_1x.py'
checkpoint_file = 'checkpoints/faster_rcnn_r50_fpn_1x_20181010-3d1b3351.pth'
device = 'cuda:0'
model = init_detector(config_file, checkpoint=checkpoint_file, device=device)
# queue is used for concurrent inference of multiple images
streamqueue = asyncio.Queue()
# queue size defines concurrency level
streamqueue_size = 3
for _ in range(streamqueue_size):
streamqueue.put_nowait(torch.cuda.Stream(device=device))
# test a single image and show the results
img = 'test.jpg' # or img = mmcv.imread(img), which will only load it once
async with concurrent(streamqueue):
result = await async_inference_detector(model, img)
# visualize the results in a new window
show_result(img, result, model.CLASSES)
# or save the visualization results to image files
show_result(img, result, model.CLASSES, out_file='result.jpg')
asyncio.run(main())
mmdetection采用分布式训练与非分布式的训练
使用MMDistributedDataParallel
和 MMDataParallel
实现分布式训练
所有的输出(日志文件与权重文件)保存在由work_dir
指定的工作目录下
通常每1epoch就执行一次evaluation,可以通过修改间隔语句来进行保存。
evaluation = dict(interval=12) # This evaluate the model per 12 epoch.
*Important*: 在config文件中默认的学习率是8gpus,每张gpu2张图像,
如果在训练过程中采用不同的硬件配置,依据所用的batch_size,按照 Linear Scaling Rule进行设置
python tools/train.py ${CONFIG_FILE}
./tools/dist_train.sh ${CONFIG_FILE} ${GPU_NUM} [optional arguments]
--validate
(strongly recommended): 在训练过程中可以按照 this) 调整评估的间隔,默认每个epoch执行评估.--work_dir ${WORK_DIR}
: 重新指定工作目录。--resume_from ${CHECKPOINT_FILE}
: 重新制定重新训练读取文件的位置resume_from
和 load_from
的不同点:
resume_from
:不仅要从checkpoint文件中读取权重也需要得到特定的优化器状态和epoch数目, 用于程序运行过程中中断后继续训练
load_from
: 仅仅读取模型权重用于fine-tune
这一部分近期不会接触到,因此没有看这一部分的内容
如果在一台服务器上部署多个训练任务,需要设置不同的port,默认的port是29500.
如果使用dist_train.sh
进行训练,需要在命令行中设置port
CUDA_VISIBLE_DEVICES=0,1,2,3 PORT=29500 ./tools/dist_train.sh ${CONFIG_FILE} 4
CUDA_VISIBLE_DEVICES=4,5,6,7 PORT=29501 ./tools/dist_train.sh ${CONFIG_FILE} 4
框架在./tools
中提供了很多有用的工具
可以利用一个训练的log日志直接plot 损失曲线,首先pip install seaborn
安装所需的依赖。
python tools/analyze_logs.py plot_curve [--keys ${KEYS}] [--title ${TITLE}] [--legend ${LEGEND}] [--backend ${BACKEND}] [--style ${STYLE}] [--out ${OUT_FILE}]
一些例子
python tools/analyze_logs.py plot_curve log.json --keys loss_cls --legend loss_cls
python tools/analyze_logs.py plot_curve log.json --keys loss_cls loss_reg --out losses.pdf
python tools/analyze_logs.py plot_curve log1.json log2.json --keys bbox_mAP --legend run1 run2
python tools/analyze_logs.py cal_train_time ${CONFIG_FILE} [--include-outliers]
训练速度分析计算的大致输出如下表示:
-----Analyze train time of work_dirs/some_exp/20190611_192040.log.json-----
slowest epoch 11, average time is 1.2024
fastest epoch 1, average time is 1.1909
time std over epochs is 0.0028
average iter time: 1.1959 s/iter
框架提供了一个脚本用来计算FLOPs和parameters, 脚本文件来自于 flops-counter.pytorch
python tools/get_flops.py ${CONFIG_FILE} [--shape ${INPUT_SHAPE}]
会得到类似如下的结果:
==============================
Input shape: (3, 1280, 800)
Flops: 239.32 GMac
Params: 37.74 M
==============================
Note: 这个工具依然在实验过程中,不能保证得到的数据正确,可以利用这个数据做简单的对比,但是在被用做论文中时需要多次检查
mmdet/utils/flops_counter.py
.在上传一个模型到AWS之前,你需要做如下的工作:
python tools/publish_model.py ${INPUT_FILENAME} ${OUTPUT_FILENAME}
E.g.,
python tools/publish_model.py work_dirs/faster_rcnn/latest.pth faster_rcnn_r50_fpn_1x_20190801.pth
最终的输出文件是faster_rcnn_r50_fpn_1x_20190801-{hash id}.pth
.
请参考ROBUSTNESS_BENCHMARKING.md.
框架提供了一个脚本文件去将模型转换为ONNX格式,转换完的模型可以利用Netron可视化.
python tools/pytorch2onnx.py ${CONFIG_FILE} ${CHECKPOINT_FILE} --out ${ONNX_FILE} [--shape ${INPUT_SHAPE}]
Note: 这个工具依然在实验过程中,自定义的操作不支持转换,为RoIPool
and RoIAlign
我们动态设置了use_torchvision=True
.
最简单的方式就是转换数据集为COCO格式
下边展示一个例子,利用一个5个类别的自定义数据集。假设是COCO格式
In mmdet/datasets/my_dataset.py
:
from .coco import CocoDataset
from .registry import DATASETS
@DATASETS.register_module
class MyDataset(CocoDataset):
CLASSES = ('a', 'b', 'c', 'd', 'e')
In mmdet/datasets/__init__.py
:
from .my_dataset import MyDataset
然后在config文件中,可以使用MyDataset。
如果不想要转换为COCO或者PASCAL格式,实际上,我们也定义了一个简单的标注格式并且所有的数据集都可以处理,所有现有的数据集
在线或离线处理以与之兼容。
数据集的标注是一个字典列表,每个字典对应了一个图像,有三个field,filename
, width
, height
,这三个参数在测试时使用,
并且,一个额外的field ann
用来训练, ann
也是一个field,它包含了至少两个field:bboxes
and labels
,这两者都是numpy.一些数据集可能还有crowd/difficult/ignored bboxes, 我们使用bboxes_ignore
and labels_ignore
来代替。
这是一个例子
[
{
'filename': 'a.jpg',
'width': 1280,
'height': 720,
'ann': {
'bboxes': (n, 4),
'labels': (n, ),
'bboxes_ignore': (k, 4),
'labels_ignore': (k, ) (optional field)
}
},
...
]
有两个方法去转换自定义数据集
在线转换
可以用继承自 CustomDataset
的数据集类来写一个新的数据集类, 并且重新写两个方案。load_annotations(self, ann_file)
and get_ann_info(self, idx)
,
就像 CocoDataset and VOCDataset.
离线转换
转换标注文件的格式为希望的格式,并且保存为pkl或者json文件的格式
在 mmdet/core/optimizer/copy_of_sgd.py
中定义了一个自定义优化器的例子CopyOfSGD
In mmdet/core/optimizer/my_optimizer.py
:
from .registry import OPTIMIZERS
from torch.optim import Optimizer
@OPTIMIZERS.register_module
class MyOptimizer(Optimizer):
In mmdet/core/optimizer/__init__.py
:
from .my_optimizer import MyOptimizer
Then you can use MyOptimizer
in optimizer
field of config files.
可以将模型组件归类为4个类型
下边我们显示如何开发新组件
mmdet/models/backbones/mobilenet.py
.import torch.nn as nn
from ..registry import BACKBONES
@BACKBONES.register_module
class MobileNet(nn.Module):
def __init__(self, arg1, arg2):
pass
def forward(self, x): # should return a tuple
pass
def init_weights(self, pretrained=None):
pass
mmdet/models/backbones/__init__.py
.from .mobilenet import MobileNet
model = dict(
...
backbone=dict(
type='MobileNet',
arg1=xxx,
arg2=xxx),
...
更多的信息将会展示在 TECHNICAL_DETAILS.md.