在上一篇文章中,我们讨论了 SSD 算法的工作原理,介绍了其实现细节并展示了其训练过程。如果你还没有阅读上一篇博文,我建议你在继续阅读之前先阅读一下。
在本系列第二部分,我们将重点讨论 SSD 的移动端友好变体——SSDlite。我们计划首先介绍算法的主要组件,突出与原始 SSD 不同的部分,然后讨论发布的模型是如何训练的,最后提供我们探索过的所有新目标检测模型的详细基准测试。
SSDlite 网络架构
SSDlite 是 SSD 的一种改进版本,最早在MobileNetV2 论文中简要介绍,后来又在MobileNetV3 论文中再次使用。由于这两篇论文的主要重点是引入新颖的 CNN 架构,因此 SSDlite 的大部分实现细节并未得到阐明。我们的代码遵循这两篇论文中介绍的所有细节,并在必要时从官方实现中填补空白。
如前所述,SSD 是一个模型家族,因为可以使用不同的骨干网络(例如 VGG、MobileNetV3 等)和不同的头部(例如使用常规卷积、可分离卷积等)对其进行配置。因此,SSD 的许多组件在 SSDlite 中保持不变。下面我们仅讨论那些不同的部分。
分类和回归头部
根据 MobileNetV2 论文的第 6.2 节,SSDlite 用可分离卷积替换了原始头部中使用的常规卷积。因此,我们的实现引入了使用3x3 深度可分离卷积和 1x1 投影的新头部。由于 SSD 方法的所有其他组件保持不变,为了创建 SSDlite 模型,我们的实现初始化 SSDlite 头部并将其直接传递给 SSD 构造函数。
骨干网络特征提取器
我们的实现引入了一个新类来构建 MobileNet 特征提取器。根据 MobileNetV3 论文的第 6.3 节,骨干网络返回输出步长为 16 的倒置残差块(Inverted Bottleneck block)的扩展层输出,以及池化层之前输出步长为 32 的层输出。此外,骨干网络的所有额外块都被替换为使用 1x1 压缩、步长为 2 的可分离 3x3 卷积和 1x1 扩展的轻量级等价块。最后,为了确保即使使用小的宽度乘数,头部仍具有足够的预测能力,所有卷积的最小深度大小由 min_depth
超参数控制。
SSDlite320 MobileNetV3-Large 模型

本节讨论提供的SSDlite 预训练模型的配置以及为了尽可能地复现论文结果而遵循的训练过程。
训练过程
用于在 COCO 数据集上训练模型的所有超参数和脚本都可以在我们的参考文档文件夹中找到。这里我们将讨论训练过程中最值得注意的细节。
调优超参数
尽管论文没有提供用于训练模型的超参数信息(例如正则化、学习率和批大小),但官方仓库配置文件中列出的参数是很好的起点,我们通过交叉验证将它们调整到了最优值。所有这些改进使我们在基线 SSD 配置的基础上获得了显著提升。
数据增强
SSDlite 与 SSD 的一个关键重要区别在于前者的骨干网络只有后者权重的很小一部分。这就是为什么在 SSDlite 中,数据增强更侧重于使模型对不同尺寸的对象具有鲁棒性,而不是试图避免过拟合。因此,SSDlite 仅使用 SSD 变换的子集,从而避免了模型的过度正则化。
学习率方案
由于依赖数据增强来使模型对中小尺寸对象具有鲁棒性,我们发现使用大量的 epoch 对于训练方案特别有益。更具体地说,通过使用大约比 SSD 多 3 倍的 epoch,我们能够将精度提高 4.2mAP;使用 6 倍的乘数,我们将精度提高 4.9mAP。进一步增加 epoch 似乎收益递减,并且训练变得太慢且不切实际。然而,根据模型配置,论文作者似乎使用了等效的 *16 倍乘数*。
权重初始化、输入缩放和 ReLU6
最后一组优化使得我们的实现非常接近官方实现,并帮助我们弥合了准确性差距,包括从零开始训练骨干网络而不是从 ImageNet 初始化,调整我们的权重初始化方案,改变我们的输入缩放,并将 SSDlite 头部中添加的所有标准 ReLU 替换为 ReLU6。请注意,由于我们从随机权重开始训练模型,我们额外应用了论文中描述的使用缩减尾部的骨干网络速度优化。
实现差异
将上述实现与官方仓库中的实现进行比较,我们发现了一些差异。其中大部分是次要的,与我们初始化权重的方式(例如Normal 初始化 vs Truncated Normal 初始化),我们参数化学习率调度的方式(例如较小 vs 较大的 warmup 速率,较短 vs 较长的训练时间)等有关。最大的已知差异在于我们计算分类损失的方式。更具体地说,官方仓库中带有 MobileNetV3 骨干网络的 SSDlite 实现没有使用 SSD 的 Multibox 损失,而是使用了 RetinaNet 的focal loss。这与论文有相当大的偏差,并且由于 TorchVision 已经提供了完整的 RetinaNet 实现,我们决定使用普通的 Multi-box SSD 损失来实现 SSDlite。
关键精度提升分解
正如前文所述,复现研究论文并将其移植到代码中并非精度单调递增的过程,尤其是在未知全部训练和实现细节的情况下。通常,这个过程涉及大量的回溯,因为需要识别出那些对精度有显著影响的实现细节和参数,并将其与那些没有影响的分开。下面我们尝试可视化那些提升了我们精度从基线开始的最重要的迭代。

迭代 | mAP |
---|---|
基线,使用“SSD 风格”超参数 | 10.6 |
+ 调优超参数 | 14.2 |
+ SSDlite 数据增强 | 15.2 |
+ 3x 学习率方案 | 19.4 |
+ 6x 学习率方案 | 20.1 |
+ 权重初始化、输入缩放和 ReLU6 | 21.3 |
上面呈现的优化顺序是准确的,尽管在某些情况下有些理想化。例如,尽管在超参数调优阶段测试了不同的调度器,但它们都没有提供显著的改进,因此我们保留了基线中使用的 MultiStepLR。然而,后来在尝试不同的学习率方案时,我们发现切换到 CosineAnnealingLR 是有益的,因为它需要较少的配置。因此,我们认为上述总结的主要启示是,即使从同家族模型的正确实现和一组最优超参数开始,通过优化训练方案和调整实现,总能找到提升精度的空间。诚然,上述是一个相当极端的例子,精度翻了一倍,但即便在许多情况下,仍有大量的优化可以帮助我们显著提升精度。
基准测试
以下是如何初始化两个预训练模型
ssdlite = torchvision.models.detection.ssdlite320_mobilenet_v3_large(pretrained=True)
ssd = torchvision.models.detection.ssd300_vgg16(pretrained=True)
下面是新旧(部分)检测模型之间的基准测试
模型 | mAP | CPU 推理时间 (秒) | 参数数量 (百万) |
---|---|---|---|
SSDlite320 MobileNetV3-Large | 21.3 | 0.0911 | 3.44 |
SSD300 VGG16 | 25.1 | 0.8303 | 35.64 |
SSD512 VGG16 (未发布) | 28.8 | 2.2494 | 37.08 |
SSD512 ResNet50 (未发布) | 30.2 | 1.1137 | 42.70 |
Faster R-CNN MobileNetV3-Large 320 FPN (低分辨率) | 22.8 | 0.1679 | 19.39 |
Faster R-CNN MobileNetV3-Large FPN (高分辨率) | 32.8 | 0.8409 | 19.39 |
正如我们所见,SSDlite320 MobileNetV3-Large 模型是迄今为止最快、最小的模型,因此它是现实世界移动应用的绝佳选择。尽管其精度低于预训练的低分辨率 Faster R-CNN 等效模型,但 SSDlite 框架是可适应的,可以通过引入包含更多卷积的更重型头部来提升其精度。
另一方面,SSD300 VGG16 模型相当慢且精度较低。这主要是因为它使用了 VGG16 作为骨干网络。尽管 VGG 架构极其重要且具有影响力,但如今已经相当过时了。因此,尽管该模型具有历史和研究价值,并被收录在 TorchVision 中,但我们建议需要用于现实世界应用的高分辨率检测器的用户,要么将 SSD 与其他骨干网络结合使用(参见此示例了解如何创建),要么使用 Faster R-CNN 的预训练模型之一。
希望您喜欢 SSD 系列的第二部分也是最后一篇文章。我们期待您的反馈。