Faster RCNN目标检测模型原理及代码

Faster RCNN目标检测模型原理及代码

引言

1 原理介绍

1.1 VOC数据集介绍

​ 本项目两个模型均采用的是VOC数据集。它是一个广泛用于计算机视觉任务的图像数据集,其全称为PASCAL Visual Object Classes。该数据集最初由牛津大学的计算机视觉研究组(Visual Geometry Group)创建,并于2005年首次发布。VOC数据集的目标是提供用于对象识别和检测的标准测试基准,以推动计算机视觉研究和算法的发展。

​ 该数据集包含20个常见的物体类别,包括人、车辆、动物和家居物品等。这些类别分别是aeroplane、bicycle、bird、boat、bottle、bus、car、cat、chair、cow、diningtable、dog、horse、motorbike、person、pottedplant、sheep、sofa、train、tvmonitor。其结构如下图。 在这里插入图片描述

1.2 Faster R-CNN模型整体架构

​ 本文基于 Pytorch框架构建了在VOC数据集上检测的 Faster-RCNN 模型。Faster-RCNN 模型将特征抽取、目标识别、候选框生成和分类预测集中在同一个网络中,使得模型的训练速度和检测精度相较于 Fast-RCNN有较大提高。Faster-RCNN网络主 要包括3个部分,分别是前置特征提取网络(也称主干网络)、候选区域建议网络 (Region Proposal Network,RPN)、基于感兴趣区域池化(Rol Pooling)和全连接层的目标分类与位置回归网络。下图为基于Faster-RCNN的目标检测模型结构(VOC数据集下)示意图。
在这里插入图片描述

​ Faster RCNN是一个两阶段的目标检测算法,因为他引入了建议框的概念。我们首先会用建议框进行一个粗略的筛选,然后再进行一个细致的调整。

​ 他的结构可以分成三个部分。当有一副图像在进入faster rcnn的网络之前,首先会进行一个尺寸压缩。接着利用主干网络进行一个特征提取。一般主干网络可以采用VGG或者resnet。

​ 然后我们得到了一个共享特征层。他是一堆特征的集合。这个共享特征层有两条路。

​ 一是向左走,对建议框进行一个粗略的预测。进行一个3x3的卷积,再进行两个1x1的卷积。其中一个1x1的卷积通道数是18,另外一个1x1卷积通道数是36. 把他们分别拆分成9x2和9x4.因为我们项目里共享特征层shape是38x38x1024,共享特征层相当于把图片划分成38x38的网格,然后每个网格有9个先验框,然后我们在这个地方两个1x1的预测结果就是判断我们的先验框内部是否真实的包含物体,然后我们会对先验框进行调整,把它调成我们的建议框。其中9x2的卷积就是用来判断先验框内部是否真实的包含物体。这个2一个是代表先验框背景的概率,一个代表先验框内部有物体的概率,然后这个9x4中的4是指先验框调整参数。我们需要利用4个参数才能确定一个框(分别是中心点的x轴,y轴坐标,框的高和宽)。然后经过这两次卷积,我们就可以获得一个建议框,建议框就是对画面中的物体进行粗略的筛选。

​ 然后这个建议框会跟共享特征层结合,传入到roi池化层中,roi就会利用建议框对输出的特征层进行截取。由于我们获得的建议框大小并不是同样大的,所以在这里roipooling层会对所有局部特征层进行分区域的池化,池化之后我们获得同等大小的局部特征层。之后我们对获得的所有局部特征层进行分类预测和回归预测。

​ 回归预测的结果会直接对建议框直接进行调整。然后获得最终的预测框。

​ 分类预测的结果会去判断建议框内部是否真实的包含物体,并且判断物体的种类。

1.2.1 Backbone网络

​ 本项目采用 ResNet50 为前置特征提取网络,对输入图像进行特征抽取。ResNet50 是一种残差网络,能够有效解决深度卷积网络训练过程中存在的梯度消失问题,具有良好的特征提取精度与实时性。ResNet50 由卷积模块和恒等映射模块组成,卷积模块的输入和输出维度是不一样的,所以不能连续串联,作用是改变网络维度;恒等映射模块用于将维度相同的层次进行串联,从而加深网络深度。

1.2.2 RPN候选区域建议网络

​ 将 ResNet50 最后阶段输出特征图作为候选区域建议网络的输入,经滑动窗口操作运算得到通道维数为256的特征图,通过两次全连接操作,初步得到作物位置坐标以及分数预测。

1.2.3 感兴趣区域池化和全连接层

​ 感兴趣区域池化操作将候选区域建议网络产生的候选框映射到特征图对应位置并将其调整至固定尺寸,然后通过两个全连接层对建议框内的物体再次进行分类和位置调整,最终得到目标的分类和位置预测。

2 Faster R-CNN实现思路

2.1 数据集准备

​ 本项目模型均采用的是合并的VOC数据集,包括VOC2007和VOC2012。该数据集包含20个常见的物体类别,包括人、车辆、动物和家居物品等,数据集为21540张

训练前将标签文件和图片文件进行摆放。之后,在voc_annotation.py文件里获得训练用的train.txt和val.txt。

在这里插入图片描述

2.2 模型构建

2.2.1 主干网络实现

​ 其本项目的主干网络采用ResNet50。ResNet50有两个基本的块,分别名为Conv Block和Identity Block,其中Conv Block输入和输出的维度是不一样的,所以不能连续串联,它的作用是改变网络的维度;Identity Block输入维度和输出维度相同,可以串联,用于加深网络的。这两个都是残差网络结构。Faster-RCNN的主干特征提取网络部分只包含了长宽压缩了四次的内容,第五次压缩后的内容在ROI中使用。即Faster-RCNN在主干特征提取网络所用的网络层如图所示。以输入的图片为600x600为例,shape变化如下:

在这里插入图片描述

​ 最后一层的输出就是公用特征层。在代码里里面,我们定义了一个基于ResNet架构的模型,主要包括Bottleneck模块和ResNet模块,以及一个辅助的函数resnet50,用于创建ResNet-50模型的特征提取部分和分类部分。

​ Bottleneck模块是 ResNet 中的基本模块,用于构建深层网络。它包含三个卷积层,其中第一个和第三个卷积层的通道数是中间卷积层的4倍。此模块通过残差连接引入了跳跃连接,有助于训练深层网络时的梯度流动。ResNet模块定义了整个 ResNet 网络的结构,包括初始的卷积层、四个阶段的残差块(由不同数量的 Bottleneck模块堆叠而成),以及全局平均池化层和最后的全连接层用于分类。 ResNet 的不同层次通过 layer1、layer2、layer3 和 layer4表示make_layer函数用于构建 ResNet 的阶段,通过堆叠 Bottleneck 模块实现。当需要进行高和宽的压缩时,会使用 downsample 添加一个残差边以进行下采样。forward 函数定义了模型的前向传播过程,包括卷积、池化、残差块的堆叠等操作。

​ 使用resnet50()函数来获得resnet50的公用特征层。其中features部分为公用特征层,classifier部分为第二阶段用到的分类器。在预训练时,通过 load_state_dict_from_url函数加载 PyTorch 官方提供的预训练权重。features和classifier被封装成nn.Sequential模块,以方便在后续的任务中使用。

import math

import torch.nn as nn
from torchvision.models.utils import load_state_dict_from_url

class Bottleneck(nn.Module):
    expansion = 4
    def __init__(self, inplanes, planes, stride=1, downsample=None):
        super(Bottleneck, self).__init__()
        self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, stride=stride, bias=False)
        self.bn1 = nn.BatchNorm2d(planes)

        self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(planes)

        self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)
        self.bn3 = nn.BatchNorm2d(planes * 4)

        self.relu = nn.ReLU(inplace=True)
        self.downsample = downsample
        self.stride = stride

    def forward(self, x):
        residual = x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        out = self.conv2(out)
        out = self.bn2(out)
        out = self.relu(out)

        out = self.conv3(out)
        out = self.bn3(out)
        if self.downsample is not None:
            residual = self.downsample(x)

        out += residual
        out = self.relu(out)

        return out

class ResNet(nn.Module):
    def __init__(self, block, layers, num_classes=1000):
        #-----------------------------------#
        #   假设输入进来的图片是600,600,3
        #-----------------------------------#
        self.inplanes = 64
        super(ResNet, self).__init__()

        # 600,600,3 -> 300,300,64
        self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False)
        self.bn1 = nn.BatchNorm2d(64)
        self.relu = nn.ReLU(inplace=True)

        # 300,300,64 -> 150,150,64
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=0, ceil_mode=True)

        # 150,150,64 -> 150,150,256
        self.layer1 = self._make_layer(block, 64, layers[0])
        # 150,150,256 -> 75,75,512
        self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
        # 75,75,512 -> 38,38,1024 到这里可以获得一个38,38,1024的共享特征层
        self.layer3 = self._make_layer(block, 256, layers[2], stride=2)
        # self.layer4被用在classifier模型中
        self.layer4 = self._make_layer(block, 512, layers[3], stride=2)
        
        self.avgpool = nn.AvgPool2d(7)
        self.fc = nn.Linear(512 * block.expansion, num_classes)

        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
                m.weight.data.normal_(0, math.sqrt(2. / n))
            elif isinstance(m, nn.BatchNorm2d):
                m.weight.data.fill_(1)
                m.bias.data.zero_()

    def _make_layer(self, block, planes, blocks, stride=1):
        downsample = None
        #-------------------------------------------------------------------#
        #   当模型需要进行高和宽的压缩的时候,就需要用到残差边的downsample
        #-------------------------------------------------------------------#
        if stride != 1 or self.inplanes != planes * block.expansion:
            downsample = nn.Sequential(
                nn.Conv2d(self.inplanes, planes * block.expansion,kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(planes * block.expansion),
            )
        layers = []
        layers.append(block(self.inplanes, planes, stride, downsample))
        self.inplanes = planes * block.expansion
        for i in range(1, blocks):
            layers.append(block(self.inplanes, planes))
        return nn.Sequential(*layers)

    def forward(self, x):
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        x = self.maxpool(x)

        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)

        x = self.avgpool(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x

def resnet50(pretrained = False):
    model = ResNet(Bottleneck, [3, 4, 6, 3])
    if pretrained:
        state_dict = load_state_dict_from_url("https://download.pytorch.org/models/resnet50-19c8e357.pth", model_dir="./model_data")
        model.load_state_dict(state_dict)
    #----------------------------------------------------------------------------#
    #   获取特征提取部分,从conv1到model.layer3,最终获得一个38,38,1024的特征层
    #----------------------------------------------------------------------------#
    features    = list([model.conv1, model.bn1, model.relu, model.maxpool, model.layer1, model.layer2, model.layer3])
    #----------------------------------------------------------------------------#
    #   获取分类部分,从model.layer4到model.avgpool
    #----------------------------------------------------------------------------#
    classifier  = list([model.layer4, model.avgpool])
    
    features    = nn.Sequential(*features)
    classifier  = nn.Sequential(*classifier)
    return features, classifier

2.2.2 RPN网络结构的实现

​ 接下来就是定义Region Proposal Network(RPN)。RPN是用于目标检测中的一部分,它负责生成候选目标框(Region of Interest,ROI),这些候选框将被送入后续的ROI Pooling或ROI Align网络。

​ 获得的公用特征层在图像中就是Feature Map,其有两个应用,一个是和ROIPooling结合使用、另一个是进行一次3x3的卷积后,进行一个18通道的1x1卷积,还有一个36通道的1x1卷积。在Faster-RCNN中,num_priors也就是先验框的数量就是9,所以两个1x1卷积的结果实际上也就是:

​ ①9 x 4的卷积用于预测公用特征层上每一个网格点上每一个先验框的变化情况。

​ ②9 x 2的卷积 用于预测 公用特征层上 每一个网格点上 每一个预测框内部是否包含了物体,序号为1的内容为包含物体的概率。

​ 当我们输入的图片的shape是600x600x3的时候,公用特征层的shape就38x38x1024,相当于把输入进来的图像分割成38x38的网格,然后每个网格存在9个先验框,这些先验框有不同的大小,在图像上密密麻麻。代码实现的流程如下图。
在这里插入图片描述

代码实现如下:

class RegionProposalNetwork(nn.Module):
    def __init__(
        self, 
        in_channels     = 512, 
        mid_channels    = 512, 
        ratios          = [0.5, 1, 2],
        anchor_scales   = [8, 16, 32], 
        feat_stride     = 16,
        mode            = "training",
    ):
        super(RegionProposalNetwork, self).__init__()
        #-----------------------------------------#
        #   生成基础先验框,shape为[9, 4]
        #-----------------------------------------#
        self.anchor_base    = generate_anchor_base(anchor_scales = anchor_scales, ratios = ratios)
        n_anchor            = self.anchor_base.shape[0]

        #-----------------------------------------#
        #   先进行一个3x3的卷积,可理解为特征整合
        #-----------------------------------------#
        self.conv1  = nn.Conv2d(in_channels, mid_channels, 3, 1, 1)
        #-----------------------------------------#
        #   分类预测先验框内部是否包含物体
        #-----------------------------------------#
        self.score  = nn.Conv2d(mid_channels, n_anchor * 2, 1, 1, 0)
        #-----------------------------------------#
        #   回归预测对先验框进行调整
        #-----------------------------------------#
        self.loc    = nn.Conv2d(mid_channels, n_anchor * 4, 1, 1, 0)

        #-----------------------------------------#
        #   特征点间距步长
        #-----------------------------------------#
        self.feat_stride    = feat_stride
        #-----------------------------------------#
        #   用于对建议框解码并进行非极大抑制
        #-----------------------------------------#
        self.proposal_layer = ProposalCreator(mode)
        #--------------------------------------#
        #   对FPN的网络部分进行权值初始化
        #--------------------------------------#
        normal_init(self.conv1, 0, 0.01)
        normal_init(self.score, 0, 0.01)
        normal_init(self.loc, 0, 0.01)

    def forward(self, x, img_size, scale=1.):
        n, _, h, w = x.shape
        #-----------------------------------------#
        #   先进行一个3x3的卷积,可理解为特征整合
        #-----------------------------------------#
        x = F.relu(self.conv1(x))
        #-----------------------------------------#
        #   回归预测对先验框进行调整
        #-----------------------------------------#
        rpn_locs = self.loc(x)
        rpn_locs = rpn_locs.permute(0, 2, 3, 1).contiguous().view(n, -1, 4)
        #-----------------------------------------#
        #   分类预测先验框内部是否包含物体
        #-----------------------------------------#
        rpn_scores = self.score(x)
        rpn_scores = rpn_scores.permute(0, 2, 3, 1).contiguous().view(n, -1, 2)
        
        #--------------------------------------------------------------------------------------#
        #   进行softmax概率计算,每个先验框只有两个判别结果
        #   内部包含物体或者内部不包含物体,rpn_softmax_scores[:, :, 1]的内容为包含物体的概率
        #--------------------------------------------------------------------------------------#
        rpn_softmax_scores  = F.softmax(rpn_scores, dim=-1)
        rpn_fg_scores       = rpn_softmax_scores[:, :, 1].contiguous()
        rpn_fg_scores       = rpn_fg_scores.view(n, -1)

2.2.3 Proposal建议框的解码

​ 代码定义了一个 ProposalCreator类,它负责根据 RPN 网络的预测结果生成最终的建议框,并进行一些后处理操作,包括非极大抑制(NMS)等。代码思路如下:

​ 首先初始化函数,分别设置mod模式,可以是 “training” 或 “testing”,用于区分训练和测试阶段。设置NMS(非极大抑制)的 IoU(交并比)阈值,默认为 0.7,n_train_pre_nms和 n_train_post_nms训练阶段用于 NMS 前后的建议框数量,默认为 12000 和 600。n_test_pre_nms和n_test_post_nms测试阶段用于 NMS 前后的建议框数量,默认为 3000 和 300。min_size为建议框的宽和高的最小值

​ 然后定义__call__方法,接受输入参数loc(回归预测)、score(分类预测)、anchor(先验框坐标)、img_size和缩放比例scale。根据模式选择训练或测试时的 NMS 数量。然后将先验框转换成 PyTorch Tensor,并在 GPU 上进行计算。 使用 RPN 网络的回归预测结果和先验框坐标计算最终的建议框roi。为了防止建议框超出图像边界,通过torch.clamp进行裁剪。根据建议框的宽和高是否满足最小值的条件,筛选出有效的建议框。接着根据得分对建议框进行排序,取前n_pre_nms个建议框。使用非极大抑制(NMS)对建议框进行筛选,保留得分较高的建议框。最终返回经过处理的建议框。

​ 这个类的主要作用是在训练和测试阶段生成有效的建议框,并通过非极大抑制(NMS)筛选出最终的建议框。这些建议框将用于后续的目标检测任务。具体代码如下。

class ProposalCreator():
    def __init__(
        self, 
        mode, 
        nms_iou             = 0.7,
        n_train_pre_nms     = 12000,
        n_train_post_nms    = 600,
        n_test_pre_nms      = 3000,
        n_test_post_nms     = 300,
        min_size            = 16
    
    ):
        #-----------------------------------#
        #   设置预测还是训练
        #-----------------------------------#
        self.mode               = mode
        #-----------------------------------#
        #   建议框非极大抑制的iou大小
        #-----------------------------------#
        self.nms_iou            = nms_iou
        #-----------------------------------#
        #   训练用到的建议框数量
        #-----------------------------------#
        self.n_train_pre_nms    = n_train_pre_nms
        self.n_train_post_nms   = n_train_post_nms
        #-----------------------------------#
        #   预测用到的建议框数量
        #-----------------------------------#
        self.n_test_pre_nms     = n_test_pre_nms
        self.n_test_post_nms    = n_test_post_nms
        self.min_size           = min_size

    def __call__(self, loc, score, anchor, img_size, scale=1.):
        if self.mode == "training":
            n_pre_nms   = self.n_train_pre_nms
            n_post_nms  = self.n_train_post_nms
        else:
            n_pre_nms   = self.n_test_pre_nms
            n_post_nms  = self.n_test_post_nms

        #-----------------------------------#
        #   将先验框转换成tensor
        #-----------------------------------#
        anchor = torch.from_numpy(anchor)
        if loc.is_cuda:
            anchor = anchor.cuda()
        #-----------------------------------#
        #   将RPN网络预测结果转化成建议框
        #-----------------------------------#
        roi = loc2bbox(anchor, loc)
        #-----------------------------------#
        #   防止建议框超出图像边缘
        #-----------------------------------#
        roi[:, [0, 2]] = torch.clamp(roi[:, [0, 2]], min = 0, max = img_size[1])
        roi[:, [1, 3]] = torch.clamp(roi[:, [1, 3]], min = 0, max = img_size[0])
        
        #-----------------------------------#
        #   建议框的宽高的最小值不可以小于16
        #-----------------------------------#
        min_size    = self.min_size * scale
        keep        = torch.where(((roi[:, 2] - roi[:, 0]) >= min_size) & ((roi[:, 3] - roi[:, 1]) >= min_size))[0]
        #-----------------------------------#
        #   将对应的建议框保留下来
        #-----------------------------------#
        roi         = roi[keep, :]
        score       = score[keep]

        #-----------------------------------#
        #   根据得分进行排序,取出建议框
        #-----------------------------------#
        order       = torch.argsort(score, descending=True)
        if n_pre_nms > 0:
            order   = order[:n_pre_nms]
        roi     = roi[order, :]
        score   = score[order]

        #-----------------------------------#
        #   对建议框进行非极大抑制
        #   使用官方的非极大抑制会快非常多
        #-----------------------------------#
        keep    = nms(roi, score, self.nms_iou)
        keep    = keep[:n_post_nms]
        roi     = roi[keep]
        return roi

2.2.4 感兴趣区域ROI头部定义

​ 代码的思路是定义了一个使用ResNet50作为骨干网络的Faster R-CNN模型的RoI头部。负责处理感兴趣区域(RoIs)的特征提取和预测。通过RoI池化获取RoI的特征,经过分类器进行特征提取,然后分别通过线性层进行边界框回归和目标分类的预测,最终输出RoI的位置信息和目标类别概率。这是目标检测中关键的组件,用于对提议的区域进行详细的分析和识别。具体代码如附录。

​ 之后,我们对建议框进行再一次进行解码后,我们可以获得预测框在原图上的位置,而且这些预测框都是经过筛选的。这些筛选后的框可以直接绘制在图片上,就可以获得结果了。

class Resnet50RoIHead(nn.Module):
    def __init__(self, n_class, roi_size, spatial_scale, classifier):
        super(Resnet50RoIHead, self).__init__()
        self.classifier = classifier
        #--------------------------------------#
        #   对ROIPooling后的的结果进行回归预测
        #--------------------------------------#
        self.cls_loc = nn.Linear(2048, n_class * 4)
        #-----------------------------------#
        #   对ROIPooling后的的结果进行分类
        #-----------------------------------#
        self.score = nn.Linear(2048, n_class)
        #-----------------------------------#
        #   权值初始化
        #-----------------------------------#
        normal_init(self.cls_loc, 0, 0.001)
        normal_init(self.score, 0, 0.01)

        self.roi = RoIPool((roi_size, roi_size), spatial_scale)

    def forward(self, x, rois, roi_indices, img_size):
        n, _, _, _ = x.shape
        if x.is_cuda:
            roi_indices = roi_indices.cuda()
            rois = rois.cuda()
        
        rois_feature_map = torch.zeros_like(rois)
        rois_feature_map[:, [0,2]] = rois[:, [0,2]] / img_size[1] * x.size()[3]
        rois_feature_map[:, [1,3]] = rois[:, [1,3]] / img_size[0] * x.size()[2]

        indices_and_rois = torch.cat([roi_indices[:, None], rois_feature_map], dim=1)
        #-----------------------------------#
        #   利用建议框对公用特征层进行截取
        #-----------------------------------#
        pool = self.roi(x, indices_and_rois)
        #-----------------------------------#
        #   利用classifier网络进行特征提取
        #-----------------------------------#
        fc7 = self.classifier(pool)
        #--------------------------------------------------------------#
        #   当输入为一张图片的时候,这里获得的f7的shape为[300, 2048]
        #--------------------------------------------------------------#
        fc7 = fc7.view(fc7.size(0), -1)

        roi_cls_locs    = self.cls_loc(fc7)
        roi_scores      = self.score(fc7)
        roi_cls_locs    = roi_cls_locs.view(n, -1, roi_cls_locs.size(1))
        roi_scores      = roi_scores.view(n, -1, roi_scores.size(1))
        return roi_cls_locs, roi_scores


总结整个Faster R-CNN构建的执行流程如下。

在这里插入图片描述


2.3 模型训练

​ 本项目采用了迁移学习,在训练开始时加载预训练权重。预训练权重对于不同的数据集是通用的,因为特征是通用的。
在这里插入图片描述

​ 主干网络的特征提取模型采用了resnet50。不用的话主干部分的权值太过随机,特征提取效果不明显网络训练的结果也不会好。


​ 本项目采用冻结训练。训练分为两个阶段,分别是冻结阶段和解冻阶段因为目标检测模型里,主干特征提取部分所提取到的特征是通用的,把 backbone 冻结起来训练可以加快训练效率,也可以防止权值被破坏。

​ 在冻结阶段,模型的主干被冻结,特征提取网络不发生改变,占用的显存较小,仅对网络进行微调。在解冻阶段,模型的主干不被冻结了,特征提取网络会发生改变,占用的显存较大,网络所有的参数都会发生改变。

在这里插入图片描述


PS. Faster-RCNN模型训练的所有超参数设置如下:

在这里插入图片描述

2.4 模型评估

​ 本项目的评估方法采用的是mAP(mean Average Precision)。是目标检测任务中常用的性能评估指标之一,用于衡量模型在检测多个目标类别时的准确性。因为多标签图像分类任务中图片的标签不止一个,因此评价不能用普通单标签图像分类的标准,对于每个目标类别,AP表示模型在该类别上的精度,它通过计算 Precision-Recall 曲线下的面积来度量。mAP是对所有目标类别的AP进行平均得到的值,用于综合评估模型在多个类别上的性能。

2.4.1 mAP计算方法

​ 首先是针对每个目标类别,根据模型的输出结果按照置信度进行排序,计算在不同置信度阈值下的 Precision和 Recall。

​ Precision的计算公式如下:

在这里插入图片描述

​ Recall的计算公式如下:

在这里插入图片描述

​ 这形成了Precision-Recall曲线。接着对于每个类别,通过对 Precision-Recall 曲线下的面积进行积分来计算AP。这个积分可以使用数值积分方法(例如,插值)来估算。将所有类别的AP进行平均得到mAP。整个计算方法流程图如下:

在这里插入图片描述

2.4.2 Faster R-CNN模型的评估

​ 以下是Faster-RCNN模型训练了100轮的各个类别的准确率以及map。由图可见,mAP达到了80.04%。其中不同网络的LOSS不同,LOSS只是一个参考指标,用于查看网络是否收敛,而非评价网络好坏,LOSS的值不重要,重要的是是否在变小,预测是否有效果。

在这里插入图片描述

​ Faster-RCNN模型训练的loss曲线图和mAP曲线图如下。由于在60轮的时候解冻训练,主干不被冻结,特征提取网络发生改变,loss突增,但经过继续训练,模型的loss最后下降到收敛。

在这里插入图片描述

2.5 模型预测效果

​ 接下来,将训练得到的最佳权值载入,模型的各个参数值如下:

在这里插入图片描述

​ Faster-RCNN预测效果如下图所示。可见在这幅图中,对于没有什么遮挡的目标,模型的检测效果优秀,能达到100.0%的识别精度,比如图中的car、bicycle等目标,都能被精确识别。而对于目标重叠的情况下,预测效果较为良好,虽然识别的精度不是很高,但仍能将该类别检测出来。如图中未遮挡的person检测精度达到了100.0%,而对于远处的几个重叠的person识别精度能达到80.0%左右。

在这里插入图片描述

​ 接下来展示其他图像上的预测效果。为了进一步检验模型的性能,以下图片选取了自己的照片进行预测。可见对于目标较大的物体,模型的识别准确率会比较好,可以达到99.0%、100.0%左右。而对于远处的目标,目标有些虚化以及缺失不完整,虽然识别精度没那么高,但仍然能被检测出来。总体而言,模型的性能还是不错的。

在这里插入图片描述

3 结论

​ Faster R-CNN采用了一种两阶段的训练策略,指目标检测模型的训练过程分为两个主要阶段。采用两阶段训练的优势在于,它将目标检测任务分解为两个独立的阶段,使得模型更容易收敛和训练。RPN的引入帮助模型生成高质量的目标区域提议,使第二阶段的目标检测模型能够关注在这些候选区域上进行更准确的分类和边界框回归。

3.1 Faster R-CNN的特点

  • Faster R-CNN引入了区域建议网络(RPN),该网络负责生成潜在的目标区域(即候选框或建议框),这些区域有可能包含目标对象。RPN使用卷积层来在图像中的各个位置生成多个建议框,并为每个框分配一个概率得分,表示框内是否可能包含目标。

  • 在生成候选框之后,Faster
    R-CNN使用RoI区域兴趣池化来将这些不同尺寸的候选框映射为固定大小的特征图。这使得不同大小的候选框能够对应相同大小的特征表示,方便后续处理。

  • 采用卷积神经网络(通常是预训练的深度卷积神经网络,如VGG16或ResNet)作为特征提取器。该网络用于提取图像中的特征表示,以供RPN和RoI池化层使用。

  • RoI池化后的特征图用于目标分类和边界框回归。具体而言,每个RoI都被送入两个全连接层,一个用于目标的二元分类(是否属于某个类别),另一个用于预测边界框的位置。

3.2 Faster R-CNN的优点

  • 一是端到端训练。与先前的R-CNN系列相比,Faster R-CNN实现了端到端的训练,而不需要多个阶段的训练和微调。尽管 Faster
    R-CNN 是一个两阶段的目标检测模型,但"端到端训练"通常是指整个模型可以一起进行训练,而不是分阶段训练。Faster R-CNN
    的两个阶段(RPN 和 Fast
    R-CNN)可以同时进行训练,因此整个模型在训练过程中是端到端的。这意味着模型的参数可以通过单一的优化过程进行更新,而不需要分开为每个阶段优化。

  • 二是高效性能。Faster R-CNN在目标检测任务上取得了较好的性能,同时也更加高效,减少了计算和存储的需求。

  • 42
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值