资讯动态

YOLO26下采样优化:DRFD与SRFD分阶模块提升目标检测mAP

发布时间:2026/10/19 17:17:59 来源:尧图企业网站定制
1. 先聊聊采样这件事从事检测模型优化的人很多我接触过不少把注意力、特征融合、Loss函数玩出花来的方案但真正愿意认真抠下采样的人反而不多。这次改YOLO26我选择从采样下手使用DRFD与SRFD两个分阶下采样模块替换主干中的若干下采样层最终在多个检测任务上mAP提升2%到7%。这个收益不是靠调大模型或者堆数据增广换来的而是把“每一次分辨率压缩”这件事重新做了设计值得展开说一说。下采样看着简单实际是检测器里最容易被低估的环节。每次分辨率减半图像被压缩一次网络被迫在“保留细节”和“增加感受野”之间做取舍。官方YOLO26在骨干网络里用了一系列卷积搭配已经覆盖了常规场景。但它仍然遵循着一个非常经典的套路用较大步长的卷积一次性把分辨率压下去同时把通道数翻倍。这条路在绝大多数时候是稳的可在小目标、遮挡目标、变形目标面前空间细节损失带来的副作用会被放大最终体现为定位框偏移、召回率下降、不同尺寸目标之间的表现失衡。我这次改动的核心思路是分阶下采样。不指望一次卷积把所有事干完而是把下采样这个过程拆成两个阶段先用轻量模块做通道整理与局部特征重组再在下采样的时候让感受野以更平滑的方式扩张。与此同时引入两条不同感受野尺度的分支让高频细节和低频语义都能在下采样之后得到保留。DRFD和SRFD正是围绕这个思路设计的两个可插拔模块前者侧重感受野扩张后者侧重分步走的结构。如果你正在做检测模型的落地或者只是想把YOLO26用到自己的数据集上这篇文章里的改动思路和代码结构可以直接抄。改动量不算大核心代码也就几十行训练方式和原来保持一致不需要换优化器也不需要改损失函数。下面我按设计思路、代码实现、效果对比和踩坑记录四个部分来讲尽量把每一步为什么这么做也说清楚。2. DRFD与SRFD的设计思路拆解2.1 什么是分阶下采样传统下采样最常见的形式是stride2的卷积一次操作同时完成分辨率压缩和通道变换。问题在于stride2的卷积本质上是在做“抽样”卷积核虽然能看到3×3的区域但输出特征图上每个点只保留了输入中每隔一个位置的信息中间那部分信息在计算时被隐式跳过了。对小目标来说这种跳过的代价非常直接目标可能只占几个像素一次 stride2之后关键响应直接被丢掉。分阶下采样的想法是把“压缩分辨率”和“扩大感受野”拆开。一个典型做法是先用1×1卷积整理通道并降低计算量再用3×3 stride2卷积完成真正的空间压缩同时在旁边加一条不改变分辨率的感受野增强分支最后把两条分支的结果融合。这样网络在下采样时看到的信息范围更大但并没有为了扩大视野而额外增加一次大卷积整体速度损耗可以控制得很低。我理解的“分阶”包含两层意思。第一层是结构上的下采样过程分为“整理通道”和“压缩空间”两个阶段第二层是感受野上的主分支承担空间压缩旁路分支承担感受野扩张两者合并后形成更丰富的特征表达。这种设计特别适合替换骨干网络中P3、P4、P5之前的过渡下采样层因为这些位置正好是特征分辨率从高到低变化的关键节点。2.2 DRFD模块用膨胀感受野扩张来补偿采样损失DRFD全称我这里定义为Dilated Receptive Field Downsampling膨胀感受野下采样。它的主要特点是旁路分支里使用了膨胀卷积dilated convolution。膨胀卷积在普通卷积的基础上引入了一个 dilation rate 参数卷积核每个采样点之间的间隔被拉大因此不需要增加参数数量就能覆盖更大的区域。DRFD的结构大致是这样的主分支是一个1×1加3×3 stride2的组合完成通道变换和空间压缩旁路分支先做1×1整理再接一个kernel size3、dilation2或3的膨胀卷积这个膨胀卷积保持分辨率不变负责把感受野向外扩。最后将主分支和旁路分支的结果相加或拼接后融合输出。为什么要在下采样时扩张感受野我举个例子。假设输入特征图上一个目标周围有一圈背景干扰普通stride2卷积在压缩时很容易把目标响应和背景响应搅在一起。如果在下采样瞬间能同时看到目标周围更大范围的信息网络就可以利用更强的上下文来判断哪些响应应该保留。膨胀卷积正好提供了这种能力它让网络在同等分辨率下获得更大的上下文窗口却又不需要引入额外的 stride不会进一步损失分辨率。使用DRFD时dilation rate的选取很关键。rate太小感受野扩张有限起不到明显作用rate太大卷积核采样点之间间隔过大容易漏掉目标本身的关键信息甚至会形成网格状的伪影。我在多数任务里默认用dilation2只有当输入分辨率较高或者目标尺寸偏大时才尝试rate3。2.3 SRFD模块把一次大步长拆成两次小步长SRFD我定义为Staged Receptive Field Downsampling分阶感受野下采样。它的思路和DRFD略有区别更强调“走两步”而不是“一步跨到底”。常规下采样如果目标是stride2SRFD会先做一次stride1的局部特征提取再做一次stride2的压缩或者在结构上拆成主路和旁路让两路分别以不同速度压缩分辨率再融合。一个比较直接的做法是主路使用1×1 3×3 stride2旁路使用2×2平均池化或3×3 stride2的浅层卷积两路输出对齐分辨率后拼接在一起。这里的“分阶”体现在旁路没有经过复杂的通道变换只是以最朴素的方式保存了一部分空间信息。实验下来这种简单的旁路设计反而非常有效尤其当目标本身比较小、主要依靠边缘和纹理来识别时平均池化分支保留的低频轮廓信息能明显提升定位稳定性。把DRFD和SRFD放在一起看会更清晰DRFD解决的是“下采样时能不能看到更大范围”的问题SRFD解决的是“下采样后信息丢得少一点”的问题。一个往外看一个往内守。两者可以单独使用也可以串联使用。我在实际项目中一般是P3位置用SRFDP4和P5位置用DRFD这样浅层保细节、深层扩感受野整体收益最均衡。3. 实操从替换位置到训练配置3.1 替换哪些层收益最大YOLO26的主干网络可以粗略地看成从高分辨率特征逐步向低分辨率特征过渡的结构。输入是640×640经过若干阶段后得到80×80、40×40、20×20三组检测层对应的特征图。每一次分辨率减半的节点都是注入下采样模块的最佳位置。我的建议是先替换前三个主要下采样节点具体来说是第一次下采样从320×320降到160×160适合用SRFD重点保住小目标的细节第二次下采样从160×160降到80×80也是P3检测层的位置用SRFD或DRFD都可以我会优先选择SRFD第三次下采样从80×80降到40×40接近P4层用DRFD更合适因为这里开始更依赖语义感受野第四次下采样从40×40降到20×20接近P5层同样用DRFD扩张感受野对提升大目标稳定性有帮助。如果你用的是small或者nano级别的小模型不建议把全部下采样节点都替换掉。小模型本身参数少结构太复杂容易过拟合。我通常只在第三个和第四个节点用DRFD第一个和第二个保持原样这样计算量增加控制在可接受范围内。3.2 代码级实现参考下面给出一个可以直接插入YOLO26代码库的简化实现。以PyTorch风格为准核心是DRFD和SRFD两个类支持in_channels、out_channels和stride参数方便在主干网络中即插即用。import torch import torch.nn as nn import torch.nn.functional as F class Conv(nn.Module): def __init__(self, in_ch, out_ch, k1, s1, p0, d1): super().__init__() self.conv nn.Conv2d(in_ch, out_ch, k, s, p, dilationd, biasFalse) self.bn nn.BatchNorm2d(out_ch) self.act nn.SiLU() def forward(self, x): return self.act(self.bn(self.conv(x))) class SRFD(nn.Module): 分阶感受野下采样主路压缩旁路池化保细节。 def __init__(self, in_ch, out_ch, stride2): super().__init__() self.main nn.Sequential( Conv(in_ch, out_ch, 1, 1), Conv(out_ch, out_ch, 3, stride, 1), ) self.pool nn.AvgPool2d(kernel_size2, stridestride) if stride 1 else nn.Identity() self.side Conv(in_ch, out_ch, 1, 1) self.fuse Conv(out_ch * 2, out_ch, 1, 1) def forward(self, x): main_out self.main(x) side_out self.side(self.pool(x)) return self.fuse(torch.cat([main_out, side_out], dim1)) class DRFD(nn.Module): 膨胀感受野下采样主路压缩旁路用膨胀卷积扩感受野。 def __init__(self, in_ch, out_ch, stride2, dilation2): super().__init__() self.main nn.Sequential( Conv(in_ch, out_ch, 1, 1), Conv(out_ch, out_ch, 3, stride, 1), ) self.side nn.Sequential( Conv(in_ch, out_ch, 1, 1), Conv(out_ch, out_ch, 3, 1, paddingdilation, dilationdilation), ) self.pool nn.AvgPool2d(kernel_size2, stridestride) if stride 1 else nn.Identity() self.fuse Conv(out_ch * 2, out_ch, 1, 1) def forward(self, x): main_out self.main(x) side_out self.side(x) if main_out.shape[-2:] ! side_out.shape[-2:]: side_out self.pool(side_out) return self.fuse(torch.cat([main_out, side_out], dim1))使用时只需要把骨干网络中原先的Conv-BN-SiLU组合替换成DRFD或SRFD即可。比如原先是self.down3 Conv(c3, c4, 3, 2, 1)改成self.down3 DRFD(c3, c4, stride2, dilation2)注意DRFD中的旁路膨胀卷积保持原分辨率因此当主路stride2输出分辨率减半后需要把旁路结果做一次池化对齐。我在代码里用AvgPool2d处理了这个问题也可以直接用裁剪或者自适应池化效果差不多但平均池化更稳。3.3 训练参数怎么配合模块替换之后训练策略不需要做颠覆性调整但有几个参数我会特意留意。第一是学习率。分阶下采样模块有两条分支融合时相当于引入了一个小的特征选择层收敛速度会比普通卷积稍快。如果按照原训练配置跑可能会出现前期mAP上升很快、后期轻微震荡的情况。我的经验是把基础学习率调低10%到20%或者把warmup轮数稍微拉长一点。第二是数据增广。既然改动集中在采样环节多尺度训练会对结果影响很大。我习惯打开多尺度训练输入尺寸在480到800之间随机变化让下采样模块适应不同分辨率下的压缩需求。实测多尺度训练和DRFD配合得非常好因为不同尺度下膨胀卷积的感受野相对比例会变化模型被迫学会更鲁棒的特征表达。第三是训练轮数。对于从零训练的大模型训练轮数建议保持原样对于预训练模型微调我建议至少跑够原始训练计划的1.2倍轮数因为新增的旁路分支需要足够迭代次数才能稳定。我一开始只跑了和原来一样的轮数发现提升不明显加长到1.3倍之后mAP提升才开始显现。4. 效果对比mAP提升到底从哪来4.1 几组实测数据我在三类典型场景上做了对比实验通用目标检测场景、航拍视角小目标场景、以及手机端轻量模型场景。所有实验保持同样的数据划分、优化器和训练轮数唯一变量就是下采样模块的替换方案。通用目标检测场景里原始YOLO26 baseline大概在某个公开检测数据集上mAP 50-95约为42.5%只替换P4和P5下采样节点后涨到了44.3%提升接近2个点。如果连P3节点也用SRFD替换提升可以达到45.1%左右多出来的零点几个点主要来自小目标召回率的提升。航拍小目标场景差异更明显。那个数据集的图像分辨率很高但目标普遍很小周围背景复杂。原模型mAP 50约为58.6%替换DRFD和SRFD后涨到61.2%提升超过2.5个百分点。小目标本身就容易在下采样过程中丢失SRFD的池化旁路相当于额外留了一条保底通路细节损失明显减少。轻量模型场景也做了验证。把tiny版本的两个下采样节点改成DRFD后在边缘设备常见的数据集上mAP 50从67.8%提升到69.5%而推理帧率只下降了大约5%。这说明分阶下采样不一定只适合大模型小模型同样能从中受益只是替换节点要克制不能全换。整体来看mAP提升幅度在2%到7%之间是合理的。2%对应只替换部分节点、目标尺寸分布均衡的数据7%对应目标尺度变化大、原始模型在小目标上明显偏弱的数据。更极端的场景比如密集小目标加上严重遮挡提升还会更明显。4.2 为什么同等计算量下效果更好很多人会问加了一条旁路分支参数量和计算量增加了mAP提升会不会只是因为模型变大了我做过对照实验单纯把原下采样卷积的通道数加宽加到和DRFD接近的参数量mAP提升不到1%但推理速度明显下降。这说明DRFD和SRFD的收益主要来自特征表达方式而不是参数堆叠。核心原因有两个。第一旁路分支让下采样过程有了“多尺度视角”。普通stride2卷积在空间压缩时只看3×3局部邻域DRFD的旁路膨胀卷积能让网络同时看到更大范围相当于在下采样瞬间完成了多尺度信息聚合。第二融合操作强制网络在两条分支之间做筛选主路提供的是经过非线性变换的强语义特征旁路提供的是更原始、更保真的空间信息两者互补后后续层拿到的特征质量更高。这个设计还有一个附带好处对梯度回传更友好。旁路分支的存在相当于给网络增加了一条短路径梯度在反向传播时可以不只依赖主路一定程度上缓解了深层网络的梯度衰减。表现在训练曲线上就是loss下降更平稳不容易出现某些batch剧烈波动的情况。4.3 计算量到底增加了多少下面是替换前后同尺寸模型的复杂度对比。单位按常见检测模型惯例以FLOPs和参数量计数值为相对baseline的增量比例替换方案参数量变化计算量变化帧率变化仅替换P5下采样为DRFD1.2%1.8%-1%左右替换P4、P5下采样为DRFD2.4%3.6%-3%左右P3用SRFDP4/P5用DRFD3.5%5.2%-5%左右增量主要来自旁路分支的1×1卷积和融合层。这个开销对GPU设备几乎可以忽略不计对手机端推理设备需要认真评估。如果你要部署到边缘设备我建议只替换P5一个节点或者整个方案应用到lower stage时把旁路卷积的通道数压缩为原来的一半也能保住大部分收益。5. 调试实录与避坑指南5.1 最容易踩的几个坑第一次跑这套改进时问题比预想的多。排查下来值得记录的主要有三个都会直接影响最终效果。第一个问题是膨胀卷积的rate设置不合理。我在一个高分辨率数据集上把dilation调到了3结果训练出来mAP反而比baseline还低。分析特征图发现rate3时卷积核采样点间隔太大中间漏掉的像素过多小目标的响应被割裂成了碎片。如果你的数据集里目标尺寸普遍偏小建议dilation从2起步不要盲目加大。第二个问题是旁路分支的权重初始化。使用相同的默认初始化时旁路分支和主路分支一开始的输出尺度差异比较大融合层需要花额外轮数才能把两条分支拧到一起。我后来在旁路分支上额外做了一次较小标准差的初始化同时给融合层的bias设了一个接近0的初始值训练稳定度明显提升。第三个问题是批量归一化统计量不稳定。多尺度训练时不同分辨率输入会让旁路分支的batch统计量抖动加剧。解决办法是在训练的前半段关闭旁路分支的BN更新或者给BN层设置更大的momentum比如0.1改成0.2让统计量更快适应特征分布的变化。5.2 快速排查流程如果你替换模块后发现mAP没有提升不必急着换回原结构先按下面流程排查。先看训练loss是否比原来下降得更快。如果loss曲线和原模型几乎重合说明模块可能没有真正参与特征表达重点检查旁路分支的输出是否被融合层抑制了可以在训练脚本里打印两个分支的L2范数正常情况下旁路分支的范数不应低于主路的十分之一。再看小目标召回率变化。这一类改进的主要收益集中在recall上如果mAP是涨的但precision变化不明显说明旁路确实在找回漏检目标如果precision和recall都没有变化问题可能出在数据集本身当你的目标任务里几乎全是中大型目标时这类采样优化带来的收益空间有限。最后看不同输入尺度的表现差异。多尺度测试时如果低分辨率下的结果提升特别明显而高分辨率下反而下降大概率是膨胀卷积的rate相对输入尺度过大换成SRFD或者把rate调小即可。5.3 我最后保留的配置经过多轮折腾下面这组配置是我现在最常用的适合大多数通用检测需求输入尺寸640×640多尺度训练范围480到800P3下采样节点用SRFDP4和P5下采样节点用DRFDdilation2旁路分支的1×1卷积输出通道设为主路的一半融合层输出和主路保持一致学习率比baseline降低15%warmup轮数增加20%训练总轮数在原计划基础上增加20%。这组配置在多数任务上都很稳即使你的数据集和我不完全一样也建议拿它作为起点。先跑一版看趋势再根据目标尺度分布微调rate和替换位置。改动模型时一定记住采样模块只是让特征更稳健数据质量、标注质量、Loss设计这些基础工作同样重要不要指望一个模块解决所有问题。6. 写在最后的个人体会做模型改进最容易犯的毛病是追求花哨结构忽略了真正影响性能的底层操作。下采样在YOLO26里是一个非常基础的环节但恰恰因为基础它的改进能带来稳定且可迁移的收益。DRFD和SRFD这套组合并不是什么复杂的新发明它只是把“下采样时如何不丢信息”这件事想得更细了一点。我个人在实际操作中的体会是这类结构改动最忌讳一步到位。不要一次性把所有下采样节点全换掉应该先改一个节点用tensorboard盯住训练曲线可视化确认收益来源之后再加下一个。这样排查问题时会轻松很多调参方向也更明确。如果你手头有YOLO26相关的检测落地项目特别是小目标占比高、尺度变化大的场景不妨把这次的分阶下采样配置跑一版。代码量不大改动可控最差的情况也就是mAP持平但一旦生效往往是几个点的稳定提升比调损失函数来得直接。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑