【OpenMMLab AI训练营第二期】Class7:MMDetection代码课

本文详细介绍了MMDetection的版本更新,如何配置环境,安装依赖,并提供了数据集准备和可视化步骤。接着,文章展示了模型训练的过程,以及使用Grad-CAM进行特征图和注意力区域的可视化分析,帮助理解模型的决策过程。
摘要由CSDN通过智能技术生成

MMDetection版本变化

MMDetection3.0通过更细粒度的模块解耦,进一步拆解出了数据、模型、评测等抽象,并对这些接口进行了统一设计。通过对MMEngine和MMCV的重新适配,使得MMDetection的速度和精度得到提高。

环境配置

# Check nvcc version
!nvcc -V
# Check GCC version
!gcc --version

# # 安装 mmengine 和 mmcv 依赖
# # 为了防止后续版本变更导致的代码无法运行,我们暂时锁死版本
# !pip install -U "openmim==0.3.7"
# !mim install "mmengine==0.7.1"
# !mim install "mmcv==2.0.0"

# # Install mmdetection
# !rm -rf mmdetection
# # 为了防止后续更新导致的可能无法运行,特意新建了 tutorials 分支

# 之前的课程配置过内容,所以就不重新配置环境了,重新clone一下代码库
!git clone -b tutorials https://github.com/open-mmlab/mmdetection.git
%cd mmdetection

%pip install -e .

打印环境信息

from mmengine.utils import get_git_hash
from mmengine.utils.dl_utils import collect_env as collect_base_env

import mmdet

# 环境信息收集和打印
def collect_env():
    """Collect the information of the running environments."""
    env_info = collect_base_env()
    env_info['MMDetection'] = f'{mmdet.__version__}+{get_git_hash()[:7]}'
    return env_info


if __name__ == '__main__':
    for name, val in collect_env().items():
        print(f'{name}: {val}')

准备数据集

! rm -rf cat_dataset*
! wget https://download.openmmlab.com/mmyolo/data/cat_dataset.zip
! unzip cat_dataset.zip -d cat_dataset && rm cat_dataset.zip 

下载好的数据集已经是COCO格式的了,可以对数据进行可视化

import os
import matplotlib.pyplot as plt
from PIL import Image

%matplotlib inline
%config InlineBackend.figure_format = 'retina'

original_images = []
images = []
texts = []
plt.figure(figsize=(16, 5))

image_paths= [filename for filename in os.listdir('cat_dataset/images')][:8]

for i,filename in enumerate(image_paths):
    name = os.path.splitext(filename)[0]

    image = Image.open('cat_dataset/images/'+filename).convert("RGB")
  
    plt.subplot(2, 4, i+1)
    plt.imshow(image)
    plt.title(f"{filename}")
    plt.xticks([])
    plt.yticks([])

plt.tight_layout()

得到以下结果
在这里插入图片描述

from pycocotools.coco import COCO
from PIL import Image
import numpy as np
import os.path as osp
import matplotlib.pyplot as plt
from matplotlib.collections import PatchCollection
from matplotlib.patches import Polygon

def apply_exif_orientation(image):
    _EXIF_ORIENT = 274
    if not hasattr(image, 'getexif'):
        return image

    try:
        exif = image.getexif()
    except Exception:
        exif = None

    if exif is None:
        return image

    orientation = exif.get(_EXIF_ORIENT)

    method = {
        2: Image.FLIP_LEFT_RIGHT,
        3: Image.ROTATE_180,
        4: Image.FLIP_TOP_BOTTOM,
        5: Image.TRANSPOSE,
        6: Image.ROTATE_270,
        7: Image.TRANSVERSE,
        8: Image.ROTATE_90,
    }.get(orientation)
    if method is not None:
        return image.transpose(method)
    return image


def show_bbox_only(coco, anns, show_label_bbox=True, is_filling=True):
    """Show bounding box of annotations Only."""
    if len(anns) == 0:
        return

    ax = plt.gca()
    ax.set_autoscale_on(False)

    image2color = dict()
    for cat in coco.getCatIds():
        image2color[cat] = (np.random.random((1, 3)) * 0.7 + 0.3).tolist()[0]

    polygons = []
    colors = []

    for ann in anns:
        color = image2color[ann['category_id']]
        bbox_x, bbox_y, bbox_w, bbox_h = ann['bbox']
        poly = [[bbox_x, bbox_y], [bbox_x, bbox_y + bbox_h],
                [bbox_x + bbox_w, bbox_y + bbox_h], [bbox_x + bbox_w, bbox_y]]
        polygons.append(Polygon(np.array(poly).reshape((4, 2))))
        colors.append(color)

        if show_label_bbox:
            label_bbox = dict(facecolor=color)
        else:
            label_bbox = None

        ax.text(
            bbox_x,
            bbox_y,
            '%s' % (coco.loadCats(ann['category_id'])[0]['name']),
            color='white',
            bbox=label_bbox)

    if is_filling:
        p = PatchCollection(
            polygons, facecolor=colors, linewidths=0, alpha=0.4)
        ax.add_collection(p)
    p = PatchCollection(
        polygons, facecolor='none', edgecolors=colors, linewidths=2)
    ax.add_collection(p)

    
coco = COCO('./cat_dataset/annotations/test.json')
image_ids = coco.getImgIds()
np.random.shuffle(image_ids)

plt.figure(figsize=(16, 5))

# 只可视化 8 张图片
for i in range(8):
    image_data = coco.loadImgs(image_ids[i])[0]
    image_path = osp.join('./cat_dataset/images/',image_data['file_name'])
    annotation_ids = coco.getAnnIds(
            imgIds=image_data['id'], catIds=[], iscrowd=0)
    annotations = coco.loadAnns(annotation_ids)
    
    ax = plt.subplot(2, 4, i+1)
    image = Image.open(image_path).convert("RGB")
    
    # 这行代码很关键,否则可能图片和标签对不上
    image=apply_exif_orientation(image)
    
    ax.imshow(image)
    
    show_bbox_only(coco, annotations)
    
    plt.title(f"{filename}")
    plt.xticks([])
    plt.yticks([])
        
plt.tight_layout()    

在这里插入图片描述

修改配置文件

主要是修改类别数量、学习率等等。
修改配置文件需要注意几个问题:
自定义数据集中最重要的是 metainfo 字段,用户在配置完成后要记得将其传给 dataset,否则不生效(有些用户在自定义数据集时候喜欢去 直接修改 coco.py 源码,这个是强烈不推荐的做法,正确做法是配置 metainfo 并传给 dataset)
如果用户 metainfo 配置不正确,通常会出现几种情况:(1) 出现 num_classes 不匹配错误 (2) loss_bbox 始终为 0 (3) 出现训练后评估结果为空等典型情况
MMDetection 提供的学习率大部分都是基于 8 卡,如果你的总 bs 不同,一定要记得缩放学习率,否则有些算法很容易出现 NAN。

在配置完成之后,可以先进行可视化来测试整个数据流,从而判断这个配置文件是否是有效的。


from mmdet.registry import DATASETS, VISUALIZERS
from mmengine.config import Config
from mmengine.registry import init_default_scope
import matplotlib.pyplot as plt
import os.path as osp

cfg = Config.fromfile('./cat_dataset/cat_rtmdet_config.py')

init_default_scope(cfg.get('default_scope', 'mmdet'))

dataset = DATASETS.build(cfg.train_dataloader.dataset)
visualizer = VISUALIZERS.build(cfg.visualizer)
visualizer.dataset_meta = dataset.metainfo

plt.figure(figsize=(16, 5))

# 只可视化前 8 张图片
for i in range(8):
   item=dataset[i]

   img = item['inputs'].permute(1, 2, 0).numpy()
   data_sample = item['data_samples'].numpy()
   gt_instances = data_sample.gt_instances
   img_path = osp.basename(item['data_samples'].img_path)

   gt_bboxes = gt_instances.get('bboxes', None)
   gt_instances.bboxes = gt_bboxes.tensor
   data_sample.gt_instances = gt_instances

   visualizer.add_datasample(
            osp.basename(img_path),
            img,
            data_sample,
            draw_pred=False,
            show=False)
   drawed_image=visualizer.get_image()

   plt.subplot(2, 4, i+1)
   plt.imshow(drawed_image[..., [2, 1, 0]])
   plt.title(f"{osp.basename(img_path)}")
   plt.xticks([])
   plt.yticks([])

plt.tight_layout()    

得到以下结果
在这里插入图片描述
这是通过马赛克数据增强后的可视化结果。

模型训练和测试推理

训练

python tools/train.py cat_dataset/cat_rtmdet_config.py

测试和推理

# 测试并保存结果
python tools/test.py ipynb/cat_dataset/cat_rtmdet_config.py work_dirs/cat_rtmdet_config/best_coco_bbox_mAP_epoch_30.pth --show-dir results

会在 work_dir/cat_rtmdet_config/当前时间戳/results/ 下生成测试图片,下面对前 8 张图片进行可视化。

import os
import matplotlib.pyplot as plt
from PIL import Image

%matplotlib inline

plt.figure(figsize=(20, 20))

# 你如果重新跑,这个时间戳是不一样的,需要自己修改
root_path='../work_dirs/cat_rtmdet_config/20230613_210036/results/'
image_paths= [filename for filename in os.listdir(root_path)][:4]

for i,filename in enumerate(image_paths):
    name = os.path.splitext(filename)[0]

    image = Image.open(root_path+filename).convert("RGB")
  
    plt.subplot(4, 1, i+1)
    plt.imshow(image)
    plt.title(f"{filename}")
    plt.xticks([])
    plt.yticks([])

plt.tight_layout()

在这里插入图片描述
左边是GT,右边是预测值。

推理过程

python demo/image_demo.py ipynb/cat_dataset/cat.jpg ipynb/cat_dataset/cat_rtmdet_config.py --weights work_dirs/cat_rtmdet_config/best_coco_bbox_mAP_epoch_30.pth 

在这里插入图片描述

可视化分析

这里需要用到MMYOLO中提供的脚本和功能,因为MMYOLO是基于MMDetection开发的,所以不存在迁移的问题。
可视化分析包括特征图可视化以及类似Grad CAM等可视化分析手段。

安装MMYOLO

git clone -b tutorials https://github.com/open-mmlab/mmyolo.git
mim install mmyolo
cd mmyolo
pip install -e .

特征图可视化

对上面的cat的图片进行可视化分析,但是这张图片分辨率太大,会导致程序崩溃,所以先进行缩放处理。

import cv2


img = cv2.imread('mmdetection/ipynb/cat_dataset/cat.jpg')

# print(img.shape)
h,w=img.shape[:2]
resized_img = cv2.resize(img, (640, 640))
cv2.imwrite('mmdetection/ipynb/cat_dataset/cat_resize.jpg', resized_img)

1.可视化backbone输出的3个通道

!python demo/featmap_vis_demo.py \
      ../mmdetection/ipynb/cat_dataset/cat_resize.jpg \
      ../mmdetection/ipynb/cat_dataset/cat_rtmdet_config.py \
      ../mmdetection/work_dirs/cat_rtmdet_config/best_coco_bbox_mAP_epoch_30.pth  \
      --target-layers backbone  \
      --channel-reduction squeeze_mean

在这里插入图片描述
上图中绘制的3个输出特征图对应大中小输出特征图。由于本次训练的backbone实际上没有参与训练,从上图可以看到,大物体cat是在小特征图进行预测,这符合目标检测分层检测思想。

2.可视化neck输出的3个通道

!python demo/featmap_vis_demo.py \
      ../mmdetection/ipynb/cat_dataset/cat_resize.jpg \
      ../mmdetection/ipynb/cat_dataset/cat_rtmdet_config.py \
      ../mmdetection/work_dirs/cat_rtmdet_config/best_coco_bbox_mAP_epoch_30.pth  \
      --target-layers neck  \
      --channel-reduction squeeze_mean

在这里插入图片描述
从上图可以看出,由于neck是参与训练的,并且FPN间的信息融合导致输出特征图更加聚集

Grad-Based CAM可视化

由于目标检测的特殊性,这里实际上可视化的并不是CAM而是Grad Box AM。使用前需要先安装grad-cam库

pip install grad-cam

1.查看neck输出的最小输出特征图的Grad CAM

!python demo/boxam_vis_demo.py \
      ../mmdetection/ipynb/cat_dataset/cat_resize.jpg \
      ../mmdetection/ipynb/cat_dataset/cat_rtmdet_config.py \
      ../mmdetection/work_dirs/cat_rtmdet_config/best_coco_bbox_mAP_epoch_30.pth  \
      --target-layers neck.out_convs[2]

在这里插入图片描述

2.查看neck输出的最大输出特征图的Grad CAM

!python demo/boxam_vis_demo.py \
      ../mmdetection/ipynb/cat_dataset/cat_resize.jpg \
      ../mmdetection/ipynb/cat_dataset/cat_rtmdet_config.py \
      ../mmdetection/work_dirs/cat_rtmdet_config/best_coco_bbox_mAP_epoch_30.pth  \
      --target-layers neck.out_convs[0]

在这里插入图片描述
这一层是空白的原因是,因为cat是大物体,而这一层是训练小目标的,所以没有对猫进行训练,所以梯度为0。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值