在TorchVision v0.10中,我们发布了两款基于SSD架构的新目标检测模型。我们计划通过两部分文章来涵盖算法的关键实现细节以及训练信息。
在本系列的第一部分中,我们将重点介绍SSD算法在Single Shot MultiBox Detector论文中描述的原始实现。我们将简要高层描述该算法的工作原理,然后介绍其主要组件,突出代码的关键部分,最后讨论我们如何训练发布的模型。我们的目标是涵盖重现模型所需的所有细节,包括论文中未涵盖但属于原始实现一部分的优化。
SSD如何工作?
强烈建议阅读上述论文,但这里是一个快速简化的回顾。我们的目标是检测图像中对象的位置及其类别。这是SSD论文中的图5,显示了模型的预测示例。

SSD算法使用CNN骨干网络,将输入图像通过它,并从网络的不同层获取卷积输出。这些输出的列表被称为特征图。然后,这些特征图通过分类和回归头部,负责预测类别和框的位置。
由于每张图像的特征图包含来自网络不同级别的输出,它们的尺寸各不相同,因此可以捕获不同尺寸的对象。在每个特征图之上,我们平铺了几个默认框,可以将其视为我们粗略的先验猜测。对于每个默认框,我们预测是否存在一个对象(及其类别)及其偏移量(对原始位置的校正)。在训练期间,我们首先需要将真实标签与默认框匹配,然后使用这些匹配来估计损失。在推理期间,相似的预测框被组合以估计最终预测。
SSD网络架构
在本节中,我们将讨论SSD的关键组件。我们的代码严格遵循论文,并使用了官方实现中包含的许多未记录的优化。
DefaultBoxGenerator
DefaultBoxGenerator类负责生成SSD的默认框,其操作类似于FasterRCNN的AnchorGenerator(有关它们差异的更多信息,请参阅论文第4-6页)。它生成一组具有特定宽度和高度的预定义框,这些框在图像上平铺,并作为对象可能位于何处的第一个粗略先验猜测。这是SSD论文中的图1,其中包含真实标签和默认框的可视化。

该类由一组控制其形状和平铺的超参数参数化。对于那些想尝试新的骨干网络/数据集的人,实现将自动提供良好的默认参数,但也可以传递优化的自定义值。
SSDMatcher
SSDMatcher类扩展了FasterRCNN使用的标准Matcher,负责将默认框与真实标签匹配。在估计所有组合的IoU后,我们使用匹配器为每个默认框找到重叠高于IoU阈值的最佳候选真实标签。匹配器的SSD版本有一个额外步骤,以确保每个真实标签都与具有最高重叠的默认框匹配。匹配器的结果用于模型训练过程中的损失估计。
分类和回归头部
SSDHead类负责初始化网络的分类和回归部分。以下是关于其代码的一些值得注意的细节:
骨干特征提取器
特征提取器通过额外的层重新配置和增强了标准VGG骨干网络,如SSD论文的图2所示:

该类支持TorchVision的所有VGG模型,并且可以为其他类型的CNN创建类似的提取器类(参见ResNet示例)。以下是该类的一些实现细节:
- 修补第3个Maxpool层的
ceil_mode参数
对于获得与论文中相同的特征图大小是必要的。这是由于PyTorch和原始Caffe模型实现之间存在细微差异。 - 它在VGG之上添加了一系列额外特征层。如果在构建时
highres参数
为True
,它将附加一个额外的卷积。这对于SSD512版本的模型很有用。 - 如论文第3节所述,原始VGG的全连接层被转换为卷积层,其中第一个使用Atrous。此外,maxpool5的步幅和核大小也被修改。
- 如第3.1节所述,对conv4_3的输出进行L2归一化,并引入一组可学习权重来控制其缩放。
SSD算法
实现中最后一个关键部分在SSD类中。以下是一些值得注意的细节:
- 该算法由一组类似于其他检测模型的参数参数化。强制性参数包括:负责估计特征图的骨干网络,应为配置实例的
anchor_generator
(DefaultBoxGenerator
类),将被调整大小的输入图像大小,以及用于分类的不包括背景的num_classes
。 - 如果未提供头部,构造函数将初始化默认的
SSDHead
。为此,我们需要知道骨干网络生成的每个特征图的输出通道数。最初,我们尝试从骨干网络检索此信息,如果不可用,我们将动态估计。 - 该算法重用了其他检测模型使用的标准BoxCoder类。该类负责编码和解码边界框,并配置为使用与原始实现相同的先验方差。
- 尽管我们重用了标准的GeneralizedRCNNTransform类来调整和归一化输入图像,但SSD算法配置它以确保图像尺寸保持固定。
以下是实现中的两个核心方法:
compute_loss
方法估计SSD论文第5页描述的标准Multi-box损失。它使用平滑L1损失进行回归,并使用标准的交叉熵损失和难负例采样进行分类。- 与所有检测模型一样,
forward
方法目前根据模型是处于训练模式还是评估模式而具有不同的行为。它首先调整和归一化输入图像,然后将它们通过骨干网络以获取特征图。然后,特征图通过头部以获取预测,然后该方法生成默认框。- 如果模型处于训练模式,
forward
将估计默认框与真实标签的IoU,使用SSDmatcher
生成匹配,最后通过调用compute_loss
方法估计损失。 - 如果模型处于评估模式,我们首先通过仅保留通过分数阈值的检测结果来选择最佳检测,选择最有希望的框,并运行NMS以清理和选择最佳预测。最后,我们对预测结果进行后处理,将其调整到原始图像大小。
- 如果模型处于训练模式,
SSD300 VGG16模型
SSD是一个模型系列,因为它可以配置不同的骨干网络和不同的头部配置。在本节中,我们将重点介绍提供的SSD预训练模型。我们将讨论其配置的细节以及用于重现报告结果的训练过程。
训练过程
该模型使用COCO数据集进行训练,其所有超参数和脚本都可以在我们的references文件夹中找到。下面我们提供训练过程中最值得注意的方面的详细信息。
论文超参数
为了在COCO上获得最佳结果,我们采用了论文第3节中描述的关于优化器配置、权重正则化等超参数。此外,我们发现采用官方实现中关于DefaultBox生成器平铺配置的优化很有用。这项优化在论文中没有描述,但对于提高小目标的检测精度至关重要。
数据增强
按照论文第6页和第12页所述,实现SSD数据增强策略对于复现结果至关重要。更具体地说,随机“放大”和“缩小”变换的使用使模型对各种输入尺寸具有鲁棒性,并提高了其对中小目标的精度。最后,由于VGG16具有相当多的参数,增强中包含的光度畸变具有正则化效果,有助于避免过拟合。
权重初始化和输入缩放
我们发现另一个有益的方面是遵循论文提出的权重初始化方案。为此,我们不得不通过撤销ToTensor()执行的0-1缩放来调整我们的输入缩放方法,并使用预训练的ImageNet权重(向Max deGroot致敬,他将它们提供在他的repo中)。所有新卷积的权重都使用Xavier进行初始化,它们的偏差设置为零。初始化后,网络端到端训练。
学习率方案
正如论文中报道的,在应用激进的数据增强后,有必要更长时间地训练模型。我们的实验证实了这一点,我们必须调整学习率、批量大小和总步数才能获得最佳结果。我们提出的学习方案配置相对保守,在某些步骤之间显示出平台期迹象,因此很有可能只需我们纪元的66%即可训练出类似的模型。
关键精度改进细分
值得注意的是,直接从论文中实现模型是一个迭代过程,它在编码、训练、错误修复和调整配置之间循环,直到我们达到论文中报告的精度。通常,它还涉及简化训练配方或通过最新方法对其进行增强。这绝不是一个线性过程,即通过一次改进一个方向来获得增量精度改进,而是涉及探索不同的假设,在不同方面进行增量改进,并进行大量回溯。
考虑到这一点,下面我们尝试总结对我们精度影响最大的优化。我们通过将各种实验分为4个主要组,并将实验改进归因于最接近的匹配。请注意,图的Y轴从18而不是0开始,以使优化之间的差异更明显。

模型配置 | mAP增量 | mAP |
---|---|---|
基线与“FasterRCNN风格”超参数 | —— | 19.5 |
+ 论文超参数 | 1.6 | 21.1 |
+ 数据增强 | 1.8 | 22.9 |
+ 权重初始化和输入缩放 | 1 | 23.9 |
+ 学习率方案 | 1.2 | 25.1 |
我们的最终模型实现了25.1的mAP,并完全复现了论文中报告的COCO结果。这里是准确性指标的详细分解。
希望您发现本系列的第一部分很有趣。在第二部分中,我们将重点介绍SSDlite的实现,并讨论其与SSD的差异。在此之前,我们期待您的反馈。