本文入选【技术写作训练营】优秀结营作品,作者:易宁远
MMYOLO 是一个基于 PyTorch 和 MMDetection 的 YOLO 系列算法开源工具箱,它是 OpenMMLab 项目的一部分。MMYOLO 定位为 YOLO 系列热门开源库以及工业应用核心库,其中实现了很多 YOLO 系列的算法(YOLOv5、v6、v7 以及 RTMDet-Rotated、RTMDet)。
本文综合多处 MMYOLO 文档,并结合了本人的实践经验,希望可以帮助到大家。
主要参考资料如下: https://mmyolo.readthedocs.io/zh_CN/latest/get_started/overview.html https://github.com/open-mmlab/OpenMMLabCourse/blob/main/mmyolo.md
1. 基础测试
这部分主要涉及 MMYOLO 的安装与测试,目的是查看 MMYOLO 是否安装成功。
1.1 安装和验证
安装完成 MMYOLO 后,按照 MMYOLO 文档中的方法进行验证:
文档链接:https://mmyolo.readthedocs.io/zh_CN/latest/get_started/installation.html
1.2 建立软连接
为方便代码调试以及后面的跨库调用模块,参考以下命名完成软连接操作,将 OpenMMLab 其他库链接到 MMYOLO 中:
代码语言:javascript复制# 下载 MMYOLO
git clone git@github.com:open-mmlab/MMYOLO.git
# 下载 mmengine
git clone https://github.com/open-mmlab/mmengine.git
# 下载 mmdet
git clone https://github.com/open-mmlab/mmdetection.git
# 进入 MMYOLO 路径下cd MMYOLO
# 建立软链接(win10 也支持软链接了)
ln -s ../mmdetection/mmdet mmdet
ln -s ../mmengine/mmengine mmengine
注意检查更新保持 git clone 的链接为最新的链接地址
完成后可以在 pycharm 等 IDE 中试下能否全局搜索跳转到 DetDataPreprocessor(mmdet 中一个类)。
2. 数据从准备到训练的全流程
2.1 单一类别
2.1.1 数据集获取与划分
数据集获取
可以直接下载制作好的数据集或者自己制作。MMYOLO 自带了下载脚本,可以下载一些方便验证算法与掌握 MMYOLO 的数据集,并且还贴心地配有后续转化为 coco 格式以及划分数据集的脚本。、MMYOLO 单类别提供了两类数据集:猫猫数据集和气球数据集,猫猫数据集直接为 MMYOLO 配套的 coco 格式,气球数据集需要使用 ./tool 下的数据集转化脚本转换为 coco 形式。下面以下载 MMYOLO 配套猫猫(cat)数据集为例:
代码语言:javascript复制python tools/misc/download_dataset.py --dataset-name cat --save-dir ./data/cat --unzip --delete
数据集划分
通常,自定义图片都是一个大文件夹,里面全部都是图片,需要我们自己去对图片进行训练集、验证集、测试集的划分,如果数据量比较少,可以不划分验证集。下面是划分脚本的具体用法:
代码语言:javascript复制python tools/misc/coco_split.py --json ${COCO label json 路径}
--out-dir ${划分 label json 保存根路径}
--ratios ${划分比例}
[--shuffle]
[--seed ${划分的随机种子}]
其中:
- --ratios:划分的比例,如果只设置了 2 个,则划分为 trainval test;如果设置为 3 个,则划分为 train val test。支持两种格式 —— 整数、小数: 整数:按比例进行划分,代码中会进行归一化之后划分数据集。例子:--ratio 2 1 1(代码里面会转换成 0.5 0.25 0.25) or --ratio 3 1(代码里面会转换成 0.75 0.25) 小数:划分为比例。如果加起来不为 1 ,则脚本会进行自动归一化修正。例子:--ratio 0.8 0.1 0.1 or --ratio 0.8 0.2
- --shuffle: 是否打乱数据集再进行划分。
- --seed:设定划分的随机种子,不设置的话自动生成随机种子。
例子:
代码语言:javascript复制python tools/misc/coco_split.py --json ./data/cat/annotations/annotations_all.json
--out-dir ./data/cat/annotations
--ratios 0.8 0.2
--shuffle
--seed 10
2.1.2 转 coco 格式后数据集操作
数据集格式(不一定为这个文件夹结构,训练和测试的 json 格式对即可)
- annotation_all.json 是所有的标注。trainval.json 和 test.json 是从 annotation_all.json 中划分出的训练验证集和测试集
- iamge 文件夹下,是所有图片
- class_with_id.txt 是类别的 id 标号和类别名的对应关系,如:1 cat
数据集检查和可视化
脚本1,检查数据集:
使用下面的命令可以将 COCO 的 label 在图片上进行显示,这一步可以验证刚刚转换是否有问题:
代码语言:javascript复制python tools/analysis_tools/browse_coco_json.py --img-dir ./data/cat/images --ann-file ./data/cat/annotations/annotations_all.json
效果:逐一显示标注后的图片,显示图片数量和类别名称、数量。(Ctrl C 强行终止显示)
更多参数可参考: https://mmyolo.readthedocs.io/zh-cn/latest/useful_tools/browse_dataset.html
脚本2,可视化数据集:
脚本 tools/analysis_tools/dataset_analysis.py 能够帮助用户得到数据集的分析图。该脚本可以生成 4 种分析图:
- 显示类别和 bbox 实例个数的分布图:show_bbox_num
- 显示类别和 bbox 实例宽、高的分布图:show_bbox_wh
- 显示类别和 bbox 实例宽/高比例的分布图:show_bbox_wh_ratio
- 基于面积规则下,显示类别和 bbox 实例面积的分布图:show_bbox_area
以本教程 cat 数据集 的 config 为例。
注:本脚本需要使用 config 文件,按本文档行文顺序,目前并未讲如何写对应数据集的 config 文件,所以该脚本可以在下一小节有了 config 文件 后再尝试。
查看训练集数据分布情况:
代码语言:javascript复制python tools/analysis_tools/dataset_analysis.py configs/custom_dataset/yolov5_s-v61_syncbn_fast_1xb32-100e_cat.py --out-dir work_dirs/dataset_analysis_cat/train_dataset
查看验证集数据分布情况:
代码语言:javascript复制python tools/analysis_tools/dataset_analysis.py configs/custom_dataset/yolov5_s-v61_syncbn_fast_1xb32-100e_cat.py
--out-dir work_dirs/dataset_analysis_cat/val_dataset
--val-dataset
更多内容可参考:https://mmyolo.readthedocs.io/zh-cn/latest/useful_tools/dataset_analysis.html
2.1.3 配置文件的复用更改
MMYOLO 根据配置文件(写有各种字段的 py 文件)调整网络结构与训练测试流程,配置文件是“总控制器”。训练自己的模型可以复制一份原有配置文件(./config/xxx/yyy.py)然后进行修改。这里暂不涉及对模型结构修改(对模型结构的修改见第四节)。复用配置文件需要改:
- 数据集根路径(即存放image和ann的总目录)。注意:最后要有/结尾
- 训练、测试等json文件路径
- 图片子路径前缀(从根路径到图片文件夹的路径)
- class name(注意:('cat',)单个种类最后要空个,否则不认识为tuple类型)
- class_num
- metainfo:指定类别名称和对应的bbox颜色
- 以上为配合数据集需要修改的,其他参数根据个人需要进行修改。例如:
batch size
workers
max_epochs :训练多少epoch
train_batch_size_per_gpu :根据gpu和数据情况确定
其他
参考:
代码语言:javascript复制# 基于该配置进行继承并重写部分配置
_base_ = 'yolov5_s-v61_syncbn_fast_8xb16-300e_coco.py'
data_root = './data/cat/' # 数据集根路径
class_name = ('cat', ) # 数据集类别名称
num_classes = len(class_name) # 数据集类别数
# metainfo 必须要传给后面的 dataloader 配置,否则无效
# palette 是可视化时候对应类别的显示颜色
# palette 长度必须大于或等于 classes 长度
metainfo = dict(classes=class_name, palette=[(20, 220, 60)])
# 基于 tools/analysis_tools/optimize_anchors.py 自适应计算的 anchor
anchors = [
[(68, 69), (154, 91), (143, 162)], # P3/8
[(242, 160), (189, 287), (391, 207)], # P4/16
[(353, 337), (539, 341), (443, 432)] # P5/32
]
# 最大训练 40 epoch
max_epochs = 40
# bs 为 12
train_batch_size_per_gpu = 12
# dataloader 加载进程数
train_num_workers = 4
# 加载 COCO 预训练权重
load_from = 'https://download.openmmlab.com/MMYOLO/v0/yolov5/yolov5_s-v61_syncbn_fast_8xb16-300e_coco/yolov5_s-v61_syncbn_fast_8xb16-300e_coco_20220918_084700-86e02187.pth' # noqa
model = dict(
# 固定整个 backbone 权重,不进行训练
backbone=dict(frozen_stages=4),
bbox_head=dict(
head_module=dict(num_classes=num_classes),
prior_generator=dict(base_sizes=anchors)
))
train_dataloader = dict(
batch_size=train_batch_size_per_gpu,
num_workers=train_num_workers,
dataset=dict(
data_root=data_root,
metainfo=metainfo,
# 数据集标注文件 json 路径
ann_file='annotations/trainval.json',
# 数据集前缀
data_prefix=dict(img='images/')))
val_dataloader = dict(
dataset=dict(
metainfo=metainfo,
data_root=data_root,
ann_file='annotations/test.json',
data_prefix=dict(img='images/')))
test_dataloader = val_dataloader
_base_.optim_wrapper.optimizer.batch_size_per_gpu = train_batch_size_per_gpu
val_evaluator = dict(ann_file=data_root 'annotations/test.json')
test_evaluator = val_evaluator
default_hooks = dict(
# 每隔 10 个 epoch 保存一次权重,并且最多保存 2 个权重
# 模型评估时候自动保存最佳模型
checkpoint=dict(interval=10, max_keep_ckpts=2, save_best='auto'),
# warmup_mim_iter 参数非常关键,因为 cat 数据集非常小,默认的最小 warmup_mim_iter 是 1000,导致训练过程学习率偏小
param_scheduler=dict(max_epochs=max_epochs, warmup_mim_iter=10),
# 日志打印间隔为 5
logger=dict(type='LoggerHook', interval=5))
# 评估间隔为 10
train_cfg = dict(max_epochs=max_epochs, val_interval=10)
注意:定义的所有参数要在复用的 config 文件中被调用才会起作用。例如:
定义并更改了 num_classes 值,就要在需要该值的 bbox_head 的 head_module=dict(num_classes=num_classes) 中被调用。对 metainfo 和 max_epochs 的修改同理,都需要在 dict 中被调用。
另外,对于需要 anchor 的算法:anchor 参数可通过 MMYOLO 提供的脚本生成:
YOLOv5 是 一个 anchor-based 算法,需要对 anchor 进行自适应计算,anchor-free 的模型可以跳过此步骤。脚本 tools/analysis_tools/optimize_anchors.py 支持 YOLO 系列中三种锚框生成方式,分别是 k-means、Differential Evolution、v5-k-means。
本示例使用的是 YOLOv5 进行训练,使用的是 640 x 640 的输入大小,使用 v5-k-means 进行描框的优化:
代码语言:javascript复制python tools/analysis_tools/optimize_anchors.py configs/custom_dataset/yolov5_s-v61_syncbn_fast_1xb32-100e_cat.py
--algorithm v5-k-means
--input-shape 640 640
--prior-match-thr 4.0
--out-dir work_dirs/dataset_analysis_cat
更多内容可参考: https://mmyolo.readthedocs.io/zh-cn/latest/useful_tools/optimize_anchors.html
2.1.4 训练前的检查
检查 pipeline
代码语言:javascript复制python tools/analysis_tools/browse_dataset.py
${CONFIG_FILE}
[-o, --out-dir ${OUTPUT_DIR}]
[-p, --phase ${DATASET_PHASE}]
[-n, --show-number ${NUMBER_IMAGES_DISPLAY}]
[-i, --show-interval ${SHOW_INTERRVAL}]
[-m, --mode ${DISPLAY_MODE}]
[--cfg-options ${CFG_OPTIONS}]
所有参数的说明:
- config:模型配置文件的路径。
- -o, --out-dir: 保存图片文件夹,如果没有指定,默认为 './output'。
- -p, --phase: 可视化数据集的阶段,只能为 ['train', 'val', 'test'] 之一,默认为 'train'。
- -n, --show-number: 可视化样本数量。如果没有指定,默认展示数据集的所有图片。
- -m, --mode: 可视化的模式,只能为 ['original', 'transformed', 'pipeline'] 之一。默认为 'transformed'。
- --cfg-options : 对配置文件的修改,参考学习配置文件。
代码语言:javascript复制学习配置文件链接:https://mmyolo.readthedocs.io/zh-cn/latest/tutorials/config.html
`-m, --mode` 用于设置可视化的模式,默认设置为 'transformed'。
- 如果 `--mode` 设置为 'original',则获取原始图片;
- 如果 `--mode` 设置为 'transformed',则获取预处理后的图片;
- 如果 `--mode` 设置为 'pipeline',则获得数据流水线所有中间过程图片。
更多内容可参考可视化数据集文档: https://mmyolo.readthedocs.io/zh-cn/latest/useful_tools/browse_dataset.html
例如:
代码语言:javascript复制python tools/analysis_tools/browse_dataset.py cor data_analyse --show-number 10 --mode 'pipeline'--out-dir
(图片比较大 number 为 1 就可以)
检查优化器配置
代码语言:javascript复制python tools/analysis_tools/vis_scheduler.py configs/yolov5/yolov5_s-v61_fast_1xb12-40e_cat.py --dataset-size 1000 --ngpus 1 --out-dir data_analyse
更多内容可参考可视化优化器参数策略文档: https://mmyolo.readthedocs.io/zh-cn/latest/useful_tools/vis_scheduler.html
打印全部配置文件
获得全部配置文件可以通过 mim 脚本获得:
代码语言:javascript复制mim runmim run mmdet print_config configs/yolov5/yolov5_s-v61_fast_1xb12-40e_cat.py --save-path configs_full/yolov5/yolov5_s-v61_fast_1xb12-40e_cat.py
也可以在开始训练后的输出路径中获得(默认为 work_dir/ 配置文件名/配置文件 .py)。
在总配置文件中,首先检查类别信息、json 文件和图片路径。这是能否跑通的基础。
2.1.5 训练
可视化:
在我们刚刚新建的 config 文件 configs/custom_dataset/yolov5_s-v61_syncbn_fast_1xb32-100e_cat.py 中添加 tensorboard 配置(写在最后一行即可):
代码语言:javascript复制visualizer = dict(vis_backends=[dict(type='LocalVisBackend'),dict(type='TensorboardVisBackend')])
训练:
代码语言:javascript复制python tools/train.py configs/yolov5/yolov5_s-v61_fast_1xb12-40e_cat.py
如果训练中途停止,可以在训练命令最后加上 --resume,程序会自动从 work_dirs 中加载最新的权重文件恢复训练。
代码语言:javascript复制python tools/train.py configs/yolov5/yolov5_s-v61_fast_1xb12-40e_cat.py --resume
上述配置大概需要 3.0G 显存,如果你的显存不够,可以考虑开启混合精度训练。
代码语言:javascript复制python tools/train.py configs/yolov5/yolov5_s-v61_fast_1xb12-40e_cat.py --amp
运行训练命令后,Tensorboard 文件会生成在可视化文件夹 work_dirs/yolov5_s-v61_syncbn_fast_1xb32-100e_cat/${TIMESTAMP}/vis_data 下, 运行下面的命令便可以在网页链接使用 Tensorboard 查看 loss、学习率和 coco/bbox_mAP 等可视化数据了:
代码语言:javascript复制tensorboard --logdir=work_dirs/yolov5_s-v61_fast_1xb12-40e_cat/20230823_171741
注意:检查l ogdir 路径是不是最新的。
在本人运行中发现,使用 tensorboard 可能会遇到以下报错:
MMYOLO 安装并不会默认安装 tensorboard。所以未安装 tensorbord 就使用上述命令,可能会报 chardet 相关错误。所以第一次使用可能需要先运行一下命令,然后安装 tensorboard。
代码语言:javascript复制pip install chardet
如果运行中遇到问题:module 'distutils' has no attribute 'version' ,解决方法可以参考:https://zhuanlan.zhihu.com/p/556704117
2.1.6 测试
训练中 MMYOLO 会按照指定的间隔运行测试,打印测试指标和结果。我们在训练完成后,可以通过以下命令测试得到的模型的表现:
代码语言:javascript复制python tools/test.py configs/yolov5/yolov5_s-v61_fast_1xb12-40e_cat.py work_dirs/yolov5_s-v61_fast_1xb12-40e_cat/epoch_40.pth --show-dir show_results
如果想输出每个分类的指标(当然对于单一类别,本步骤没有多大意义,但对于多类别目标识别还是很有意义的),附加参数:
代码语言:javascript复制--cfg-options test_evaluator.classwise=True
2.1.7 可视化
查看网络结构:
代码语言:javascript复制python demo/featmap_vis_demo.py data/cat/images/IMG_20210705_084125__01.jpg configs/yolov5/yolov5_s-v61_fast_1xb12-40e_cat.py work_dirs/yolov5_s-v61_fast_1xb12-40e_cat/epoch_40.pth --preview-model
可以在命令最后加上 > net.txt 将结果存入 net.txt 中(也可以用 2.1.8 模型复杂度分析脚本,也能打印出网络结构,不全面,但很清晰)
画出特征图:
代码语言:javascript复制python demo/featmap_vis_demo.py data/cat/images/IMG_20221020_112705.jpg configs/yolov5/yolov5_s-v61_fast_1xb12-40e_cat.py work_dirs/yolov5_s-v61_fast_1xb12-40e_cat/epoch_40.pth --target-layers backbone --channel-reduction squeeze_mean
代码语言:javascript复制python demo/featmap_vis_demo.py data/cat/images/IMG_20210627_225110.jpg configs/yolov5/yolov5_s-v61_fast_1xb12-40e_cat.py work_dirs/yolov5_s-v61_fast_1xb12-40e_cat/epoch_40.pth --target-layers neck.upsample_layers[0] --channel-reduction squeeze_mean
具体参数参考: https://mmyolo.readthedocs.io/zh-cn/latest/get_started/15_minutes_object_detection.html
2.1.8 模型复杂度分析
代码语言:javascript复制python tools/analysis_tools/get_flops.py configs/yolov5/yolov5_s-v61_fast_1xb12-40e_cat.py
默认打印:总体的 Input shape: Model Flops: Model Parameters:。以及网络层次每层情况
使用 --not-show-table 不显示每层情况
其他参数类似,见: https://mmyolo.readthedocs.io/zh-cn/latest/recommended_topics/complexity_analysis.html
2.2 多类别
2.2.1 多类别数据下载与整理
上面讲了单一类别目标识别任务的相关数据准备和训练的说明。下面对多类别数据进行补充。在数据获取方面可以是自制数据集也可以是 MMYOLO 配套的示例数据集,这里以 COCO 数据集为例。
下载:
代码语言:javascript复制wget -P data https://extremevision-js-userfile.oss-cn-hangzhou.aliyuncs.com/user-25117-files/ff6429ed-0f22-4759-aa1e-f3d3f8dc4b40/coco.zip
结构:
train2017 val2017 下均为图片,两个 json 是图片信息和标注信息。
检查:
代码语言:javascript复制python tools/analysis_tools/browse_coco_json.py
--img-dir data/COCO128/coco/train2017
--ann-file data/COCO128/coco/annotations/instances_train2017.json
运行时会给出图片信息、类别信息:如种类数目、名称等。但是在复用时,不需要。
2.2.2 配置文件的复用更改
首先,改数据文件路径:
代码语言:javascript复制# ======================= Possible modified parameters =======================
# -----data changes-----
# -----data related-----
data_root = 'data/COCO128/coco/' # Root path of data
# Path of train annotation file
train_ann_file = 'data/COCO128/coco/annotations/instances_train2017.json'
train_data_prefix = 'train2017/' # Prefix of train image path
# Path of val annotation file
val_ann_file = 'data/COCO128/coco/annotations/instances_val2017.json'
val_data_prefix = 'val2017/' # Prefix of val image path
num_classes = 80 # Number of classes for classification
# Batch size of a single GPU during training
train_batch_size_per_gpu = 4
# Worker to pre-fetch data for each single GPU during training
train_num_workers = 4
# persistent_workers must be False if num_workers is 0
persistent_workers = True
# -----train val related-----
# Base learning rate for optim_wrapper
base_lr = 0.01
max_epochs = 4 # Maximum training epochs
num_last_epochs = 15 # Last epoch number to switch training pipeline
# ======================= Possible modified parameters =======================
# -----data related-----
img_scale = (640, 640) # width, height
# Dataset type, this will be used to define the dataset
dataset_type = 'YOLOv5CocoDataset'
# Batch size of a single GPU during validation
val_batch_size_per_gpu = 1
# Worker to pre-fetch data for each single GPU during validation
val_num_workers = 2
注意:开始的超参数修改并不会起作用。要在后面的 dict 中调用才可以。(继承后不会修改父文件超参数部分,只会修改 dict 中部分)。所以 要改 train_dataloader、val_dataloader、test_dataloadertest_evaluator 、val_evaluator、default_hooks 等用到所更改的超参数的部分。
而后,改类别:
类别名称改法:
代码语言:javascript复制METAINFO = {
'classes':
('person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train',
'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign',
'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep',
'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella',
'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard',
'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard',
'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork',
'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange',
'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair',
'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv',
'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave',
'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase',
'scissors', 'teddy bear', 'hair drier', 'toothbrush'),
# palette is a list of color tuples, which is used for visualization.
'palette':
[(220, 20, 60), (119, 11, 32), (0, 0, 142), (0, 0, 230), (106, 0, 228),
(0, 60, 100), (0, 80, 100), (0, 0, 70), (0, 0, 192), (250, 170, 30),
(100, 170, 30), (220, 220, 0), (175, 116, 175), (250, 0, 30),
(165, 42, 42), (255, 77, 255), (0, 226, 252), (182, 182, 255),
(0, 82, 0), (120, 166, 157), (110, 76, 0), (174, 57, 255),
(199, 100, 0), (72, 0, 118), (255, 179, 240), (0, 125, 92),
(209, 0, 151), (188, 208, 182), (0, 220, 176), (255, 99, 164),
(92, 0, 73), (133, 129, 255), (78, 180, 255), (0, 228, 0),
(174, 255, 243), (45, 89, 255), (134, 134, 103), (145, 148, 174),
(255, 208, 186), (197, 226, 255), (171, 134, 1), (109, 63, 54),
(207, 138, 255), (151, 0, 95), (9, 80, 61), (84, 105, 51),
(74, 65, 105), (166, 196, 102), (208, 195, 210), (255, 109, 65),
(0, 143, 149), (179, 0, 194), (209, 99, 106), (5, 121, 0),
(227, 255, 205), (147, 186, 208), (153, 69, 1), (3, 95, 161),
(163, 255, 0), (119, 0, 170), (0, 182, 199), (0, 165, 120),
(183, 130, 88), (95, 32, 0), (130, 114, 135), (110, 129, 133),
(166, 74, 118), (219, 142, 185), (79, 210, 114), (178, 90, 62),
(65, 70, 15), (127, 167, 115), (59, 105, 106), (142, 108, 45),
(196, 172, 0), (95, 54, 80), (128, 76, 255), (201, 57, 1),
(246, 0, 122), (191, 162, 208)]
}
这是 MMDetection 中 coco 数据类的定义,更改方法:
声明超参数
代码语言:javascript复制metainfo = dict(classes=('','', ....), palette=[(20, 220, 60),( , , ), ....])
在 dataloader 中调用(train_dataloader、val_dataloader、test_dataloader等)
代码语言:javascript复制val_dataloader = dict(
dataset=dict(
metainfo=metainfo
))
类别个数改法:
代码语言:javascript复制num_classes = len(class_name)
代码语言:javascript复制model = dict(
...
bbox_head=dict(
head_module=dict(
num_classes=num_classes
),
)
..
)
3. 更改网络-基本
3.1 更改 backbone
3.1.1 更改 MMYOLO 中的其他 backbone
假设想将 YOLOv6EfficientRep 作为 YOLOv5 的主干网络,则配置文件如下:
代码语言:javascript复制_base_ = './yolov5_s-v61_syncbn_8xb16-300e_coco.py'
model = dict(
backbone=dict(
type='YOLOv6EfficientRep',
norm_cfg=dict(type='BN', momentum=0.03,eps=0.001),
act_cfg=dict(type='ReLU', inplace=True))
)
tpye 和参数,看 MMYOLO/models/backbones/__init__.py 和 MMYOLO/models/backbones/ 中对应 backbone 实现 py 文件的类的参数(写在备注或 init 函数的参数中)。
3.1.2 更换 MMDetection 中的 backbone
使用 MMDetection 里 resnet50:
代码语言:javascript复制_base_ = './yolov5_s-v61_syncbn_8xb16-300e_coco.py'
deepen_factor = _base_.deepen_factor
widen_factor = 1.0
channels = [512, 1024, 2048]
model = dict(backbone=dict(
_delete_=True, # 将 _base_ 中关于 backbone 的字段删除type='mmdet.ResNet',
# 使用 mmdet 中的 ResNet
depth=50,num_stages=4,
out_indices=(1, 2, 3),
frozen_stages=1,
norm_cfg=dict(type='BN', requires_grad=True),
norm_eval=True,
style='pytorch',
init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),
neck=dict(type='YOLOv5PAFPN',
widen_factor=widen_factor,
in_channels=channels, # 注意:ResNet-50 输出的3个通道是 [512, 1024, 2048],和原先的 yolov5-s neck 不匹配,需要更改
out_channels=channels),
bbox_head=dict(
type='YOLOv5Head',
head_module=dict(type='YOLOv5HeadModule',
in_channels=channels,
# head 部分输入通道也要做相应更改
widen_factor=widen_factor)))
改法:
- _delete_要有,删除原来的所有配置
- 参数参照 MMDetection 里 model 的 backbone 实现 py 文件,一般注意 out_indices 参数
- neck 和 head 的输入/输出 channel 要改
>>> from mmdet.models import ResNet
>>> import torch
>>> self = ResNet(depth=18,out_indices=(1, 2, 3) #这一行,尽量所有参数和配置文件一样
>>> self.eval()
>>> inputs = torch.rand(1, 3, 32, 32)
>>> level_outputs = self.forward(inputs)
>>> for level_out in level_outputs:
... print(tuple(level_out.shape))
获得的第二个数字为各个输出通道的维度,写做[ , , ]
- widen_factor 改为 1, 因为 out_channels 由 widen_factor 控制, 的out_channels = out_channels * widen_factor
使用 MMDetection 里 regnet:
代码语言:javascript复制
channels = [192, 432, 1008]
model = dict(
backbone=dict(
_delete_=True,
type='mmdet.RegNet',
arch=dict(
w0=88,
wa=26.31,
wm=2.25,
group_w=48,
depth=25,
bot_mul=1.0),
out_indices=(1, 2, 3),
init_cfg=dict(
type='Pretrained', checkpoint='down_model/regnetx-400mf_8xb128_in1k_20211213-89bfc226.pth'
)
),
- 根据 MMDetection 里 reg 实现,取了 arch 参数
- 根据 yolo 选择了 out_indices 参数
- 根据测试脚本,得到输出尺度
from mmdet.models import RegNet
import torch
self = RegNet(
arch=dict(
w0=88,
wa=26.31,
wm=2.25,
group_w=48,
depth=25,
bot_mul=1.0),
out_indices=(1, 2, 3), )
self.eval()
inputs = torch.rand(1, 3, 32, 32)
level_outputs = self.forward(inputs)
for level_out in level_outputs:
print(tuple(level_out.shape)) # [192, 432, 1008]
- 权重来源:
https://mmpretrain.readthedocs.io/zh_CN/latest/papers/regnet.html
- 模型库统计:
https://mmpretrain.readthedocs.io/zh_CN/latest/modelzoo_statistics.html
3.1.3 更改 MMPretrain 里的 backbone
需要先安装 MMPretrain,最后使用软链接将 mmpretrain/mmpretrain 链接到 MMYOLO/mmpretrain 方便跳转查看。
代码语言:javascript复制安装方法参考:https://mmpretrain.readthedocs.io/zh-cn/latest/get_started.html#id2
custom_imports = dict(imports=['mmpretrain.models'], allow_failed_imports=False)
# 注册mmpretrain
max_epochs = 50
train_batch_size_per_gpu = 4
train_num_workers = 4
widen_factor: float = 1.0
# load_from = 'https://download.openmmlab.com/MMYOLO/v0/yolov5/yolov5_s-v61_syncbn_fast_8xb16-300e_coco/yolov5_s-v61_syncbn_fast_8xb16-300e_coco_20220918_084700-86e02187.pth' # noqa
channels = [192, 384, 768]
model = dict(
backbone=dict(
_delete_=True,
type='mmpretrain.SwinTransformer',
arch='tiny',
img_size=224,
patch_size=4,
in_channels=3,
window_size=7,
drop_rate=0.,
drop_path_rate=0.1,
out_indices=(1, 2, 3),
out_after_downsample=False,
use_abs_pos_embed=False,
interpolate_mode='bicubic',
with_cp=False,
frozen_stages=-1,
norm_eval=False,
pad_small_map=False,
norm_cfg=dict(type='LN'),
stage_cfgs=dict(),
patch_cfg=dict(),
init_cfg=dict(
type='Pretrained',
checkpoint='down_model/swin_tiny_224_b16x64_300e_imagenet_20210616_090925-66df6be6.pth',
prefix='backbone.'
)
),
更改方法与使用 MMDetection 里的类似。注意要注册 MMPretrain 以及 inin_cfg 中 prefix 参数。
在使用脚本测试 backbone 输出时,有的 backbone 输出不满足 [(1,x,y,z),(1,x2,y2,z2),(1,x3,y3,z3)] 形式,yolov5 的 neck 对不上,这样的 backbone 不能直接用于 yolov5 的替换。
预训练权重:
https://mmpretrain.readthedocs.io/zh_CN/latest/modelzoo_statistics.html
3.2 backbone 加插件
代码语言:javascript复制model = dict(backbone=dict(
plugins=[
dict(
cfg=dict(
type='GeneralizedAttention',spatial_range=-1,
num_heads=8,attention_type='0011',kv_stride=2),
stages=(False, False, True, True))
]
))
可以使用 2.1.7 中的命令查看网络结构。
3.3 更改 head
head 部分成分比较多。每个都可以按 backbone、neck 思路改。包括:
- head_module
- prior_generator
- loss类
loss_cls
loss_obj
loss_bbox
代码语言:javascript复制# 一个head例:
bbox_head=dict(
type='YOLOv5Head',
head_module=dict(
type='YOLOv5HeadModule',
num_classes=num_classes,
in_channels=[256, 512, 1024],
widen_factor=widen_factor,
featmap_strides=strides,
num_base_priors=3),
prior_generator=dict(
type='mmdet.YOLOAnchorGenerator',
base_sizes=anchors,
strides=strides),
# scaled based on number of detection layers
loss_cls=dict(
type='mmdet.CrossEntropyLoss',
use_sigmoid=True,
reduction='mean',
loss_weight=loss_cls_weight *
(num_classes / 80 * 3 / num_det_layers)),
loss_bbox=dict(
type='IoULoss',
iou_mode='ciou',
bbox_format='xywh',
eps=1e-7,
reduction='mean',
loss_weight=loss_bbox_weight * (3 / num_det_layers),
return_iou=True),
loss_obj=dict(
type='mmdet.CrossEntropyLoss',
use_sigmoid=True,
reduction='mean',
loss_weight=loss_obj_weight *
((img_scale[0] / 640)**2 * 3 / num_det_layers)),
3.3.1 loss 类更改
loss_cls:
代码语言:javascript复制loss_cls=dict(
_delete_=True,
type='mmdet.SmoothL1Loss',
beta=0.5,
reduction='sum',
loss_weight=1.0),
替换其他同理。
3.3.2 head_module 更改
代码语言:javascript复制 head_module=dict(
type='YOLOv7HeadModule',
num_classes=num_classes,
in_channels=[256, 512, 1024],
featmap_strides=strides,
num_base_priors=3)
总结
本文综合网络上多处 MMYOLO 文档和个人使用经验,包括从安装到训练再到简单的模型网络修改方法,希望给大家带来帮助。