Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…multi_machine_dist
  • Loading branch information
zytx121 committed Mar 16, 2022
2 parents 68c70f8 + b735667 commit 75130ea
Show file tree
Hide file tree
Showing 19 changed files with 247 additions and 37 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ jobs:
- name: Build and install
run: pip install -e .
- name: Run unittests
run: coverage run --branch --source mmdet -m pytest tests -sv
run: coverage run --branch --source mmdet -m pytest tests
- name: Generate coverage report
run: |
coverage xml
Expand Down
2 changes: 1 addition & 1 deletion configs/yolox/yolox_s_8x8_300e_coco.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
_base_ = ['../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py']

img_scale = (640, 640)
img_scale = (640, 640) # height, width

# model settings
model = dict(
Expand Down
2 changes: 1 addition & 1 deletion configs/yolox/yolox_tiny_8x8_300e_coco.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
neck=dict(in_channels=[96, 192, 384], out_channels=96),
bbox_head=dict(in_channels=96, feat_channels=96))

img_scale = (640, 640)
img_scale = (640, 640) # height, width

train_pipeline = [
dict(type='Mosaic', img_scale=img_scale, pad_val=114.0),
Expand Down
83 changes: 83 additions & 0 deletions docs/zh_cn/tutorials/finetune.md
Original file line number Diff line number Diff line change
@@ -1 +1,84 @@
# 教程 7: 模型微调

在 COCO 数据集上预训练的检测器可以作为其他数据集(例如 CityScapes 和 KITTI 数据集)优质的预训练模型。
本教程将指导用户如何把 [ModelZoo](../model_zoo.md) 中提供的模型用于其他数据集中并使得当前所训练的模型获得更好性能。

以下是在新数据集中微调模型需要的两个步骤。

-[教程2:自定义数据集的方法](customize_dataset.md) 中的方法对新数据集添加支持中的方法对新数据集添加支持
- 按照本教程中所讨论方法,修改配置信息

接下来将会以 Cityscapes Dataset 上的微调过程作为例子,具体讲述用户需要在配置中修改的五个部分。

## 继承基础配置

为了减轻编写整个配置的负担并减少漏洞的数量, MMDetection V2.0 支持从多个现有配置中继承配置信息。微调 MaskRCNN 模型的时候,新的配置信息需要使用从 `_base_/models/mask_rcnn_r50_fpn.py`中继承的配置信息来构建模型的基本结构。当使用 Cityscapes 数据集时,新的配置信息可以简便地从`_base_/datasets/cityscapes_instance.py`中继承。对于训练过程的运行设置部分,新配置需要从 `_base_/default_runtime.py`中继承。这些配置文件`configs`的目录下,用户可以选择全部内容的重新编写而不是使用继承方法。

```python
_base_ = [
'../_base_/models/mask_rcnn_r50_fpn.py',
'../_base_/datasets/cityscapes_instance.py', '../_base_/default_runtime.py'
]
```


## Head 的修改
接下来新的配置还需要根据新数据集的类别数量对 Head 进行修改。只需要对 roi_head 中的 `num_classes`进行修改。修改后除了最后的预测模型的 Head 之外,预训练模型的权重的大部分都会被重新使用。

```python
model = dict(
pretrained=None,
roi_head=dict(
bbox_head=dict(
type='Shared2FCBBoxHead',
in_channels=256,
fc_out_channels=1024,
roi_feat_size=7,
num_classes=8,
bbox_coder=dict(
type='DeltaXYWHBBoxCoder',
target_means=[0., 0., 0., 0.],
target_stds=[0.1, 0.1, 0.2, 0.2]),
reg_class_agnostic=False,
loss_cls=dict(
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0)),
mask_head=dict(
type='FCNMaskHead',
num_convs=4,
in_channels=256,
conv_out_channels=256,
num_classes=8,
loss_mask=dict(
type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))))
```

## 数据集的修改
用户可能还需要准备数据集并编写有关数据集的配置。目前 MMDetection V2.0 的配置文件已经支持 VOC、WIDER FACE、COCO 和 Cityscapes Dataset 的数据集信息。

## 训练策略的修改
微调超参数与默认的训练策略不同。它通常需要更小的学习率和更少的训练回合。

```python
# 优化器
# batch size 为 8 时的 lr 配置
optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001)
optimizer_config = dict(grad_clip=None)
# 学习策略
lr_config = dict(
policy='step',
warmup='linear',
warmup_iters=500,
warmup_ratio=0.001,
step=[7])
# lr_config 中的 max_epochs 和 step 需要针对自定义数据集进行专门调整
runner = dict(max_epochs=8)
log_config = dict(interval=100)
```

## 使用预训练模型

如果要使用预训练模型时,可以在 `load_from` 中查阅新的配置信息,用户需要在训练开始之前下载好需要的模型权重,从而避免在训练过程中浪费了宝贵时间。
```python
load_from = 'https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco_bbox_mAP-0.408__segm_mAP-0.37_20200504_163245-42aa3d00.pth' # noqa
```
9 changes: 7 additions & 2 deletions mmdet/datasets/api_wrappers/panoptic_evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,8 @@ def pq_compute_multi_core(matched_annotations_list,
gt_folder,
pred_folder,
categories,
file_client=None):
file_client=None,
nproc=32):
"""Evaluate the metrics of Panoptic Segmentation with multithreading.
Same as the function with the same name in `panopticapi`.
Expand All @@ -184,6 +185,9 @@ def pq_compute_multi_core(matched_annotations_list,
categories (str): The categories of the dataset.
file_client (object): The file client of the dataset. If None,
the backend will be set to `disk`.
nproc (int): Number of processes for panoptic quality computing.
Defaults to 32. When `nproc` exceeds the number of cpu cores,
the number of cpu cores is used.
"""
if PQStat is None:
raise RuntimeError(
Expand All @@ -195,7 +199,8 @@ def pq_compute_multi_core(matched_annotations_list,
file_client_args = dict(backend='disk')
file_client = mmcv.FileClient(**file_client_args)

cpu_num = multiprocessing.cpu_count()
cpu_num = min(nproc, multiprocessing.cpu_count())

annotations_split = np.array_split(matched_annotations_list, cpu_num)
print('Number of cores: {}, images per core: {}'.format(
cpu_num, len(annotations_split[0])))
Expand Down
22 changes: 15 additions & 7 deletions mmdet/datasets/coco_panoptic.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,8 @@ def evaluate_pan_json(self,
result_files,
outfile_prefix,
logger=None,
classwise=False):
classwise=False,
nproc=32):
"""Evaluate PQ according to the panoptic results json file."""
imgs = self.coco.imgs
gt_json = self.coco.img_ann_map # image to annotations
Expand All @@ -451,9 +452,13 @@ def evaluate_pan_json(self,
gt_folder = self.seg_prefix
pred_folder = os.path.join(os.path.dirname(outfile_prefix), 'panoptic')

pq_stat = pq_compute_multi_core(matched_annotations_list, gt_folder,
pred_folder, self.categories,
self.file_client)
pq_stat = pq_compute_multi_core(
matched_annotations_list,
gt_folder,
pred_folder,
self.categories,
self.file_client,
nproc=nproc)

metrics = [('All', None), ('Things', True), ('Stuff', False)]
pq_results = {}
Expand All @@ -480,6 +485,7 @@ def evaluate(self,
logger=None,
jsonfile_prefix=None,
classwise=False,
nproc=32,
**kwargs):
"""Evaluation in COCO Panoptic protocol.
Expand All @@ -494,6 +500,9 @@ def evaluate(self,
If not specified, a temp file will be created. Default: None.
classwise (bool): Whether to print classwise evaluation results.
Default: False.
nproc (int): Number of processes for panoptic quality computing.
Defaults to 32. When `nproc` exceeds the number of cpu cores,
the number of cpu cores is used.
Returns:
dict[str, float]: COCO Panoptic style evaluation metric.
Expand All @@ -512,9 +521,8 @@ def evaluate(self,
outfile_prefix = os.path.join(tmp_dir.name, 'results') \
if tmp_dir is not None else jsonfile_prefix
if 'PQ' in metrics:
eval_pan_results = self.evaluate_pan_json(result_files,
outfile_prefix, logger,
classwise)
eval_pan_results = self.evaluate_pan_json(
result_files, outfile_prefix, logger, classwise, nproc=nproc)
eval_results.update(eval_pan_results)

if tmp_dir is not None:
Expand Down
2 changes: 1 addition & 1 deletion mmdet/datasets/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ def __init__(self,
self.proposal_file = proposal_file
self.test_mode = test_mode
self.filter_empty_gt = filter_empty_gt
self.CLASSES = self.get_classes(classes)
self.file_client = mmcv.FileClient(**file_client_args)
self.CLASSES = self.get_classes(classes)

# join paths if data_root is specified
if self.data_root is not None:
Expand Down
17 changes: 13 additions & 4 deletions mmdet/datasets/pipelines/loading.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ class LoadImageFromFile:
def __init__(self,
to_float32=False,
color_type='color',
channel_order='bgr',
file_client_args=dict(backend='disk')):
self.to_float32 = to_float32
self.color_type = color_type
self.channel_order = channel_order
self.file_client_args = file_client_args.copy()
self.file_client = None

Expand All @@ -63,7 +65,8 @@ def __call__(self, results):
filename = results['img_info']['filename']

img_bytes = self.file_client.get(filename)
img = mmcv.imfrombytes(img_bytes, flag=self.color_type)
img = mmcv.imfrombytes(
img_bytes, flag=self.color_type, channel_order=self.channel_order)
if self.to_float32:
img = img.astype(np.float32)

Expand All @@ -79,6 +82,7 @@ def __repr__(self):
repr_str = (f'{self.__class__.__name__}('
f'to_float32={self.to_float32}, '
f"color_type='{self.color_type}', "
f"channel_order='{self.channel_order}', "
f'file_client_args={self.file_client_args})')
return repr_str

Expand Down Expand Up @@ -438,9 +442,14 @@ def __init__(self,
'pip install git+https://github.com/cocodataset/'
'panopticapi.git.')

super(LoadPanopticAnnotations,
self).__init__(with_bbox, with_label, with_mask, with_seg, True,
file_client_args)
super(LoadPanopticAnnotations, self).__init__(
with_bbox=with_bbox,
with_label=with_label,
with_mask=with_mask,
with_seg=with_seg,
poly2mask=True,
denorm_bbox=False,
file_client_args=file_client_args)

def _load_masks_and_semantic_segs(self, results):
"""Private function to load mask and semantic segmentation annotations.
Expand Down
20 changes: 12 additions & 8 deletions mmdet/datasets/pipelines/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from mmdet.core import PolygonMasks, find_inside_bboxes
from mmdet.core.evaluation.bbox_overlaps import bbox_overlaps
from mmdet.utils import log_img_scale
from ..builder import PIPELINES

try:
Expand Down Expand Up @@ -1979,9 +1980,10 @@ class Mosaic:
Args:
img_scale (Sequence[int]): Image size after mosaic pipeline of single
image. Default to (640, 640).
image. The shape order should be (height, width).
Default to (640, 640).
center_ratio_range (Sequence[float]): Center ratio range of mosaic
output. Default to (0.5, 1.5).
output. Default to (0.5, 1.5).
min_bbox_size (int | float): The minimum pixel for filtering
invalid bboxes after the mosaic pipeline. Default to 0.
bbox_clip_border (bool, optional): Whether to clip the objects outside
Expand All @@ -2002,6 +2004,7 @@ def __init__(self,
skip_filter=True,
pad_val=114):
assert isinstance(img_scale, tuple)
log_img_scale(img_scale, skip_square=True)
self.img_scale = img_scale
self.center_ratio_range = center_ratio_range
self.min_bbox_size = min_bbox_size
Expand Down Expand Up @@ -2232,7 +2235,7 @@ class MixUp:
| pad |
+------------------------------+
The mixup transform steps are as follows::
The mixup transform steps are as follows:
1. Another random image is picked by dataset and embedded in
the top left patch(after padding and resizing)
Expand All @@ -2241,15 +2244,15 @@ class MixUp:
Args:
img_scale (Sequence[int]): Image output size after mixup pipeline.
Default: (640, 640).
The shape order should be (height, width). Default: (640, 640).
ratio_range (Sequence[float]): Scale ratio of mixup image.
Default: (0.5, 1.5).
Default: (0.5, 1.5).
flip_ratio (float): Horizontal flip ratio of mixup image.
Default: 0.5.
Default: 0.5.
pad_val (int): Pad value. Default: 114.
max_iters (int): The maximum number of iterations. If the number of
iterations is greater than `max_iters`, but gt_bbox is still
empty, then the iteration is terminated. Default: 15.
iterations is greater than `max_iters`, but gt_bbox is still
empty, then the iteration is terminated. Default: 15.
min_bbox_size (float): Width and height threshold to filter bboxes.
If the height or width of a box is smaller than this value, it
will be removed. Default: 5.
Expand Down Expand Up @@ -2281,6 +2284,7 @@ def __init__(self,
bbox_clip_border=True,
skip_filter=True):
assert isinstance(img_scale, tuple)
log_img_scale(img_scale, skip_square=True)
self.dynamic_scale = img_scale
self.ratio_range = ratio_range
self.flip_ratio = flip_ratio
Expand Down
3 changes: 3 additions & 0 deletions mmdet/models/dense_heads/centripetal_head.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import torch.nn as nn
from mmcv.cnn import ConvModule, normal_init
from mmcv.ops import DeformConv2d
from mmcv.runner import force_fp32

from mmdet.core import multi_apply
from ..builder import HEADS, build_loss
Expand Down Expand Up @@ -203,6 +204,7 @@ def forward_single(self, x, lvl_ind):
]
return result_list

@force_fp32()
def loss(self,
tl_heats,
br_heats,
Expand Down Expand Up @@ -361,6 +363,7 @@ def loss_single(self, tl_hmp, br_hmp, tl_off, br_off, tl_guiding_shift,

return det_loss, off_loss, guiding_loss, centripetal_loss

@force_fp32()
def get_bboxes(self,
tl_heats,
br_heats,
Expand Down
5 changes: 4 additions & 1 deletion mmdet/models/dense_heads/corner_head.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import torch.nn as nn
from mmcv.cnn import ConvModule, bias_init_with_prob
from mmcv.ops import CornerPool, batched_nms
from mmcv.runner import BaseModule
from mmcv.runner import BaseModule, force_fp32

from mmdet.core import multi_apply
from ..builder import HEADS, build_loss
Expand Down Expand Up @@ -152,6 +152,7 @@ def __init__(self,
self.train_cfg = train_cfg
self.test_cfg = test_cfg

self.fp16_enabled = False
self._init_layers()

def _make_layers(self, out_channels, in_channels=256, feat_channels=256):
Expand Down Expand Up @@ -509,6 +510,7 @@ def get_targets(self,

return target_result

@force_fp32()
def loss(self,
tl_heats,
br_heats,
Expand Down Expand Up @@ -649,6 +651,7 @@ def loss_single(self, tl_hmp, br_hmp, tl_emb, br_emb, tl_off, br_off,

return det_loss, pull_loss, push_loss, off_loss

@force_fp32()
def get_bboxes(self,
tl_heats,
br_heats,
Expand Down
2 changes: 1 addition & 1 deletion mmdet/models/dense_heads/ssd_head.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ def loss(self,
gt_bboxes_ignore_list=gt_bboxes_ignore,
gt_labels_list=gt_labels,
label_channels=1,
unmap_outputs=False)
unmap_outputs=True)
if cls_reg_targets is None:
return None
(labels_list, label_weights_list, bbox_targets_list, bbox_weights_list,
Expand Down
1 change: 1 addition & 0 deletions mmdet/models/dense_heads/yolox_head.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ def forward(self, feats):
self.multi_level_conv_reg,
self.multi_level_conv_obj)

@force_fp32(apply_to=('cls_scores', 'bbox_preds', 'objectnesses'))
def get_bboxes(self,
cls_scores,
bbox_preds,
Expand Down
Loading

0 comments on commit 75130ea

Please sign in to comment.