【目标检测/实例分割】Mask R-CNN简介与Swin Transformer实践测试
前言
之前在看Swin Transformer的时候,发现该算法在目标检测、实例分割等各种视觉任务上屠榜,于是想来实践测试一下。
官方地址:https://github.com/SwinTransformer/Swin-Transformer-Object-Detection
查看源码,发现Swin Transformer并不是作为一套单独的算法进行使用,而是嵌入在mask_rcnn算法中,作为该算法的backbone。(当然,也可以使用别的算法,只是该仓库目前仅实现了mask_rcnn和cascade_mask_rcnn)
 因此,有必要先对Mask R-CNN算法做一个了解。
Mask R-CNN简介
Mask R-CNN是何凯明大神继Faster-RCNN后的又一力作,在Fasker R-CNN的基础上,集成了物体检测和实例分割两大功能。
论文链接:https://arxiv.org/pdf/1703.06870.pdf
 中文翻译:https://blog.csdn.net/weixin_43066351/article/details/106613654
这里顺便补充一个知识点:实例分割和语义分割的区别

 如图所示,中间那幅图是语义分割,只需要把不同的类别和背景分割出来即可,右侧图是实例分割,不仅需要把类别分割出来,还需要把每个个体分割出来,因此,实例分割的难度比语义分割更高。
整体结构
Mask R-CNN添加一个分支来预测每个关注区域(RoI)上的分割蒙版,从而扩展了Faster R-CNN,与现有的用于分类和边界框回归的分支并行,整体结构如下图所示:
 
RoIAlign替代RoIPool
Mask R-CNN和Fast R-CNN一样,均属于两阶段的目标检测,第一阶段是从原图中提取感兴趣区域(Rol)。
 在Fast RCNN中,采用了RoIPool,能够通过卷积和池化将原图映射成固定尺寸的特征图,不过在处理过程中,进行了两次量化,造成了精度损失。
 因此在Mask R-CNN中,使用了RoIAlign来替代RoIPool,核心思想是通过双线性插值法来避免两次量化产生的误差。
下表展示了替代之后的效果,左边这栏是实例分割的mAP,右边这栏(bb)是目标检测的mAP,可以看到替代之后对于结果的提升还是非常显著的。
 
这部分具体细节可以参考这两篇文章:
检测头结构
第二阶段是从Rol处理之后的特征图进行检测/分割,作者在这部分讨论了两种不同的检测头结构,如下图所示:
 下图左边是不带FPN结构的Mask分支,右侧是带有FPN结构的Mask分支(灰色部分为原Faster R-CNN预测box, class信息的分支,白色部分为Mask分支)

 对此,作者对不同的网络结构进行了实验比较,结果如下图所示:

 可以看到ResNeXt-101-FPN这个结构起到最好的效果。
结果展示
最后作者展示了在不同算法在COCO数据集上的比较结果:
 
 进一步拓展:可以将目标检测部分替换成人体关键点检测,结果如下图所示:

优化过程解读
最后,作者在附录中记录了对于COCO数据集的调参以及优化过程,感觉对于后续的竞赛会有帮助,研究一下:

-  
Updated baseline
这一步就是对一些超参数进行修改,包括延长迭代次数到18万轮;从12万轮到16万轮学习率降低10点;将NMS阈值从0.3调整到0.5
这一步mask AP提升0.3,box AP提升0.9 -  
End-to-end training
之前的训练策略是先单独对一阶段的RPN网络进行训练,之后再对二阶段的Mask R-CNN检测头进行训练,这一步端到端的优化就是将两者结合起来一起训练
这一步mask AP提升0.6,box AP提升1.2 -  
ImageNet-5k pre-training
这一步很简单,就是扩充数据集,引入ImageNet-5k数据集进行预训练
这一步mask AP提升1.0,box AP提升1.0 -  
Train-time augmentation
这一步就是在训练的时候,对原始数据进行数据增强,这里作者采用的数据增强策略是随机在[640, 800]像素范围内进行缩放,并且将迭代次数增加到26万轮
这一步mask AP提升0.6,box AP提升0.8 -  
Deeper
deeper就是将使用更深的网络结构,这里作者101-ResNeXt升级为152-ResNeXt
这一步mask AP提升0.5,box AP提升0.6 -  
Non-local
Non-local类似残差结构一样,可以在不改变原有网络结构的基础上,作为一种通用的结构插入,具体我没仔细了解,详细内容可以参考这篇论文笔记【论文笔记】Non-local Neural Networks
这一步mask AP提升0.6,box AP提升0.9 -  
Test-time augmentation
这一步就是在测试时对测试数据也进行数据增强,具体的策略包括每间隔100张图片,对数据随机在[400, 1200]像素范围内进行缩放,并进行水平翻转
这一步mask AP提升1.5,box AP提升2.3 
总结:从上面的实验过程不难看出,扩充数据集/对数据增强提升效果比修改网络结构提升得更为明显,这和我以往的实验经验一致。同时,对测试集进行增强检测,提升的效果最多,个人猜测是因为对测试集进行数据增强之后,测试集更贴近训练集(训练集也进行了数据增强),因此目标更容易被检测。
实践测试
下面又到了快乐的实践环节。
官方的代码实际是用mmdection框架进行实现的,而且用的版本比较老,在mmdet/__init__.py文件中,指明了mmcv的版本范围为1.2.4-1.4.0,而在mmdection最新仓库中,mmcv的最高版本已支持1.7.0。
将其版本手动扩大,尝试运行,结果报错:
TypeError: MaskRCNN: SwinTransformer: init() got an unexpected keyword argument ‘ape’
查询到官方的issues#92,发现也有人遇到了同样的问题,采用python setup.py develop等方式,并无法解决该问题,遂放弃,使用最新版本的mmdection进行实践。
mmdection地址:https://github.com/open-mmlab/mmdetection
mmdection的简介和安装可以参考我之前的博文【目标检测】MMDetection的安装与基础使用,mmcv和mmdection的版本配套比较麻烦,建议使用python3.7版本。
安装配置好之后,在configs/swin下可以看到官方实现的几个mask_rcnn算法,这里采用mask_rcnn_swin-s-p4-w7_fpn_fp16_ms-crop-3x_coco.py

之后建立一个checkpoint文件夹,下载mask_rcnn_swin-s-p4-w7_fpn_fp16_ms-crop-3x_coco_20210903_104808-b92c91f1.pth和swin_small_patch4_window7_224.pth两个模型权重,下载方式见官方的ModelZoo。

检测图片
在源码的demo文件夹中,有检测图片的程序,这里为了运行方便,在根目录下新建一个infer.py
from mmdet.apis import init_detector, inference_detector, show_result_pyplot
import warnings
warnings.filterwarnings("ignore")
config_file = 'configs/swin/mask_rcnn_swin-s-p4-w7_fpn_fp16_ms-crop-3x_coco.py'
checkpoint_file = 'checkpoint/mask_rcnn_swin-s-p4-w7_fpn_fp16_ms-crop-3x_coco_20210903_104808-b92c91f1.pth'
device = 'cuda:0'
# 初始化检测器
model = init_detector(config_file, checkpoint_file, device=device)
# 推理演示图像
img = 'demo/demo.jpg'
result = inference_detector(model, img)
show_result_pyplot(model, img, result, score_thr=0.3, out_file='1.jpg')
  
  
 
 - 1
 - 2
 - 3
 - 4
 - 5
 - 6
 - 7
 - 8
 - 9
 - 10
 - 11
 - 12
 - 13
 - 14
 - 15
 
这里的out_file指定输出的图片名,检测效果如下图所示:

检测视频
检测视频和检测图片类似,在demo中有对应的代码,这里同样在根目录下新建一个infer_video.py
import argparse
import os
import cv2 as cv
import torch
from mmdet.apis import inference_detector, init_detector
import warnings
import time
warnings.filterwarnings("ignore")
file_path = __file__
dir_path = os.path.dirname(file_path)
output_video_path = os.path.join(dir_path, 'result.mp4')
def parse_args():
    parser = argparse.ArgumentParser(description='MMDetection webcam demo')
    parser.add_argument('config', help='test config file path')
    parser.add_argument('checkpoint', help='checkpoint file')
    parser.add_argument(
        '--device', type=str, default='cuda:0', help='CPU/CUDA device option')
    parser.add_argument(
        '--camera-id', type=int, default=0, help='camera device id')
    parser.add_argument(
        '--score-thr', type=float, default=0.5, help='bbox score threshold')
    parser.add_argument(
        '--file', type=str, help='where the test video path')
    parser.add_argument(
        '--out', type=str, default=output_video_path, help='the out put video path')
    args = parser.parse_args()
    return args
def main():
    args = parse_args()
    if not args.file:
        print('No target file!')
        exit(0)
    device = torch.device(args.device)
    print('device:', args.device)
    model = init_detector(args.config, args.checkpoint, device=device)
    camera = cv.VideoCapture(args.file)
    camera_width = int(camera.get(cv.CAP_PROP_FRAME_WIDTH))
    camera_hight = int(camera.get(cv.CAP_PROP_FRAME_HEIGHT))
    # print(camera_hight, camera_width)
    fps = camera.get(cv.CAP_PROP_FPS)
    video_writer = cv.VideoWriter(args.out, cv.VideoWriter_fourcc(*'mp4v'),
                                  fps, (camera_width, camera_hight))
    count = 0
    tt = 0
    while True:
        torch.cuda.empty_cache()
        ret_val, img = camera.read()
        if ret_val:
            if count < 0:
                count += 1
                print("Write {} in result Successfuly!".format(count))
                continue
            result = inference_detector(model, img)
            frame = model.show_result(img, result, score_thr=args.score_thr, wait_time=1, show=False)
            # cv.imshow('frame', frame)
            if len(frame) >= 1:
                # 添加帧率检测
                # cv.putText(frame, "FPS:{:.1f}".format(1. / (time.time() - tt)), (20, 50), cv.FONT_HERSHEY_SIMPLEX, 2,
                #            (235, 0, 0), 4)
                # tt = time.time()
                video_writer.write(frame)
                count += 1
                print("Write {} in result Successfuly!".format(count))
        else:
            # print('Load fail!')
            break
    camera.release()
    video_writer.release()
    # cv.destroyWindow()
if __name__ == '__main__':
    main()
  
  
 
 - 1
 - 2
 - 3
 - 4
 - 5
 - 6
 - 7
 - 8
 - 9
 - 10
 - 11
 - 12
 - 13
 - 14
 - 15
 - 16
 - 17
 - 18
 - 19
 - 20
 - 21
 - 22
 - 23
 - 24
 - 25
 - 26
 - 27
 - 28
 - 29
 - 30
 - 31
 - 32
 - 33
 - 34
 - 35
 - 36
 - 37
 - 38
 - 39
 - 40
 - 41
 - 42
 - 43
 - 44
 - 45
 - 46
 - 47
 - 48
 - 49
 - 50
 - 51
 - 52
 - 53
 - 54
 - 55
 - 56
 - 57
 - 58
 - 59
 - 60
 - 61
 - 62
 - 63
 - 64
 - 65
 - 66
 - 67
 - 68
 - 69
 - 70
 - 71
 - 72
 - 73
 - 74
 - 75
 - 76
 - 77
 - 78
 - 79
 - 80
 - 81
 - 82
 - 83
 - 84
 - 85
 - 86
 - 87
 - 88
 - 89
 - 90
 - 91
 
终端输入:
python infer_video.py configs/swin/mask_rcnn_swin-s-p4-w7_fpn_fp16_ms-crop-3x_coco.py ch
eckpoint/mask_rcnn_swin-s-p4-w7_fpn_fp16_ms-crop-3x_coco_20210903_104808-b92c91f1.pth --file demo/demo.mp4
  
  
 
 - 1
 - 2
 
等待片刻,即可在根目录下生成视频结果'result.mp4'
这里我使用ResNet-50,ResNet-101,Swin-S,Swin-T四种不同的backbone,对比效果如下:
Mask R-CNN遇上ikun
实测发现,对于这段30帧的视频,Mask R-CNN的帧率基本不超过3fps(RTX 2060单显卡推理),作为二阶段的算法,推理速度还是比较慢的。
视频中,有几帧会把kun的白带识别为手提包,因为是直接采用coco数据集上训练的模型进行检测,该特征和包的提手很像。
模型测试
官方下载的模型是在coco数据集进行训练的,因此使用coco数据集可以直接进行模型测试。
 首先需要下载coco数据集:https://pan.baidu.com/s/1D_J3qGfQZWhaTxMvW5LzBA?pwd=8888
 下载好之后,位置结构如图所示:

 然后在configs/_base_/datasets/coco_instance.py中,修改数据集的路径:

修改好之后,运行tools/test.py即可进行模型测试:
终端输入:
python tools/test.py  configs/swin/mask_rcnn_swin-t-p4-w7_fpn_1x_coco.py checkpoint/mask_rcnn_swin-t-p4-w7_fpn_1x_coco_20210902_120937-9d6b7cfa.pth --eval bbox segm
  
  
 
 - 1
 
结果输出:
 注:bbox是目标检测的相关指标,segm是实例分割的相关指标
Evaluating bbox...
Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.427
Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=1000 ] = 0.652
Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=1000 ] = 0.468
Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.265
Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.459
Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.566
Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.559
Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=300 ] = 0.559
Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=1000 ] = 0.559
Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.383
Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.596
Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.703
Evaluating segm...
Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.393
Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=1000 ] = 0.622
Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=1000 ] = 0.422
Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.205
Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.418
Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.578
Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.519
Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=300 ] = 0.519
Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=1000 ] = 0.519
Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=1000 ] = 0.345
Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=1000 ] = 0.555
Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=1000 ] = 0.675
OrderedDict([('bbox_mAP', 0.427), ('bbox_mAP_50', 0.652), ('bbox_mAP_75', 0.468), ('bbox_mAP_s', 0.265), ('bbox_mAP_m', 0.459), ('bb
ox_mAP_l', 0.566), ('bbox_mAP_copypaste', '0.427 0.652 0.468 0.265 0.459 0.566'), ('segm_mAP', 0.393), ('segm_mAP_50', 0.622), ('seg
m_mAP_75', 0.422), ('segm_mAP_s', 0.205), ('segm_mAP_m', 0.418), ('segm_mAP_l', 0.578), ('segm_mAP_copypaste', '0.393 0.622 0.422 0.
205 0.418 0.578')])
  
  
 
 - 1
 - 2
 - 3
 - 4
 - 5
 - 6
 - 7
 - 8
 - 9
 - 10
 - 11
 - 12
 - 13
 - 14
 - 15
 - 16
 - 17
 - 18
 - 19
 - 20
 - 21
 - 22
 - 23
 - 24
 - 25
 - 26
 - 27
 - 28
 - 29
 - 30
 - 31
 - 32
 
模型训练
下面同样尝试利用coco数据集进行训练,数据集路径在上一节已经修改完毕,这一节主要需要修改训练参数。
我使用的是单卡GPU,显存较小,因此将configs/_base_/default_runtime.py中的base_batch_size改成1

 然后修改configs/_base_/datasets/coco_instance.py文件中的samples_per_gpu和workers_per_gpu,同样修改为1

 最后运行tools/train.py即可开始训练:
终端输入:
python tools/train.py  configs/swin/mask_rcnn_swin-t-p4-w7_fpn_1x_coco.py
  
  
 
 - 1
 
训练时会显示预估剩余时间,单卡2060在coco数据集上实测训练mask_rcnn_swin-t模型一轮需要花费近一天时间,这个模型参数量着实恐怖。
代码备份
包含完整代码/下载的权重:
 https://pan.baidu.com/s/19zpIljdlkJG-BJD7w2NzCA?pwd=8888
文章来源: zstar.blog.csdn.net,作者:zstar-_,版权归原作者所有,如需转载,请联系作者。
原文链接:zstar.blog.csdn.net/article/details/126992513
- 点赞
 - 收藏
 - 关注作者
 
            
           
评论(0)