MMDetection微调RTMDet模型针对实例分割任务

前言

  • 在以前的博客中有介绍MMdetection框架除了适用于目标检测任务外,还可以做实例分割任务
  • MMdetection框架的官方项目中关于实例分割任务的教程文件因为框架版本更新的问题,实际运行过程中会报错cannot import name 'build_dataset' from 'mmdet.datasets',所以本文主要是对新版本的框架做实例分割的教程
  • 因为官方的balloon数据集数据量太小,这里使用kaggle平台上的Motorcycle Night Ride数据集,数据地址
  • 下面所有代码均在kaggle平台,GPUP100环境下运行。

环境配置

import IPython.display as display

!pip install openmim
!mim install mmengine==0.7.2
!pip install -q /kaggle/input/frozen-packages-mmdetection/mmcv-2.0.1-cp310-cp310-linux_x86_64.whl

!rm -rf mmdetection
!git clone https://github.com/open-mmlab/mmdetection.git
!git clone https://github.com/open-mmlab/mmyolo.git
%cd mmdetection

!mkdir ./data
%pip install -e .

!pip install wandb
display.clear_output()
  • 由于我们使用的是wandb平台对训练过程进行可视化,所以还需要先登录wandb平台。
import wandb
wandb.login()

预训练模型推理

  • 我们先下载需要微调的RTMDet-l模型权重,然后在测试图片上进行推理,顺便也可以检查环境是否齐全。
  • 关于模型型号,可以在项目文件configs/rtmdet下找到。但要注意的是,在Readme.md文档中有两个表格分别是Object DetectionInstance Segmentation,由于是实例分割任务,我们应该在`Instance Segmentation``表格中查找模型型号
    在这里插入图片描述
!mkdir ./checkpoints
!mim download mmdet --config rtmdet-ins_l_8xb32-300e_coco --dest ./checkpoints
  • 模型在测试图片上的推理
import mmcv
import mmengine
from mmdet.apis import init_detector, inference_detector
from mmdet.utils import register_all_modules

config_file = 'configs/rtmdet/rtmdet-ins_l_8xb32-300e_coco.py'

checkpoint_file = 'checkpoints/rtmdet-ins_l_8xb32-300e_coco_20221124_103237-78d1d652.pth'

register_all_modules()

model = init_detector(config_file, checkpoint_file, device='cuda:0')

image = mmcv.imread('demo/demo.jpg',channel_order='rgb')
result = inference_detector(model, image)

from mmdet.registry import VISUALIZERS
visualizer = VISUALIZERS.build(model.cfg.visualizer)
visualizer.dataset_meta = model.dataset_meta

visualizer.add_datasample('result',image,data_sample=result,draw_gt = None,wait_time=0,)

display.clear_output()
visualizer.show()

请添加图片描述

数据探索与可视化

  • 由于数据集名字太长,这里将图片文件夹和标注文件复制到项目文件夹中的data子文件夹中
import os
import shutil

def copy_files(src_folder, dest_folder):
    # 确保目标文件夹存在
    os.makedirs(dest_folder, exist_ok=True)

    # 遍历源文件夹中的所有内容
    for root, _, files in os.walk(src_folder):
        for file in files:
            # 拼接源文件的完整路径
            src_file_path = os.path.join(root, file)

            # 拼接目标文件的完整路径
            dest_file_path = os.path.join(dest_folder, os.path.relpath(src_file_path, src_folder))

            # 确保目标文件的文件夹存在
            os.makedirs(os.path.dirname(dest_file_path), exist_ok=True)

            # 复制文件
            shutil.copy(src_file_path, dest_file_path)

source_folder = '/kaggle/input/motorcycle-night-ride-semantic-segmentation/www.acmeai.tech ODataset 1 - Motorcycle Night Ride Dataset'
destination_folder = './data'
copy_files(source_folder, destination_folder)
  • 数据可视化,由于数据集中已经提供了可视化后的图,我们将原图和标注后的图放在一起对比
import mmcv
import matplotlib.pyplot as plt

img_og = mmcv.imread('data/images/Screenshot (446).png')
img_fuse = mmcv.imread('data/images/Screenshot (446).png___fuse.png')

fig, axes = plt.subplots(1, 2, figsize=(15, 10))
axes[0].imshow(mmcv.bgr2rgb(img_og))
axes[0].set_title('Original Image')
axes[0].axis('off')

axes[1].imshow(mmcv.bgr2rgb(img_fuse))
axes[1].set_title('mask Image')
axes[1].axis('off')
plt.show()

请添加图片描述

  • 使用pycocotools库读取标注文件,将类别信息输出
from pycocotools.coco import COCO

# 初始化COCO对象
coco = COCO('data/COCO_motorcycle (pixel).json')

# 获取所有的类别标签和对应的类别ID
categories = coco.loadCats(coco.getCatIds())
category_id_to_name = {cat['id']: cat['name'] for cat in categories}

display.clear_output()
# 打印所有类别ID和对应的类别名称
for category_id, category_name in category_id_to_name.items():
    print(f"Category ID: {category_id}, Category Name: {category_name}")
  • 输出:
Category ID: 1329681, Category Name: Rider
Category ID: 1323885, Category Name: My bike
Category ID: 1323884, Category Name: Moveable
Category ID: 1323882, Category Name: Lane Mark
Category ID: 1323881, Category Name: Road
Category ID: 1323880, Category Name: Undrivable
  • 可以看到类别一共有6种,分别为:Rider, My bike, Moveable, Lane Mark, Road, Undrivable

修改配置文件

  • 有关配置文件的详细解释,我在博客MMDetection框架训练、测试全流程利用MMSegmentation微调Mask2Former模型MMYOLO框架标注、训练、测试全流程(补充篇)都有很详细的说明,这里也不多讲了
  • 主要修改的是预训练权重路径,图片路径,标注文件路径,batch_sizeepochs,学习率缩放,类别数量,多卡变单卡(SyncBN --> BN),类别标签与调色板
  • 要非常注意类别数量和类别标签与调色板这两个参数,否则会报错class EpochBasedTrainLoop in mmengine/runner/loops.py: class CocoDataset in mmdet/datasets/coco.py: need at least one array to concatenate,这类错误是非常非常常见的,大家一定要检查类别数和标签信息是否正确
from mmengine import Config
cfg = Config.fromfile('./configs/rtmdet/rtmdet-ins_l_8xb32-300e_coco.py')
from mmengine.runner import set_random_seed

cfg.load_from = 'checkpoints/rtmdet-ins_l_8xb32-300e_coco_20221124_103237-78d1d652.pth'

cfg.work_dir = './work_dir'

cfg.max_epochs = 100
cfg.stage2_num_epochs = 7
cfg.train_dataloader.batch_size = 4
cfg.train_dataloader.num_workers = 2

scale_factor = cfg.train_dataloader.batch_size / (8 * 32)

cfg.base_lr *= scale_factor
cfg.optim_wrapper.optimizer.lr = cfg.base_lr

# cfg.model.backbone.frozen_stages = 4
cfg.model.bbox_head.num_classes = 6

# 单卡训练时,需要把 SyncBN 改成 BN
cfg.norm_cfg = dict(type='BN', requires_grad=True)

cfg.metainfo = {
    'classes': ('Rider', 'My bike', 'Moveable', 'Lane Mark', 'Road', 'Undrivable', ),
    'palette': [
        (141, 211, 197),(255, 255, 179),(190, 186, 219),(245, 132, 109),(127, 179, 209),(251, 180, 97),
    ]
}

cfg.data_root = './data'

cfg.train_dataloader.dataset.ann_file = 'COCO_motorcycle (pixel).json'
cfg.train_dataloader.dataset.data_root = cfg.data_root
cfg.train_dataloader.dataset.data_prefix.img = 'images/'
cfg.train_dataloader.dataset.metainfo = cfg.metainfo


cfg.val_dataloader.dataset.ann_file = 'COCO_motorcycle (pixel).json'
cfg.val_dataloader.dataset.data_root = cfg.data_root
cfg.val_dataloader.dataset.data_prefix.img = 'images/'
cfg.val_dataloader.dataset.metainfo = cfg.metainfo


cfg.test_dataloader = cfg.val_dataloader

cfg.val_evaluator.ann_file = cfg.data_root+'/'+'COCO_motorcycle (pixel).json'
cfg.val_evaluator.metric = ['segm']

cfg.test_evaluator = cfg.val_evaluator

cfg.default_hooks.checkpoint = dict(type='CheckpointHook', interval=10, max_keep_ckpts=2, save_best='auto')
cfg.default_hooks.logger.interval = 20

cfg.custom_hooks[1].switch_epoch = 300 - cfg.stage2_num_epochs

cfg.train_cfg.max_epochs = cfg.max_epochs
cfg.train_cfg.val_begin = 20
cfg.train_cfg.val_interval = 2
cfg.train_cfg.dynamic_intervals = [(300 - cfg.stage2_num_epochs, 1)]

# cfg.train_dataloader.dataset = dict(dict(type='RepeatDataset',times=5,dataset = cfg.train_dataloader.dataset))

cfg.param_scheduler[0].end = 100

cfg.param_scheduler[1].eta_min = cfg.base_lr * 0.05
cfg.param_scheduler[1].begin = cfg.max_epochs // 2
cfg.param_scheduler[1].end = cfg.max_epochs
cfg.param_scheduler[1].T_max = cfg.max_epochs //2

set_random_seed(0, deterministic=False)

cfg.visualizer.vis_backends.append({"type":'WandbVisBackend'})

#------------------------------------------------------
config=f'./configs/rtmdet/rtmdet-ins_l_1xb4-100e_motorcycle.py'
with open(config, 'w') as f:
    f.write(cfg.pretty_text)
  • 开始训练
!python tools/train.py {config}
  • 展示模型效果最好的指标数值
 Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.561
 Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=100 ] = 0.758
 Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=100 ] = 0.614
 Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.017
 Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.195
 Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.633
 Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=  1 ] = 0.543
 Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets= 10 ] = 0.645
 Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.649
 Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.036
 Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.246
 Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.721
07/23 19:31:52 - mmengine - INFO - segm_mAP_copypaste: 0.561 0.758 0.614 0.017 0.195 0.633
07/23 19:31:52 - mmengine - INFO - Epoch(val) [98][40/40]    coco/segm_mAP: 0.5610  coco/segm_mAP_50: 0.7580  coco/segm_mAP_75: 0.6140  coco/segm_mAP_s: 0.0170  coco/segm_mAP_m: 0.1950  coco/segm_mAP_l: 0.6330  data_time: 0.0491  time: 3.1246

可视化训练过程

  • 我们可以登录wandb平台查看训练过程中的指标变化

在这里插入图片描述
在这里插入图片描述

  • 可以看到segm_mAP值是还在上升的状态,因为时间关系,我只跑了100个epoch,如果大家自己尝试可以跑300试试,估计效果会更好。

在测试图片上推理

  • 在训练完成后,我们加载表现最好的模型,并对测试图片进行推理
from mmengine.visualization import Visualizer
import mmcv
from mmdet.apis import init_detector, inference_detector
import glob

img = mmcv.imread('data/images/Screenshot (446).png',channel_order='rgb')

checkpoint_file = glob.glob('./work_dir/best_coco_segm_mAP*.pth')[0]

model = init_detector(cfg, checkpoint_file, device='cuda:0')

new_result = inference_detector(model, img)

visualizer_now = Visualizer.get_current_instance()

visualizer_now.dataset_meta = model.dataset_meta

visualizer_now.add_datasample('new_result', img, data_sample=new_result, draw_gt=False, wait_time=0, out_file=None, pred_score_thr=0.5)

visualizer_now.show()

请添加图片描述

常见问题排查

  • 官方写了一个文档,用于一些非常常见的问题排查,我将地址放置在此处
  • 我在运行的过程中遇到的最频繁的错误就是valueerror-need-at-least-one-array-to-concatenate,上面的官方问题排查手册中也进行了说明。要检查种类数和标签及调色板,但是我检查以后发现没有上述问题,最后发现还有一个可能会引发此错误的因素
  • 由于框架的更新,标签及调色板的写法发生了改变,错误的写法是:
cfg.metainfo = {
    'CLASSES': ('Rider', 'My bike', 'Moveable', 'Lane Mark', 'Road', 'Undrivable', ),
    'PALETTE': [
        (141, 211, 197),(255, 255, 179),(190, 186, 219),(245, 132, 109),(127, 179, 209),(251, 180, 97),
    ]
}
  • 大写的CLASSESPALETTE在新版本中已经不适用了,要换成小写,就不会保存了
cfg.metainfo = {
    'classes': ('Rider', 'My bike', 'Moveable', 'Lane Mark', 'Road', 'Undrivable', ),
    'palette': [
        (141, 211, 197),(255, 255, 179),(190, 186, 219),(245, 132, 109),(127, 179, 209),(251, 180, 97),
    ]
}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

羽星_s

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值