高性能、高适配,SSD 孪生兄弟出场即 C 位

444 阅读6分钟

内容导读

PyTorch 1.9 同步更新了一些库,包括 TorchVision 中新增的 SSD 和 SSDlite 模型,与 SSD 相比,SSDlite 更适用于移动端 APP 开发。

SSD 全称 Single Shot MultiBox Detector,是一种用于目标检测的单次检测算法,每次可以检测多个物体。本文将重点讨论 SSD 的移动端友好型变体,即 SSDlite。

具体叙述过程为:

首先通过算法的主要组件,强调与原始 SSD 的不同之处;

然后讨论这个已发布的模型,是如何训练的;

最后为所有新上线的目标检测模型,提供详细的 Benchmark。

SSDlite 网络架构

SSDlite 是 SSD 的升级版, 在 MobileNetV2 论文中首次发布,随后在 MobileNetV3 论文中被再次使用。因为这两篇论文的重点,是介绍新的 CNN 架构,所以 SSDlite 的大部分实现细节并没有提及。

我们的代码遵循了这两篇论文中的所有细节,并在必要时对官方实现方法进行了补充。

正如前文所说,SSD 是一系列模型,用户可以用不同的 backbone(如 VGG、MobileNetV3 等)和不同的 Head(如常规卷积、可分离卷积等)来进行配置。因此,在 SSDlite 中,许多 SSD 组件都是相同的。 下面我们只讨论那些不同的部分。

分类和回归 Head

根据 MobileNetV2 论文介绍,SSDlite 用可分离卷积取代了原始 Head 中使用的常规卷积。 因此,我们的实现方法引入了使用 3x3 深度卷积和 1x1 投影的全新 Head。

由于 SSD 方法的其他所有组件都保持不变,因此要创建一个 SSDlite 模型,我们的实现方法是将 SSDlite Head 初始化,并直接将其传递给 SSD 构造函数。

Backbone 特征提取器 

我们的实现方法中引入了一个新的类来构建 MobileNet 特征提取器。 根据 MobileNetV3 论文记叙,Backbone 会返回 Inverted Bottleneck 模块的扩展层输出,这个扩展层输出步长为 16,先于输出步长为 32 的池化。

此外,Backbone 的所有额外模块都被替换成轻量级模块了, 这些轻量级模块用的是一个 1x1 Compression、一个步长为 2 的可分离 3x3 卷积,以及一个 1x1 扩展

最后,为了确保即使在使用小宽度乘数时,Head 也有充足的预测能力,所有卷积的最小深度尺寸由 min_depth 超参数控制。

SSDlite320 MobileNetV3-Large模型

image.png 本节讨论 SSDlite 预训练模型的配置,以及为了尽可能地复制论文结果而进行的训练过程

训练过程 

此处讨论训练过程中最值得注意的细节。

1. 调整后的超参 

论文中并没有提供任何可用于模型训练的超参(如正则化、学习率和批尺寸等),依据官方 repo 配置文件中列出的参数,使用交叉验证法将其调整到最佳值。这使得基线 SSD 配置有了显著提升。

2. 数据增强 

与 SSD 相比,SSDlite 的主要区别在于,前者的 Backbone 权重只是后者的一小部分。因此在 SSDlite 中,数据增强更注重于使模型对不同大小的目标对象具有鲁棒性,而不是只关注过拟合。

SSDlite 只用了 SSD transformation 的一个子集,这样可以避免模型过度正则化。

3. LR Scheme 

由于要依赖数据增强,让模型对中小型目标物体具有鲁棒性,我们发现增加 epoch 数量对模型训练非常有益。 具体来说,如果将 epoch 数量提高到 SSD 的 3 倍,就能够将精度提高至 4.2mAP point;如果使用 6 倍乘数,则可以提高至 4.9mAP。

epoch 过渡增加则会适得其反,降低训练速度和准确度。 尽管如此,根据论文中的模型配置,作者似乎使用了相当于 16 倍的乘数。

4. 权重初始化 & Input Scaling & ReLU6 

一系列优化使得我们的实现方法与官方非常接近,而且缩小了准确性上的差距。这些优化方法是从头开始训练 Backbone,而不是从 ImageNet 开始进行初始化。 此外这些优化方法也调整了我们的权重初始化方案,改变了 Input Scaling,并用 ReLU6 替换了所有添加在 SSDlite Head 中的标准 ReLU。

注意,由于我们是从随机权重中训练模型,我们还应用了论文中描述的速度优化法,即在 Backbone 中使用 reduced tail。

5. 实现方法的差异 

将上面的实现方法与官方 repo 中的实现方法对比,我们发现了一些差异。

大部分差异都非常细微, 且与如何初始化权重(例如高斯分布 VS 截断正态分布)、如何参数化 LR Scheduling(如较小 VS 较大的 Warmup 率,较短 VS 较长的训练时长)有关。

已知最显著的差异在于计算分类损失的方式。 即官方 repo 中 MobileNetV3 Backbone SSDlite 的实现,没有用到 SSD Multibox loss,而是使用 RetinaNet 的 focal loss。

由于 TorchVision 已经提供了 RetinaNet 的完整实现,我们决定用 normal Multi-box SSD loss 实现 SSDlite。

关键精确度的提升  

复制论文中的代码无法保证准确率,尤其是在不知道完整的训练过程和实施细节的情况下。通常这个过程会涉及到大量的回溯, 因为我们需要从中找到那些对准确率有重大影响的实施细节和参数中。

下面我们将尝试把那些在基线基础上,提高准确率的重要的迭代过程可视化:

image.png

image.png

以上介绍的优化顺序是准确的,尽管在某些情况下有点理想化。例如,虽然在调整超参阶段测试了不同的调度器,但并没有为准确率带来明显改进,因此我们保留了基线中使用的 MultiStepLR。

后续测试不同 LR Scheme 时,我们发现改用 CosineAnnealingLR 需要的配置更少、效果更佳。

综上所述,即使我们使用的是一个正确的实现方法、一系列来自同族模型的最佳超参,通过优化训练过程以及调整实现方法,也总是能在某种程度上提升准确率。

诚然,以上是一个相当极端的案例,准确率翻了一番,但在很多情况下,仍然有大量的优化空间可以帮助我们大幅提升准确率。

Benchmark

初始化两个预训练模型:

ssdlite = torchvision.models.detection.ssdlite320_mobilenet_v3_large(pretrained=True) ssd = torchvision.models.detection.ssd300_vgg16(pretrained=True)

新旧模型之间的 Benchmark 对比:

image.png

SSDlite320 MobileNetV3-Large 模型是迄今为止最快和体量最小的模型,因此它非常适用于移动 APP 的开发。

虽然它的准确率不及预训练的低分辨率 Faster R-CNN 模型,但 SSDlite 框架高度可调,用户可以通过引入带更多卷积的更重量级的 Head 来提高准确率。

另一方面,SSD300 VGG16 模型运行速度相当慢,而且准确率较低。 这主要是因为它的 VGG16 Backbone。虽然 VGG 架构非常有影响力,但现如今已经有些过时了。

因为这个特定模型具有历史意义和研究价值,所以才在把它放在 TorchVision 里了。如果你想要使用高分辨率检测器,我们还是建议你要么把 SSD 和其他 Backbone 结合使用,要么使用某个 Faster R-CNN 预训练模型。

参考:PyTorch Blog