多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

SSD目标检测算法精讲:从先验框到工程实践

SSD目标检测算法精讲:从先验框到工程实践 1. 为什么至今还要回头啃透SSD我第一次跑通SSD是在一个非常尴尬的场景下实验室的显卡还是GTX 1080显存只有8G手头的数据集是自采的工业零部件图片目标尺寸从几十像素到几百像素不等。当时主流的Faster R-CNN系列训练一轮要几个小时而SSD是少有的能够在单卡上快速迭代、推理速度又跑得起来的方案。几年过去YOLO系列已经迭代到第八代、DETR系也成了新宠但每当有新手问我“目标检测第一口吃哪块比较合适”我还是会推荐先把SSD彻底吃透。原因很简单SSD是连接“单阶段检测”和“多尺度特征融合”的一座关键桥梁。它第一次把“在不同特征图层级上做检测”这件事做到了工程可用的程度也第一次把“预设锚框”这个思想发挥到极致。理解了SSD你再看YOLOv3的FPN、RetinaNet的Focal Loss、甚至Transformer检测器里的object query设计都会有一种“原来都是从这儿长出来的”的熟悉感。这篇博客我尽量把SSD从头拆到脚争取让完全没接触过检测的读者也能照着复现自己的模型。这篇内容适合谁两类人。第一类是刚学完CNN、准备从分类跨到检测的初学者你需要的是一个能端到端跑通且原理不复杂的模型SSD就是很好的起点。第二类是已经在用YOLO或Faster R-CNN做项目、但对检测框架底层的先验框生成、正负样本匹配、损失函数计算细节一知半解的人这篇文章会把这几块翻个底朝天。我会把原理、公式、代码和踩坑记录混在一起讲尽量做到“看完就能上手”。2. SSD的设计思路与整体架构2.1 单阶段检测的“快”到底是怎么来的目标检测器长期以来分为两大流派。两阶段检测器的代表是Faster R-CNN它先通过RPN网络生成一堆“可能含有目标的区域”RoI然后再对这些区域做逐一的分类和框回归。这套流程准确率上去了但速度被“两阶段”这个结构卡死一张图片在GPU上跑个几十毫秒是常态。而SSD属于单阶段检测器它把“找区域”和“分类回归”合并到了一个前向传播里网络一次性输出所有目标的类别概率和边框偏移量没有RoI池化这类中间环节所以速度起得快训练也相对容易收敛。但“一次到位”也带来了一个核心难题物体在图片里的尺寸差异极大。近处的人可能占满整个画面远处的小车可能只有几十个像素。如果在单一特征图上做检测小目标和高层语义特征天然不兼容小目标的细节信息在多次卷积池化之后已经丢失得差不多了。SSD的核心创新就是对症下药它从主干网的不同卷积层抽出六层特征图让每一层负责检测不同尺寸的目标。浅层特征图分辨率高、感受野小负责小目标深层特征图分辨率低、语义信息强负责大目标。这就是SSD名字里“MultiBox”的由来——在不同尺度的盒子上做检测。2.2 SSD的整体网络结构拆解先看SSD的整体结构我不会贴一大堆框架图而是用文字带你把数据流走一遍这个过程理清楚了后面的细节就好讲了。输入图片尺寸是300x300或512x512这篇以300x300为主。图片先经过一个去掉全连接层和最后分类层的VGG16作为骨干网络。对VGG16的改造有一点值得注意原版VGG16的fc6和fc7被替换成了卷积层并且用了空洞卷积dilation6来保持感受野的同时降低参数和计算量。这块处理在很多博客里只提了一句但实际影响很大它让VGG16在保持前两个全连接层语义厚度的情况下输出特征图尺寸不至于缩水太多。主干网之后SSD接了六个用于预测的特征层我直接列出尺寸和通道数Conv4_338x38x512负责检测非常小的目标Conv7fc7改造后的卷积层19x19x1024负责检测稍大的目标Conv8_210x10x512Conv9_25x5x256Conv10_23x3x256Conv11_21x1x256从Conv7往后每经过一层卷积特征图的宽高缩半通道数也做了适配。这六个层实质上构成了一个“自然形成的特征金字塔”它不是像FPN那样通过上采样融合了高层信息而是简单地抽取不同卷积深度的输出设计上更加轻量。每一个特征层上的每一个像素点也叫锚点、cell都会生成一组固定数量和固定尺寸的先验框default box。比如Conv4_3的每个cell生成4个先验框而Conv11_2的每个cell生成4个中间的层有的生成6个。全图加起来一共是8732个先验框这个数字不是拍脑袋定的它是每一层特征图尺寸乘以每层每个cell的框数量累加出来的。每个先验框对应两路输出一路是类别置信度包括背景类共21类VOC数据集背景加20类目标另一路是边框的四个偏移量中心点x偏移、中心点y偏移、宽度缩放、高度缩放。这两路输出都是通过一层3x3的卷积直接在特征图上生成这也是SSD被称为“end-to-end单阶段检测器”的底气所在。2.3 为什么是VGG16而不是更新的主干网络用VGG16做骨干在现在看来有些“复古”但SSD设计时ResNet虽然已有可配套的预训练权重和工程生态不如VGG成熟。VGG16的特点在它的结构非常规整全是3x3卷积加最大池化的堆叠改造起来特别方便直接在ImageNet上训好的权重拿过来做迁移学习也很容易。另一个隐藏的理由是VGG16的网络深度刚好适配了SSD的多尺度设计思路。VGG16不同层的特征图语义层次分明浅层捕获边缘和纹理中层捕获部件信息深层捕获整体语义这本身就是天然的多尺度划分。换个更深的网络反而要操心梯度流动和特征层抽取位置的选择。不过在你的实际项目中我建议不必拘泥于VGG16。如果你能看到这篇博客的代码实践部分会发现替换主干网其实是个特别顺手的事情只要保证抽出的特征层有对应的分辨率后面的检测头部可以原封不动地复用。我自己用ResNet50替换过VGG16做SSDmAP大致提升了1到2个点代价是速度略降。这种设计的可插拔性也是SSD值得学习的地方。3. 先验框的生成机制详解3.1 先验框的中心点坐标和尺寸计算过程先验框的理解是整个SSD里最重要的一块也是各类博客最容易一带而过的地方但如果你不搞清楚它后续的正负样本匹配和对损失函数的理解都会像看天书一样。我尝试把整个生成过程缩小成一个具体的例子来说明。假设我们要在38x38的Conv4_3层上生成先验框。这一层特征图的尺寸是38x38输入图片是300x300所以特征图相对于原图的步长stride是300/38≈7.89。这意味着特征图上的第(i,j)个cell对应到原图上是以((j0.5)*stride, (i0.5)*stride)为中心的一个区域。加0.5是为了取cell中心点这在目标检测里是通用惯例。有了中心点接下来要确定每个cell生成的先验框的长宽和大小。SSD的尺寸设计遵循一套线性递增规则六层特征图的先验框尺寸scale从min_size0.2逐步递增到max_size0.9这里的0.2和0.9是相对于输入图片尺寸的比例。具体到300x300的输入Conv4_3层的先验框尺寸为0.2*30060像素下一层的尺寸由公式计算得来[ s_k s_{min} \frac{s_{max} - s_{min}}{m - 1}(k - 1) ]其中m是特征层数量这里是6k从1开始。逐层算下来六个特征层的先验框尺寸分别是60、111、162、213、264、315像素。每一层的先验框还有不同的长宽比aspect ratio常见配置是{1, 2, 3, 1/2, 1/3}再加上一个长宽比为1但尺寸为sqrt(s_k * s_{k1})的额外框。长宽比不同但面积相近是为了匹配不同形状的物体——行人通常瘦长汽车通常扁平。我经常被问到为什么要用sqrt(s_k * s_{k1})这个几何平均尺寸而不直接用s_{k1}。答案是为了让先验框在相邻层之间形成过渡避免某些高瘦或宽扁物体跨层漏配。这个细节属于“微调但有效”的设计。3.2 每个cell生成几个框全图加起来怎么是8732不同层每个cell生成的先验框数量还不一样。拿SSD300的默认配置来说Conv4_3每个cell生成4个框长宽比有{1,2,1/2}加一个额外尺寸框所以4个Conv7、Conv8_2、Conv9_2每个cell生成6个框长宽比覆盖{1,2,3,1/2,1/3}加额外尺寸框Conv10_2、Conv11_2每个cell生成4个框那么全图先验框总数就是38x38x4 19x19x6 10x10x6 5x5x6 3x3x4 1x1x4算一下5776 2166 600 150 36 4 8732。这8732个框里绝大多数都是背景真正能匹配到目标的可能只有几十个这个巨大的不平衡后续要靠特殊的匹配策略和损失函数设计来缓解。在实际实现中每个先验框的表示是一个长度为4的向量(cx, cy, w, h)分别是归一化后的中心点x坐标、中心点y坐标、宽度和高度全都除以了图片宽高保证在0到1之间。这种归一化表示让模型对不同尺寸的输入图片没那么敏感也方便后续的边框回归。3.3 先验框与Ground Truth的匹配策略先验框生成了那训练时的监督信号是怎么来的呢这是整个SSD训练流程中最具技巧性的一环。对于每一张训练图片我们有若干标注框也就是ground truth简称GT。第一步计算所有8732个先验框和每个GT框的IoU交并比对每个GT框把IoU最高的那个先验框选为正样本。这一步保证了每个真实目标至少有一个先验框可以学习。第二步对剩下的先验框只要它和任何一个GT框的IoU大于阈值0.5也标记为正样本。这个“IoU阈值匹配”的做法非常关键它允许一个GT框匹配多个先验框让正样本数量不至于过少。这里我想强调一下0.5这个阈值的选择逻辑。阈值设高了正样本数量减少模型容易漏检阈值设低了大量低质量的框被当成正样本模型学到的是“模糊的”目标位置。0.5是Faster R-CNN和SSD用了多年验证下来的一个经验值工程上稳健。其余未能匹配上任何GT框的先验框全部归为负样本。但负样本数量太大如果全部参与损失计算模型会倾向于把所有框都预测为背景而且训练极度不平衡。SSD的处理办法是硬负样本挖掘把所有负样本按置信度损失从高到低排序选取损失最高的那些框让负正样本数量比保持在3:1左右。这个“难例挖掘”思想后来也被大量检测算法沿用比如RetinaNet里的Focal Loss本质上是要解决同一个问题只是实现方式不同。4. 训练过程中的关键细节与损失函数4.1 正负样本确定之后损失函数怎么算SSD的损失函数是定位损失和置信度损失的加权和公式写出来很简洁[ L(x, c, l, g) \frac{1}{N}(L_{conf}(x, c) \alpha L_{loc}(x, l, g)) ]N是正样本的数量。如果N为0损失直接置0。这里最容易被忽略的是那个正则化系数α默认取1也就是说定位和分类的权重是等同的。但在实际任务里如果你的目标类别特别多或者目标尺寸特别小建议给定位损失加权调到1.2到1.5效果可能会有惊喜。置信度损失用的是多类softmax交叉熵。SSD不是用sigmoid做二分类而是对包括背景在内的所有类别算一个softmax概率然后取交叉熵。这意味着每个先验框必须被明确标记为“背景类”或“某个具体目标类”背景类在所有类别中占据一个位置。定位损失用的是Smooth L1函数对预测框和GT框之间的偏移量做回归。偏移量的计算方式是个容易踩坑的点它不是直接回归中心点坐标和宽高而是先做了编码。具体来说对于先验框d和GT框g回归目标是[ \hat{g}{cx} (g{cx} - d_{cx}) / d_w ] [ \hat{g}{cy} (g{cy} - d_{cy}) / d_h ] [ \hat{g}{w} \log(g_w / d_w) ] [ \hat{g}{h} \log(g_h / d_h) ]这样做的好处是让回归的目标值标准化不会因为目标尺寸差异大导致损失波动剧烈。预测时再做反编码把预测偏移量应用到先验框上得到最终的预测框。多个检测器框架Faster R-CNN、YOLO都有这个编码过程但SSD是少数会明明白白告诉你“我在这一步就做了编码”的模型。4.2 数据增强策略及其重要性SSD原文里有一个容易看漏但影响巨大的章节数据增强。SSD使用了多种随机采样策略包括随机裁剪、缩放、颜色扭曲、水平翻转等。其中效果最明显的是一种“目标裁剪式”增强每张图以一定的概率原文是0.5随机裁剪一个patch但要求这个patch和某个GT框的IoU在0.1到0.5之间然后把这个patch缩放回300x300作为训练输入。这种增强方式本质上是人为制造了大量“目标位于图像边缘、只有部分可见”的训练样本对提升模型定位能力帮助非常大。很多复现SSD的代码跳过这个过程或者简单实现导致模型在复杂场景下漏检率偏高还以为是网络结构出了问题。我在工程实践中把这种增强完整实现之后mAP提升在2到3个点属于成本最低的提分手段。4.3 训练过程中的关键超参数配置参考超参数这块我直接给一份我实际跑通过且效果稳定的配置这些数值不是抄来的是在VOC数据集上调过几轮留下的记录优化器SGDmomentum0.9weight_decay0.0005初始学习率1e-3batch_size32如果显存不够batch_size降到16时学习率同步降到5e-4学习率调整在第80个epoch和第100个epoch分别乘以0.1总共训练120个epoch先验框匹配IoU阈值0.5负正样本比例3:1关于学习率有个实用经验SSD对初始学习率比较敏感太大会出现损失震荡太小则收敛极慢。如果发现loss在某个区间反复横跳优先检查学习率和batch_size的匹配而不是盲目加数据。VOC0712数据集上这个配置在SSD300上能跑到77到78的mAPSSD512能到79到80。虽然不是顶尖成绩但作为复现参考这个数字是合理的。5. 从零搭建并训练一个SSD检测器5.1 准备数据集和预训练权重实操环节我选择用VOC格式的数据集做示范因为这是SSD原始训练所使用的数据格式。VOC数据集的标注是XML文件每个文件对应一张图片里面的object节点包含name和bndbox坐标信息。如果你用的是自己的数据建议先转成VOC格式因为SSD的多数开源实现都自带VOC格式的数据加载器。数据准备好之后第一步是下载VGG16在ImageNet上的预训练权重。这里有个很容易犯的错直接用torchvision提供的vgg16权重是不行的因为SSD修改了VGG16的网络结构——把fc6改为卷积层、fc7改为卷积层且通道数变了、最后两个全连接被移除。正确的做法是先加载原版vgg16权重然后手动把匹配的部分赋给SSD的骨干网络fc6/fc7对应部分再单独做参数初始化。我提供一个简化的思路用PyTorch的state_dict字典按参数名匹配的方式加载。SSD骨干网络conv1_1到conv5_3这部分的参数名和原版VGG16是保持一致的所以可以直接加载而fc6到conv7这部分需要自己手动转换维度后初始化。如果你用的是现成的SSD开源实现里面通常已经写好了加载逻辑但了解这个过程很有必要。5.2 核心模型代码解读我不打算贴整个模型的完整代码那样篇幅太长这里只挑最核心的“多尺度检测头”和“先验框生成”两部分来做解析。先看先验框生成的核心逻辑。用PyTorch实现的话关键点在于在创建模型时就预先计算好所有8732个先验框的坐标存成一个tensor这样训练和推理时可以直接查表省去了每张图片重新计算的成本def default_prior_box(feature_map_list, image_size): # feature_map_list: 每层特征图的尺寸列表例如[(38,38),(19,19),...] # image_size: 输入图片尺寸这里取300 prior_boxes [] scales [0.1, 0.2, 0.37, 0.54, 0.71, 0.88, 1.05] # 各层先验框尺寸 aspect_ratios [[1,2,0.5],[1,2,3,0.5,0.333],[1,2,3,0.5,0.333], [1,2,3,0.5,0.333],[1,2,0.5],[1,2,0.5]] for idx, (feature_h, feature_w) in enumerate(feature_map_list): stride image_size[0] / feature_w for i in range(feature_h): for j in range(feature_w): cx (j 0.5) * stride / image_size[0] cy (i 0.5) * stride / image_size[1] for ar in aspect_ratios[idx]: w scales[idx] * math.sqrt(ar) h scales[idx] / math.sqrt(ar) prior_boxes.append([cx, cy, w, h]) # 额外生成一个sqrt(s_k * s_{k1})的正方形框 ... return torch.tensor(prior_boxes)这段代码里有两个细节值得注意。第一scales列表我用了7个值而不是6个因为最后一层需要用到s_{k1}来计算额外正方形框。第二所有坐标都除以image_size做了归一化这一点千万不能丢否则后续的坐标解码和IoU计算会出错。再看多尺度检测头。SSD的本质就是对六个特征图分别做两个卷积一个输出类别置信度一个输出边框偏移。每个特征图上的卷积核数量是固定的对这个设计做一个计算def build_heads(feature_channels, num_classes): # feature_channels: 每层特征图的通道数例如[512,1024,512,256,256,256] # num_classes: 类别数含背景例如VOC是21 heads [] for ch, num_boxes in zip(feature_channels, [4,6,6,6,4,4]): loc_conv nn.Conv2d(ch, num_boxes * 4, kernel_size3, padding1) conf_conv nn.Conv2d(ch, num_boxes * num_classes, kernel_size3, padding1) heads.append((loc_conv, conf_conv)) return headsnum_boxes * 4中的4对应的是中心点x、中心点y、宽度、高度四个偏移量num_boxes * num_classes对应的是每个先验框的类别概率输出。设计上的巧妙之处在于卷积核尺寸只有3x3参数非常少所有检测头加起来的参数量比重不是骨干网络的零头训练开销主要都花在骨干上。5.3 训练脚本的核心逻辑训练过程的主循环大体是常规的深度学习训练流程但有几步值得单独讲解。第一数据加载器要在每张图片上做先验框和GT框的匹配这一步不能用GPU加速必须放在CPU上事先算好。我的做法是在数据集的__getitem__里完成匹配把每个先验框的类别标签和偏移量目标存成tensor再和图片一起送入模型。这样训练循环里只需要做forward和loss计算代码干净调试也方便。第二损失计算前要做一次正负样本筛选。模型输出的分类置信度得分可以反映当前预测的好坏负样本挖掘就利用这个信息来筛出“最难”的那些负样本框参与损失计算。注意挖掘过程是每一轮训练都要重新算一遍的因为模型在变难例也在变。第三定位损失只在正样本上计算。负样本虽然参与了分类损失但不会参与定位损失这个细节一定要体现在代码里否则负样本的定位信息是噪声会干扰模型收敛。我放一个简化版的训练核心逻辑作为参考for images, targets in dataloader: images images.to(device) pred_locs, pred_confs model(images) # 所有先验框的偏移和置信度 # 计算匹配后的目标 gt_locs, gt_confs match_priors(priors, targets) # 已预先算好 # 负样本挖掘 neg_mask (gt_confs 0) # 背景 pos_mask (gt_confs 0) # 前景 # 置信度损失所有正样本 挖掘出的负样本 conf_loss cross_entropy(pred_confs[mask], gt_confs[mask]) # 定位损失只有正样本 loc_loss smooth_l1(pred_locs[pos_mask], gt_locs[pos_mask]) loss conf_loss alpha * loc_loss loss.backward() optimizer.step()5.4 推理阶段的解码和后处理训练完成后推理阶段需要把网络输出的偏移量“翻译”成真实的边框坐标这一步包含三个子步骤解码、置信度过滤、NMS。解码是编码的逆过程。拿到预测偏移量(delta_cx, delta_cy, delta_w, delta_h)后对每个先验框d做如下操作pred_cx delta_cx * d_w d_cx pred_cy delta_cy * d_h d_cy pred_w exp(delta_w) * d_w pred_h exp(delta_h) * d_h解码之后的坐标是归一化的需要乘回图片尺寸才是实际像素值。这里最容易出的bug是很多人忘记先验框的坐标已经做了归一化直接拿原图尺寸去乘结果框的位置全乱了。排查这类问题有个小技巧拿一张训练集的图片做推理如果预测框偏移得离谱先去看解码这一步把中间张量打印出来人工验证。置信度过滤通常是先按类别过滤对每个类别单独取出置信度高于0.5这个值可以根据场景调整的框然后做类别内的NMS。NMS的IoU阈值一般取0.45或0.5。需要提醒的是NMS必须按类别分别做不能把所有类别的框混在一起做否则不同类别的重叠目标会被误删。低阈值比如0.3适合密集小目标场景但会牺牲一些重叠目标的召回率高阈值比如0.6适合稀疏大目标场景误检会略有增加。6. 实际训练中常见问题与调试经验实录6.1 训练不收敛或loss为NaN的排查路径训练不收敛是新手最常碰到的问题这里整理几个高频原因和对应的排查方法。Loss变成NaN是一个让人头大的问题。我的经验是优先检查学习率如果初始学习率超过5e-3在batch_size不大时很容易梯度爆炸。其次检查数据预处理图片像素是否除以255做了归一化标注框的坐标是否存在负数或超出图片边界这些数据层面的“脏数据”会让损失计算出现异常值。还有一种相对隐蔽的情况先验框匹配后没有正样本。如果某个batch的所有GT框都没有匹配到任何先验框比如GT框特别小且位置极端那么N0按SSD的定义损失应该置0但如果代码的处理是直接除N就会出现除零问题。少数实现里这种情况会反馈为loss突然掉到0看起来像收敛了实际上模型什么都没学到。6.2 mAP低或漏检严重时的调优顺序如果训练过程正常但mAP不理想我建议按以下顺序排查这是一个“从数据到模型再到训练策略”的链路第一步看数据。检查标注框是否准确尤其是小目标的标注是否贴合边缘。很多时候mAP低不是模型的锅是标注粗糙导致训练信号本身就带噪声。第二步看先验框的尺寸设计。SSD的六层先验框尺寸是围绕VOC/COCO的常规目标尺寸分布的如果换成你自己的任务比如检测医学图像里的微小病灶、或者工业场景里的细小瑕疵原来的先验框配置大概率不合适。这时需要单独统计你的数据集中GT框的尺寸和长宽比分布然后调整min_size、max_size和长宽比列表。第三步看正负样本匹配。IoU阈值0.5是默认值但如果你的目标本身就很小IoU计算对像素级偏差特别敏感0.5的阈值可能偏严适当降到0.4会让小目标的正样本数增多。代价是引入了更多低质量正样本定位精度会有所下降这个取舍要根据任务来判断。第四步看数据增强。如果你的数据集只有几千张图没有完整的数据增强流程过拟合和泛化能力差是必然的。尤其是那些对目标进行随机裁剪、多尺度采样的增强对检测效果的帮助比在分类网络里看到的要明显得多。6.3 我对SSD速度调优和显存控制的一些心得SSD的高速优势在你刚完成复现时可能不太明显因为默认batch_size和输入分辨率会限制推理速度。如果要在边缘设备或低显存环境下跑SSD有几个实用的优化手段按收益从高到低排列输入分辨率选择300而不是512。300x300输入时六个特征层的计算量大幅减少推理速度能提升近一倍。如果检测的都是中小型目标512的优势并不明显反而需要更多算力。特征层裁剪。不是所有任务都需要六层特征图。如果你的目标都比较大可以去掉Conv4_3这一层直接用19x19及以下的特征层做检测参数量能减少一大截速度提升明显。代价是丢失了小目标的检测能力。用batch size1做推理时显存占用主要由输入图片和中间特征图决定。可以尝试在推理阶段用fp16精度SSD的结构对精度下降非常不敏感实测在普通显卡上能带来约30%到50%的显存节省和速度提升。6.4 一个典型调试案例的复盘最后分享一个我印象深刻的调试案例。有个项目需要在工业相机拍摄的流水线图片上检测若干种不同尺寸的电子元器件图片分辨率很高4000x3000但目标很小最小的只有约20x20像素。最初我直接用了标准SSD300mAP不到50漏检严重。排查过程如下先统计数据集中GT框的尺寸分布发现大多数小目标的尺寸在输入300x300图片上只能占据2到5个像素这个尺寸超过了Conv4_3的检测极限。于是做了两件事一是改用SSD512并在训练时加入了原始图片的多尺度随机裁剪增强二是把Conv4_3层的先验框最小尺寸从0.1调低到0.07并在长宽比里增加了2.5和0.4两个更极端的比值。改动之后mAP提升到68漏检率下降了一半。这个案例想说明一个道理SSD的参数配置不是固定不变的它是你的任务数据的“函数”。花时间认真理解每一层特征图的检测能力区间比盲目堆数据和调学习率要有效得多。7. SSD的边界与它留给我的东西用SSD做了几年检测之后我越发觉得它的最大价值不在于“当下最强”而在于它提供了一套极其干净的检测框架。它把先验框、多尺度特征、匹配策略、难例挖掘这些概念第一次成体系地组装在一起之后的检测算法不管怎么演变都绕不开这套底层的“工程语言”。当然SSD也有明显的短板它没有FPN那样跨层特征融合的机制对极小目标天然不友好它的先验框是手工设计的在不同数据集之间迁移时总要手工调参它的分类和回归头共享特征但没有显式的注意力机制在复杂背景下容易产生误检。这些都是后来者改进的方向也是你现在值得继续向YOLOv4、EfficientDet、DETR推进的理由。我个人的建议是如果你想做项目落地把SSD当作“快速基准”来用如果你想打比赛或者发论文SSD的代码框架是极好的实验平台快速替换主干、调整先验框配置、测试新的损失函数它都能给你最快的反馈。在实际项目中我通常用SSD快速跑通baseline用它的预测结果分析数据难点然后再决定是继续调SSD还是迁移到更重的模型上去。这个习惯来自一个朴素的观察一个你完全理解底层逻辑的简单模型在工程调试中往往比一个你只会调参的黑盒大模型更有用。
返回列表