写在前面:本人正在学习MMDetection3D的过程中,可能有理解错误,欢迎指正。
参考视频链接:4小时入门深度学习+实操MMDetection 第二课
官方中文文档:MMDetection 文档
在官方github上下载所需模型(预训练模型参数文件/pth文件)及其配置文件(后面会讲配置文件的内容/py文件)。
然后使用下列代码进行单张图片的推断:
- from mmdet import init_detector, inference_detector, show_result_pyplot
-
- config_file = "xxxx.py" # 刚才下载的配置文件路径
- checkpoint_file = "xxxx.pth" # 刚才下载的模型文件路径
- model = init_detector(config_file, checkpoint_file) # 初始化模型并从checkpoint加载模型
- result = inference_detector(model, "demo.jpg") # 得到检测结果
- show_result_pyplot(model, "demo.jpg", result) # 可视化检测结果
官方教程:教程 1: 学习配置文件 — MMDetection 2.25.1 文档
配置文件定义了完整的训练过程,通常包含多个字段,重要的有
model字段:定义模型(包含损失函数、训练和测试时的设置等)
data字段:定义数据(包含预处理)
optimizer、lr_config等字段:定义训练策略
load_from字段:定义预训练模型的参数文件
下面以yolov3_mobilenetv2_mstrain-416_300e_coco.py为例进行简单介绍(暂不深究,目前需要注意的地方见注释):
- _base_ = '../_base_/default_runtime.py'
- # 表明该配置文件继承自配置文件default_runtime.py(后面会讲到继承)
- # 暂时可以理解为等价于将default_runtime.py中的内容复制到这里
- # model settings
- model = dict( # model字段
- type='YOLOV3',
- backbone=dict( # 主干网络
- type='MobileNetV2',
- out_indices=(2, 4, 6),
- act_cfg=dict(type='LeakyReLU', negative_slope=0.1),
- init_cfg=dict(
- type='Pretrained', checkpoint='open-mmlab://mmdet/mobilenet_v2')),
- neck=dict( # 颈部网络
- type='YOLOV3Neck',
- num_scales=3,
- in_channels=[320, 96, 32],
- out_channels=[96, 96, 96]),
- bbox_head=dict( # 检测头
- type='YOLOV3Head',
- num_classes=80, # 分类的类别数
- in_channels=[96, 96, 96],
- out_channels=[96, 96, 96],
- anchor_generator=dict(
- type='YOLOAnchorGenerator',
- base_sizes=[[(116, 90), (156, 198), (373, 326)],
- [(30, 61), (62, 45), (59, 119)],
- [(10, 13), (16, 30), (33, 23)]],
- strides=[32, 16, 8]),
- bbox_coder=dict(type='YOLOBBoxCoder'),
- featmap_strides=[32, 16, 8],
- loss_cls=dict( # 分类损失
- type='CrossEntropyLoss', # 损失类型:交叉熵
- use_sigmoid=True,
- loss_weight=1.0,
- reduction='sum'),
- loss_conf=dict( # 置信度损失(YOLO特有的损失)
- type='CrossEntropyLoss', # 损失类型:交叉熵
- use_sigmoid=True,
- loss_weight=1.0,
- reduction='sum'),
- loss_xy=dict( # 位置分类损失
- type='CrossEntropyLoss', # 损失类型:交叉熵
- use_sigmoid=True,
- loss_weight=2.0,
- reduction='sum'),
- loss_wh=dict( # 长宽回归损失
- type='MSELoss', # 损失类型:MSE
- loss_weight=2.0,
- reduction='sum')),
- # training and testing settings
- train_cfg=dict( # 训练配置
- assigner=dict(
- type='GridAssigner',
- pos_iou_thr=0.5, # 正锚框的IoU阈值设置
- neg_iou_thr=0.5, # 负锚框的IoU阈值设置
- min_pos_iou=0)),
- test_cfg=dict( # 测试配置
- nms_pre=1000,
- min_bbox_size=0,
- score_thr=0.05,
- conf_thr=0.005,
- nms=dict( # NMS设置
- type='nms',
- iou_threshold=0.45), # NMS中的IoU阈值设置
- max_per_img=100))
- # dataset settings
- dataset_type = 'CocoDataset'
- data_root = 'data/coco/'
- img_norm_cfg = dict(
- mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
- train_pipeline = [ # 训练数据读取和预处理
- dict(type='LoadImageFromFile'), # 读取图像
- dict(type='LoadAnnotations', with_bbox=True), # 读取标注
- dict( # 下面都是图像预处理及数据增广等操作
- type='Expand',
- mean=img_norm_cfg['mean'],
- to_rgb=img_norm_cfg['to_rgb'],
- ratio_range=(1, 2)),
- dict(
- type='MinIoURandomCrop',
- min_ious=(0.4, 0.5, 0.6, 0.7, 0.8, 0.9),
- min_crop_size=0.3),
- dict(
- type='Resize',
- img_scale=[(320, 320), (416, 416)],
- multiscale_mode='range',
- keep_ratio=True),
- dict(type='RandomFlip', flip_ratio=0.5),
- dict(type='PhotoMetricDistortion'),
- dict(type='Normalize', **img_norm_cfg),
- dict(type='Pad', size_divisor=32),
- dict(type='DefaultFormatBundle'),
- dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])
- ]
- test_pipeline = [ # 测试数据读取和预处理
- dict(type='LoadImageFromFile'),
- dict(
- type='MultiScaleFlipAug',
- img_scale=(416, 416),
- flip=False,
- transforms=[
- dict(type='Resize', keep_ratio=True),
- dict(type='RandomFlip'),
- dict(type='Normalize', **img_norm_cfg),
- dict(type='Pad', size_divisor=32),
- dict(type='DefaultFormatBundle'),
- dict(type='Collect', keys=['img'])
- ])
- ]
- data = dict( # data字段
- samples_per_gpu=24, # batch size
- workers_per_gpu=4,
- train=dict( # 训练集
- type='RepeatDataset', # use RepeatDataset to speed up training
- times=10,
- dataset=dict(
- type=dataset_type,
- ann_file=data_root + 'annotations/instances_train2017.json',
- # 训练集标注文件路径
- img_prefix=data_root + 'train2017/', # 训练集图像所在文件夹路径
- pipeline=train_pipeline)),
- val=dict( # 验证集
- type=dataset_type,
- ann_file=data_root + 'annotations/instances_val2017.json',
- img_prefix=data_root + 'val2017/',
- pipeline=test_pipeline),
- test=dict( # 测试集
- type=dataset_type,
- ann_file=data_root + 'annotations/instances_val2017.json',
- img_prefix=data_root + 'val2017/',
- pipeline=test_pipeline))
- # optimizer
- optimizer = dict( # 优化器设置
- type='SGD', # 优化器类型
- lr=0.003, # 学习率
- momentum=0.9,
- weight_decay=0.0005)
- optimizer_config = dict(grad_clip=dict(max_norm=35, norm_type=2))
- # learning policy
- lr_config = dict( # 学习率变化策略设置
- policy='step',
- warmup='linear',
- warmup_iters=4000,
- warmup_ratio=0.0001,
- step=[24, 28])
- # runtime settings
- runner = dict(
- type='EpochBasedRunner',
- max_epochs=30) # 训练的总epoch数
- evaluation = dict(
- interval=1,
- metric=['bbox'])
- find_unused_parameters = True
-
- # NOTE: `auto_scale_lr` is for automatically scaling LR,
- # USER SHOULD NOT CHANGE ITS VALUES.
- # base_batch_size = (8 GPUs) x (24 samples per GPU)
- auto_scale_lr = dict(base_batch_size=192)
官方教程:教程 2: 自定义数据集 — MMDetection 2.25.1 文档
以目标检测任务为例,若要使用自己的数据集,在预训练模型上进行微调,则步骤如下:
1.下载基础模型的参数和配置文件
2.将自己的数据集整理为MMDetection支持的格式
一个方法是使自己的数据集与已支持的数据集有相同的组织方式(如标注结构与含义等)。
例如,要将自己的图像数据集组织为Coco数据集的形式,首先观察Coco数据集标注文件(json文件)格式:
- {
- "info" : info, # 数据集相关,可忽略
- "images" : [image], # 图像列表
- "annotations" : [annotation], # 标注列表
- "license" : [license], # 数据集相关,可忽略
- "categories" : [categories], # 类别名称列表
- }
其中
- image {
- "id" : int,
- "width" : int,
- "height" : int,
- "file_name" : str,
- "license" : int,
- "flickr_url" : str,
- "coco_url" : str,
- "date_captured" : datetime,
- }
- annotation {
- "id" : int,
- "image_id" : int,
- "category_id" : int,
- "segmentation" : RLE or [polygon],
- "area" : float,
- "bbox" : [x,y,width,height], # x,y为边界框左上角到图像左上角的距离
- "iscrowd" : 0 or 1,
- }
- categories [{
- "id" : int,
- "name" : str,
- "supercategory" : str,
- }]
按照上述格式建立自己数据集的标注文件即可。
将自定义数据集转换为预训练时所用数据集的形式后,还需要在配置文件的data字段中修改相应的图像文件路径和标注文件路径(见下一步)。
3.修改配置文件(数据路径、分类头、预训练模型加载、优化器配置等)
配置文件的修改可通过继承的方式(当然,也可直接复制预训练模型的配置文件后修改)。
例如新建微调模型的配置文件(如new_model_cfg.py),想在预训练模型的配置文件上修改时,可先写上如下语句表明该配置文件是在原配置文件上进行修改的:
_base_ = ['yolov3_mobilenetv2_mstrain-416_300e_coco.py'] # 预训练模型的配置文件路径
然后修改数据路径只需要找到对应项进行修改(下列代码中未出现的项不变):
- data = dict(
- train = dict(
- dataset = dict(
- ann_file = 'xxx', # 标注文件路径
- img = 'xxx', # 图像路径
- classes = ("xx", ...)) # 类别名称(新增项)
- ),
- val = dict(...), # 类似修改验证集和测试集
- test = dict(...)
- )
分类头的修改也类似,只需修改类别数:
model = dict(bbox_head(num_classes = xx))
训练配置的修改同理:
- runner = dict(max_epochs=xx)
- optimizer = dict(lr=xx)
- lr_config = None
最后加上load_from字段以使模型从预训练模型开始进行微调:
- load_from = "yolov3_mobilenetv2_mstrain-416_300e_coco_20210718_010823-f68a07b3.pth"
- # 预训练模型参数文件路径
输出信息的频率设置如下(原始模型的配置文件中该部分继承自default_runtime.py):
log_config = dict(interval=xx) # 每xx迭代次数输出一次信息