【项目实践】从零开始学习SSD目标检测算法训练自己的数据集(附注释项目代码)(二)

简介: 【项目实践】从零开始学习SSD目标检测算法训练自己的数据集(附注释项目代码)(二)

2.3、SSD的训练过程与细节

2.3.1、框架训练的具体步骤

022c84460fea32d8ea8a63b195b47627.png

  1. 首先VGG16在ILSVRC CLS-LOC数据集上进行预训练
  2. 然后将VGG16的全连接层fc6和fc7转换成3×3卷积层Conv6(Conv6采用带孔卷积Dilation Convolution,Conv6采用3×3大小,dilation rate=6的膨胀卷积)和1×1卷积层Conv7;
  3. 然后移除dropout层和fc8层,并新增一系列卷积层,在检测数据集上做fine tuning
  4. 从后面新增的卷积层中提取Conv7,Conv8_2,Conv9_2,Conv10_2,Conv11_2作为检测所用的特征图,加上Conv4_3层,共提取了6个特征图,其大小分别(38,38),(19,19),(10,10),(5,5),(3,3),(1,1),但是不同特征图设置的先验框数目不同(同一个特征图上每个单元设置的先验框是相同的,这里的数目指的是一个单元的先验框数目),由于每个先验框都会预测一个边界框,所以 SSD300一共可以预测38×38×4+19×19×6+10×10×6+5×5×6+3×3×4+1×1×4 = 8732 个边界框,这是一个相当大的数字, 所以说SSD本质上是密集采样;
  5. 得到了特征图之后,对特征图进行3x3卷积得到检测结果
  6. 对于每个预测框,首先根据类别置信度确定其类别(置信度最大者)与置信度值, 并过滤掉属于背景的预测框。
  7. 然后根据置信度阈值(如0.5)过滤掉阈值较低的预测框。对于留下的预测框进行解码,根据先验框得 到其真实的位置参数(解码后一般还需要做clip,防止预测框位置超出图片)。解码之后,一般需要 根据置信度进行降序排列,然后仅保留top-k(如400)个预测框。
  8. 使用非最大值抑制NMS进行筛选,过滤掉那些重叠度较大的预测框。最后剩余的预测框就是检测结果
base = [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'C', 512, 512, 512, 'M',
            512, 512, 512]
def vgg(i):
    layers = []
    in_channels = i
    for v in base:
        if v == 'M':
            layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
        elif v == 'C':
            layers += [nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True)]
        else:
            conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)
            layers += [conv2d, nn.ReLU(inplace=True)]
            in_channels = v
    pool5 = nn.MaxPool2d(kernel_size=3, stride=1, padding=1)
    conv6 = nn.Conv2d(512, 1024, kernel_size=3, padding=6, dilation=6)
    conv7 = nn.Conv2d(1024, 1024, kernel_size=1)
    layers += [pool5, conv6,
               nn.ReLU(inplace=True), conv7, nn.ReLU(inplace=True)]
    return layers
def add_extras(i, batch_norm=False):
    # Extra layers added to VGG for feature scaling
    layers = []
    in_channels = i
    # Block 6
    # 19,19,1024 -> 10,10,512
    layers += [nn.Conv2d(in_channels, 256, kernel_size=1, stride=1)]
    layers += [nn.Conv2d(256, 512, kernel_size=3, stride=2, padding=1)]
    # Block 7
    # 10,10,512 -> 5,5,256
    layers += [nn.Conv2d(512, 128, kernel_size=1, stride=1)]
    layers += [nn.Conv2d(128, 256, kernel_size=3, stride=2, padding=1)]
    # Block 8
    # 5,5,256 -> 3,3,256
    layers += [nn.Conv2d(256, 128, kernel_size=1, stride=1)]
    layers += [nn.Conv2d(128, 256, kernel_size=3, stride=1)]
    # Block 9
    # 3,3,256 -> 1,1,256
    layers += [nn.Conv2d(256, 128, kernel_size=1, stride=1)]
    layers += [nn.Conv2d(128, 256, kernel_size=3, stride=1)]
    return layers

2.3.2、特征图的检测过程:

检测值包含两个部分:类别置信度和边界框位置, 各采用一次3×3卷积来进行完成。

2c30007266f7f76ace56bcff774ed52d.png

每一个有效特征层对应的先验框对应着该特征层上 每一个网格点上 预先设定好的多个框。所有的特征层对应的预测结果的shape如下:

fce2ab53d770ee11c755cf58640c9497.png

class SSD(nn.Module):
    def __init__(self, phase, base, extras, head, num_classes):
        super(SSD, self).__init__()
        self.phase = phase
        self.num_classes = num_classes
        self.cfg = Config
        self.vgg = nn.ModuleList(base)
        self.L2Norm = L2Norm(512, 20)
        self.extras = nn.ModuleList(extras)
        self.priorbox = PriorBox(self.cfg)
        with torch.no_grad():
            self.priors = Variable(self.priorbox.forward())
        self.loc = nn.ModuleList(head[0])
        self.conf = nn.ModuleList(head[1])
        if phase == 'test':
            self.softmax = nn.Softmax(dim=-1)
            self.detect = Detect(num_classes, 0, 200, 0.01, 0.45)
    def forward(self, x):
        sources = list()
        loc = list()
        conf = list()
        # 获得conv4_3的内容
        for k in range(23):
            x = self.vgg[k](x)
        s = self.L2Norm(x)
        sources.append(s)
        # 获得fc7的内容
        for k in range(23, len(self.vgg)):
            x = self.vgg[k](x)
        sources.append(x)
        # 获得后面的内容
        for k, v in enumerate(self.extras):
            x = F.relu(v(x), inplace=True)
            if k % 2 == 1:
                sources.append(x)
        # 添加回归层和分类层
        for (x, l, c) in zip(sources, self.loc, self.conf):
            loc.append(l(x).permute(0, 2, 3, 1).contiguous())
            conf.append(c(x).permute(0, 2, 3, 1).contiguous())
        # 进行resize
        loc = torch.cat([o.view(o.size(0), -1) for o in loc], 1)
        conf = torch.cat([o.view(o.size(0), -1) for o in conf], 1)
        if self.phase == "test":
            # loc会resize到batch_size,num_anchors,4
            # conf会resize到batch_size,num_anchors,
            output = self.detect(
                loc.view(loc.size(0), -1, 4),                   # loc preds
                self.softmax(conf.view(conf.size(0), -1,
                             self.num_classes)),                # conf preds
                self.priors              
            )
        else:
            output = (
                loc.view(loc.size(0), -1, 4),
                conf.view(conf.size(0), -1, self.num_classes),
                self.priors
            )
        return output
mbox = [4, 6, 6, 6, 4, 4]
def get_ssd(phase,num_classes):
    vgg, extra_layers = add_vgg(3), add_extras(1024)
    loc_layers = []
    conf_layers = []
    vgg_source = [21, -2]
    for k, v in enumerate(vgg_source):
        loc_layers += [nn.Conv2d(vgg[v].out_channels,
                                 mbox[k] * 4, kernel_size=3, padding=1)]
        conf_layers += [nn.Conv2d(vgg[v].out_channels,
                        mbox[k] * num_classes, kernel_size=3, padding=1)]
    for k, v in enumerate(extra_layers[1::2], 2):
        loc_layers += [nn.Conv2d(v.out_channels, mbox[k]
                                 * 4, kernel_size=3, padding=1)]
        conf_layers += [nn.Conv2d(v.out_channels, mbox[k]
                                  * num_classes, kernel_size=3, padding=1)]
    SSD_MODEL = SSD(phase, vgg, extra_layers, (loc_layers, conf_layers), num_classes)
    return SSD_MODEL

2.3.3、Anchor中心的获取

每个默认框的中心位置为:

其中,W、H分别输入图像宽度和高度,i=0,1,...,m-1,j=0,1,...,n-1。

2.3.4、数据增强

为了提高关于目标大小和形状的鲁棒性,SSD采用了数据增强的随机采样策略。

  1. 使用整幅原始输入图像
  2. 采样图像块,与目标的最小交并比为0.1、0.3、0.5、0.7、0.9
  3. 随机采样图像块,每个采样块的大小为原始图像大小的[0.1,1],高宽比为[0.5,2],如果真是边框的中心在采样块内,则保留重叠部分;
  4. 采样块的增强(亮度变化、大小调整、概率翻转等操作)。

90c4661b22668cf0fb0a6d11256d5af7.png

2.4、SSD的优缺点

2.4.1、SSD的优点

  1. 对于小尺寸目标对象,SSD的性能比Faster R-CNN差。SSD只能在较高分辨率的层(最左边的层)检测小 目标。但是这些层包含低级特征,如边缘或色块,分类的信息量较少。
  2. 准确率随着默认边界框的数量而增加,但以速度为代价。
  3. 多尺度特征图改进了不同尺度的目标的检测。
  4. 设计更好的默认边界框将有助于准确性。
  5. COCO数据集具有较小的目标。要提高准确性,使用较小的默认框(以较小的尺度0.15开始)。
  6. 与R-CNN相比,SSD具有较低的定位误差,但处理相似类别的分类错误较多。较高的分类错误可能是因为使 用相同的边界框来进行多个类别预测。
  7. SSD512 具有比SSD300更高的精度(2.5%),但运行速度为22 FPS而不是59 FPS。

2.4.2、SSD的缺点

  1. SSD算法对小目标不够鲁棒(会出现误检和漏检);
  2. 最主要的原因是浅层特征图的表示能力 不够强。

3、SSD的改进——DSSD


3.1、DSSD模型概览

DSSD在原来的SSD模型上主要作了两大改进:

  1. 替换掉VGG,而改用了Resnet-101作为特征提取网络并在对不同尺度的特征图进行默认框检测时使用了更新的检测单元;
  2. 在网络的后端使用了多个反卷积层(deconvolution layers)以有效地扩展低维度信息 的上下文信息(contextual information),从而有效地改善了小尺度目标的检测。

f54ea5a6787188747b7650197e4e1d61.png

3.2、新的预测模块

为每个预测层添加一个残差块,如(c)所示。

还尝试了原始的SSD方法(a)和具有跳跃连接(b)的残差块的版本以及两个连续的残差块(d)。

3.3、反卷积模块

DSSD的第二个重大创新来自于在模型后端添加了多个反卷积模块来扩大模型在小尺度上的high level特征信息。而这 种反卷积输出的特征图又会与模型前端卷积层的相同尺度的特征图进行元素级的乘法

(element-wise product)来 生成相应尺度的特征图。

添加额外的反卷积层以连续增加特征图层的分辨率。

为了加强特征,采用了沙漏模型中的“跳跃连接”理念

  1. 首先,在每个卷积层之后添加BN层。
  2. 其次,使用学习的反卷积层而不是双线性上采样。
  3. 最后,测试了不同的 组合方法:逐元素相加和逐元素乘积。

实验结果表明,逐元素乘积提供了最佳的准确性。新的DSSD模型能够胜过以前的SSD框架,特别是在小目标或上下文特定目标上, 同时仍然保持与其他检测器相当的速度。

相关文章
|
8天前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
29 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
9天前
|
算法
动态规划算法学习三:0-1背包问题
这篇文章是关于0-1背包问题的动态规划算法详解,包括问题描述、解决步骤、最优子结构性质、状态表示和递推方程、算法设计与分析、计算最优值、算法实现以及对算法缺点的思考。
32 2
动态规划算法学习三:0-1背包问题
|
4天前
|
存储 缓存 算法
如何通过优化算法和代码结构来提升易语言程序的执行效率?
如何通过优化算法和代码结构来提升易语言程序的执行效率?
|
5天前
|
搜索推荐
插入排序算法的讲解和代码
【10月更文挑战第12天】插入排序是一种基础的排序算法,理解和掌握它对于学习其他排序算法以及数据结构都具有重要意义。你可以通过实际操作和分析,进一步深入了解插入排序的特点和应用场景,以便在实际编程中更好地运用它。
|
8天前
|
机器学习/深度学习 人工智能 算法
【玉米病害识别】Python+卷积神经网络算法+人工智能+深度学习+计算机课设项目+TensorFlow+模型训练
玉米病害识别系统,本系统使用Python作为主要开发语言,通过收集了8种常见的玉米叶部病害图片数据集('矮花叶病', '健康', '灰斑病一般', '灰斑病严重', '锈病一般', '锈病严重', '叶斑病一般', '叶斑病严重'),然后基于TensorFlow搭建卷积神经网络算法模型,通过对数据集进行多轮迭代训练,最后得到一个识别精度较高的模型文件。再使用Django搭建Web网页操作平台,实现用户上传一张玉米病害图片识别其名称。
22 0
【玉米病害识别】Python+卷积神经网络算法+人工智能+深度学习+计算机课设项目+TensorFlow+模型训练
|
8天前
|
算法
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
这篇文章介绍了动态规划算法中解决最大上升子序列问题(LIS)的方法,包括问题的描述、动态规划的步骤、状态表示、递推方程、计算最优值以及优化方法,如非动态规划的二分法。
37 0
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
|
9天前
|
算法
动态规划算法学习二:最长公共子序列
这篇文章介绍了如何使用动态规划算法解决最长公共子序列(LCS)问题,包括问题描述、最优子结构性质、状态表示、状态递归方程、计算最优值的方法,以及具体的代码实现。
40 0
动态规划算法学习二:最长公共子序列
|
4天前
|
机器学习/深度学习 算法 数据建模
计算机前沿技术-人工智能算法-生成对抗网络-算法原理及应用实践
计算机前沿技术-人工智能算法-生成对抗网络-算法原理及应用实践
9 0
|
9天前
|
存储 算法
动态规划算法学习一:DP的重要知识点、矩阵连乘算法
这篇文章是关于动态规划算法中矩阵连乘问题的详解,包括问题描述、最优子结构、重叠子问题、递归方法、备忘录方法和动态规划算法设计的步骤。
42 0
|
17天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于MSER和HOG特征提取的SVM交通标志检测和识别算法matlab仿真
### 算法简介 1. **算法运行效果图预览**:展示算法效果,完整程序运行后无水印。 2. **算法运行软件版本**:Matlab 2017b。 3. **部分核心程序**:完整版代码包含中文注释及操作步骤视频。 4. **算法理论概述**: - **MSER**:用于检测显著区域,提取图像中稳定区域,适用于光照变化下的交通标志检测。 - **HOG特征提取**:通过计算图像小区域的梯度直方图捕捉局部纹理信息,用于物体检测。 - **SVM**:寻找最大化间隔的超平面以分类样本。 整个算法流程图见下图。

热门文章

最新文章