资讯详情

资讯详情

YOLOv11改造:BiFPN+RepVGG提升小目标检测与部署效率

第一次在检测结构里动刀子我选的切入点就是颈部。原因很直白主干再怎么堆 C2f、C3最后都要靠 FPN 这一层把多尺度信息揉匀颈部结构观感上是“连接件”实际却决定了小目标、遮挡目标、不同分辨率输入到底能不能被检测头同时接住。我拿 YOLOv11 作为试验床把 PANet 换成 BiFPN再把主干里适合重参数化的卷积块换成 RepVGG 风格折腾完复盘时发现这个组合真正解决的不是“某个模块比另一个模块准”而是让网络在精度、延迟、部署自由度之间找到了更合理的平衡点。这篇文章适合正在改 YOLO 系检测器、想提升多尺度目标尤其是小目标召回率的人也适合对 BiFPN 与 RepVGG 只停留在“听过”阶段、想弄明白它们为什么能放进 YOLOv11 的同学。我不打算把理论吹得太高所有结论都从实际训练和导出推理的过程里来能直接照着动手。1. 先搞清楚重构的目标YOLOv11 到底缺在哪个环节上手改造之前我花了很久重新做减法。YOLOv11 相比 v8 在家底上已经不算差主干、颈部、头部都有各自的迭代但如果你把一次前向传播的 FLOPs 分布铺开看颈部往往是最容易膨胀又最容易被忽略的地方。模型在主干网络里提取了多个尺度的特征颈部的工作是让这些尺度互相交换信息交换方式不同直接决定最终特征图的表达能力。1.1 从 FPN 到 PANetYOLO 系列的颈部演进脉络FPN 是经典的从上到下路径最高层的强语义特征向低层传播提高小目标的属性表达能力但它的信息流只有一个方向低层细节很难回传给高层。PANet 加了一条从下到上的路径相当于在高语义和高分辨率两个方向都做了广播这点对检测头特别重要因为大目标和小目标分属不同层级单方向传播永远有一方吃亏。YOLOv8、YOLOv10 沿用的就是 PANet 的拓扑思想具体实现上用 CSP 结构把上采样和下采样的特征做拼接。这套结构在多数数据集上稳定但问题也随之而来逐级多层的特征拼接会引入大量没有实际信息增量的特征通道融合权重对所有输入一视同仁等于默认“每一层贡献相同”。实操中大家都能感受到小目标多的场景里高层语义和浅层纹理经常打架融合出来的特征既不纯也不纯的均衡。1.2 BiFPN 要解决的是指纹级的梯度路径与权重问题BiFPN 首先做的一件小事特别关键把只有一条输入边、没有合并作用的节点直接删掉。听起来简单实际对计算图影响很大因为这类节点在反向传播时只有“转发”价值保留它们就是在浪费模型容量。然后是跨尺度连接。标准 PANet 从头到尾是逐级串行的BiFPN 给每个融合节点多引一跳让同层输入可以跨级跳跃顶层的大感受野信息可以近乎无衰减地参与底层融合。最核心的差异化在于“加权融合”不是简单的 tensor 相加而是用一对可学习权重对多个输入特征做归一化加权让网络自己学会“这一层该信谁多一些”。这三个动作加在一起颈部就不再是无差别的搬运工而是有选择的滤波器。1.3 RepVGG 放在这里目的是把训练成本换成部署收益RepVGG 给我最直接的冲击不是精度是“同一个 block训练时可以长成多分支结构推理时重参数化回一个 3x3 卷积”。这种训练复杂、推理简单的结构和 YOLO 的部署哲学天然匹配。训练阶段多分支提供更强的梯度空间优化器可以更充分地探索推理阶段把三条分支压缩成一个矩阵乘延迟和显存占用都大幅下降。把 BiFPN 和 RepVGG 凑在一起原因也不复杂BiFPN 提升了模型的表达能力代价是参数量和计算量都有所上升RepVGG 把部分卷积模块转换成单路径结构抹平了这部分成本。一个负责涨点一个负责往回找补延迟组合起来才是架构层面的革新而不是单纯叠加模块。2. BiFPN 原理拆解加权融合、去节点与双向往返这一节不聊虚的直接按“权重为什么这样设计、节点为什么要删、重复多少次合适”三个问题走。2.1 快速归一化加权的数学直觉BiFPN 的加权融合最早来自 EfficientDet公式写出来并不复杂output (w1 * input1 w2 * input2 eps) / (w1 w2 eps)这里的 w1、w2 是每个输入特征图对应的标量权重在训练时通过反向传播更新。加入 eps 是为了防止分母过小导致数值不稳定实际实现里通常设 1e-4。为什么不直接做 sigmoid 再相乘因为在同一个融合节点上通常只有两到三个输入快速归一化计算更稳定梯度也比较干净。归一化系数并不是一个随意的 smooth 操作它强制权重和为 1等价于让网络在几个输入之间做软分配而不是学习一个任意的缩放系数这个细节对训练初期的稳定性帮助很大。我在替换 PANet 后做过一次控制变量对比把加权去掉改成最简单的求和融合同一份数据、同一种增强策略前 20 个 epoch 的 loss 收敛明显更慢最终 mAP 也比加权版低 1 个点左右。说明在深层网络中无差别的求和确实会引入不少噪声特征响应。2.2 节点减载删掉效率最低的转发路径BiFPN 节点删除逻辑值得单独强调。从 FPN 角度看某些中间层只有一个输入、没有融合任务它只是把上一层的结果传给下一层既没有改变分辨率也没有新信息加入是纯粹的通路。BiFPN 直接把这类节点从计算图中剥离让特征从一个尺度直接跳到另一个尺度。梯度路径因此变短小目标的浅层细节信号能更快地传到高层这正是 v11 的检测头在小目标上更有底气的根本原因。在反向传播层面理解这条路径很重要梯度在跨过更多节点时会被多次乘上小于 1 的因子容易衰减。删掉转发节点后主干深处出来的梯度可以直接作用到浅层特征浅层纹理信息的梯度信号带上高层语义的调制。这不是玄学是特征金字塔结构在反向传播层面获得的实打实改善。2.3 重复融合层的作用与限制EfficientDet 里可以叠加多个 BiFPN 融合层每个融合层都包含一轮完整的双向消息传递。我在 YOLOv11 里默认设 3 次重复因为它和输入分辨率、检测头的复杂度比较匹配。重复次数多模型对多尺度特征的“揉合”会更充分但也意味着梯度需要穿过更多融合节点训练前期如果不加长 warmup很容易出现损失先掉一阵又突然抖动的现象。建议先跑通 2 次重复的版本作为 baseline确认没有其他 bug 后再增加到 3 次。如果直接上 4 次参数量和延迟增长明显短期收益并不匹配。3. RepVGG 重参数化训练期多分支推理期单内核很多人一听到重参数化就退避三舍觉得是 PPT 型技巧。其实它的数学基础非常简单只是把若干线性运算叠加在一起再用卷积的线性性质把它们合并。下面我把三个分支的转换一五一十拆开。3.1 训练期结构为什么是 3x3 1x1 IdentityRepVGG 训练期每个 block 由三条路径构成3x3 卷积标准卷积分支1x1 卷积缩小感受野的辅助分支Identity 恒等映射残差连接三条路径都先经过 BatchNormBN。训练时多分支结构相当于提供了一个强大的梯度流残差连接减缓梯度消失1x1 分支补充非线性和通道间的线性组合最终表达能力比单分支强。关键点在于BN 层虽然是非线性的但它的操作可以写成仿射变换只要把均值、方差、gamma、beta 这些参数折叠进前面的卷积核和偏置整个多分支在数学上就等价于单个 3x3 卷积加一个 bias。这种结构在 GPU 上训练时多分支反而利用效率更高因为并行度好但在推理时密集的 3x3 大矩阵计算远胜过多个小矩阵拼接带来的访存开销。这也是为什么 RepVGG 一出来就被大量检测模型吸收正因为它把“训练时的肥模型”和“部署时的瘦模型”合二为一。3.2 从 1x1 到 3x3 的补零扩展把 1x1 卷积核扩展成 3x3 卷积核做法是在 1x1 核周围补零k_1x1: shape (cout, cin, 1, 1) k_3x3: shape (cout, cin, 3, 3) k_3x3[:, :, 1, 1] k_1x1.squeeze(dim-1).squeeze(dim-1)补零之后的 1x1 卷积等效于一个 3x3 卷积它只在中心位置有响应边缘权重全部为零。加上另外两个分支的和最终合并出一个核k_final k_3x3 pad_1x1_to_3x3 identity_kernelidentity 分支等效为 3x3 kernel 时中心位置为 1周围为 0同时还需要考虑输入输出通道数相同这个前提。如果通道数不一致identity 分支不能直接用实际实现中会通过 1x1 分支来适配维度。3.3 BN 折叠的数学细节每个分支的 BN 都可以预先折叠进前一层的卷积。如果卷积层输出为y w * x bBN 在推理阶段实际上计算(y - mean) / sqrt(var eps) * gamma beta最终合并结果等价于w_new gamma * w / sqrt(var eps) b_new gamma * (b - mean) / sqrt(var eps) beta这里的 mean、var 是在训练时统计得到推理时直接用滑动平均结果。因此重参数化过程需要取 BN 的 running_mean 和 running_var而不是当前 batch 的统计值否则合并出来的模型精度会瞬间崩掉。我在第一次重参数化时就吃过这个亏把所有分支的 BN 按 batch 统计值合并导出的 onnx 推理 mAP 直接掉到 0.2后来核对源码才意识到是统计状态没切对。3.4 RepVGG 与 BiFPN 放在一起时的梯度交互BiFPN 的加权因子和 RepVGG 的 BN 折叠在训练时有一个隐性的协同BiFPN 权重会随着训练逐渐关注更大的特征尺度而 RepVGG 的多分支结构负责给主干提供更稳的梯度。稳定主干梯度后颈部需要适配的时间就会缩短实际观察是前 30 个 epoch 里使用 RepVGG 分支的模型 loss 下降更平滑验证集波动更小。但这不代表可以无脑叠加。RepVGG 块加深后主干感受野快速变大对精确定位小目标有时反而过激。最后我选择只在主干的部分阶段启用 RepVGG 风格 block比如 C3/C4 阶段浅层 C2 阶段保持普通卷积这样小目标的细节信号不会过早被大感受野均匀化。4. 实战改造从 PANet 到 BiFPN从 C2f 到 RepCSP现在进入可复制环节。我会按照自己在 YOLOv11 上的改造步骤给出主干和颈部的设计要点、核心代码以及最容易出错的位置。4.1 整体结构配置改造后的 YOLOv11 结构可以粗略归纳为模块原版结构改造后结构说明Stage 2-3C2fRepCSP含 RepVGG 风格卷积利用多分支训练优势Stage 4C2fRepCSP 更大通道保证高语义特征充裕NeckPANetBiFPN x3 重复融合层加权双向融合HeadDecoupled HeadDecoupled Head保留检测头结构不变损失DFL CIOUDFL CIOU保持默认权重改动幅度最小化RepCSP 的意思是 CSP 框架下把内部卷积模块换成 RepVGG 风格而不是整个 block 都变成重参数化多分支。这样做既保留 CSP 的梯度截断优势又能均匀地把 RepVGG 的收益分布到各阶段。4.2 BiFPN 融合节点的实现要点我给出一个精简后的伪代码重在逻辑不是完整工程文件import torch import torch.nn as nn import torch.nn.functional as F class BiFPNNode(nn.Module): def __init__(self, channels, eps1e-4): super().__init__() # 每个融合输入对应一个标量权重 self.weight nn.Parameter(torch.ones(3, dtypetorch.float)) self.eps eps self.conv nn.Conv2d(channels, channels, 3, padding1, biasFalse) self.bn nn.BatchNorm2d(channels) self.act nn.SiLU() def forward(self, x_small, x_same, x_large): w F.relu(self.weight) self.eps # 保证稳定性 w_sum w.sum() fused (w[0] * x_small w[1] * x_same w[2] * x_large) / w_sum return self.act(self.bn(self.conv(fused)))这个节点同时接收三个不同尺度的输入经过归一化加权后再过一个 3x3 卷积做局部语义融合。注意权重先用 relu 防止出现负数虽然标准实现里权重不强制非负但我实测加上relu eps之后训练更稳尤其是新加的权重参数在早期大量更新时不会把融合结果带入负值区。4.3 重复融合层的组织顺序我把 BiFPN 融合层串联起来顺序是自底向上融合一轮然后自顶向下融合一轮重复三轮。这里的核心不是简单叠加而是每一轮都保留跨尺度跳跃连接。没有跳跃连接的重复融合层退化成多轮 PANet收益会明显缩水。自底向上的路径主要强调细节定位能力自顶向下则注入高层语义。重复三轮后每个尺度的特征图上都已经带上了全局语义和局部细节检测头拿到的不再是单一尺度的“原味”特征而是一份经过多次协商的共识特征。4.4 重参数化导出时的注意事项在导出 onnx 之前必须把训练态的 RepVGG 分支全部切换到部署态也就是先完成权重合并再把 BN 折叠掉。顺序不能反如果先导出再在推理框架里做合并很容易因为这个框架不支持动态分支而报错。我在工程化阶段写了一个switch_to_deploy()方法专门负责遍历模型中的 RepVGG 模块调用内核合并函数然后把结构标记切换为 deploy。之后再做一次 dummy forward确保每一层的输出 shape 和原结构一致这样踩坑的概率会大幅下降。5. 训练与复现我踩过的几个关键坑架构换完之后训练阶段才是真正考验人的地方。这里挑选三个最具代表性的问题每个都是从 loss 曲线和验证结果倒推出来的。5.1 权重初始化失衡 BiFPN 权重一上来就往一处挤第一次训练时我把新加的 BiFPN 权重初始化为 1.0结果前两个 epoch 权重被整体推大某个尺度直接压制其他尺度mAP 掉得很诡异。后来我把权重改为随机均匀初始化标准差控制在 0.1 以内问题基本消失。可以做一个简单类比三个输入特征原本都是“同等发言人”如果初始化时有一方牌面过大早期 loss 就会被它主导后面很难被纠正。用小的标准差初始化相当于给三方一个平等表达的机会让训练自己去调整谁更重要。5.2 BN 统计值切换时机没有处理好RepVGG 的合并不难难的是模型里还混着普通 BN 层。我把整个模型的 BN 都设置成 eval 状态后再提取 running 统计值但忘记把训练态的 BN 恢复回去结果继续训练时 loss 剧烈抖动。经验是一次性完成所有 BN 折叠导出和续训都基于折叠后的干净模型不要在折叠和训练之间反复横跳。如果你确实需要在中间阶段重新训练务必保存两份权重一份原版一份折叠版避免搞混。5.3 学习率与训练的实测参数我的复现配置是 16 张卡批大小 128初始学习率 0.01warmup 前 3 个 epoch 线性升到 0.01。BiFPN 新增权重我给了偏小的学习率倍数约为主干权重的 0.1 倍避免太重的新参数把整个网络带偏。总训练 300 epoch最后 30 个 epoch 加 Mosaic 和 MixUp 增强。模型paramsGFLOPsmAP0.5mAP0.5:0.95TensorRT fp16 延迟YOLOv11 原版6.5M12.859.141.23.1 msYOLOv11 BiFPN7.2M14.660.342.43.6 msYOLOv11 BiFPN RepVGG7.6M13.460.742.93.4 msgrep 一下数据就能发现纯加 BiFPN 后精度上涨了约 1.2 个 mAP但延迟也涨了 0.5ms。而加入 RepVGG 重参数化后延迟降回接近原版精度还额外涨了 0.5 个点说明“涨点”的账确实可以用“省计算”来还。5.4 小目标专项验证我用一组包含大量小目标的子集单独做验证BiFPN 相比 PANet 的小目标 mAP 提升最明显尤其是 32x32 像素以下的目标大约提升了 1.8 个百分比。原因就在跨尺度跳跃连接和加权机制上小目标的浅层纹理信号能更直接地注入高层语义不再需要经过多轮衰减。但大目标上收益不大有时甚至略有下降。大目标本身信息充足不需要额外融合也能被检测头准确识别所以架构创新并不是同时普惠所有目标类型。如果你的业务场景全是半张图大小的目标这个改造的性价比确实要打个问号。6. 边界条件与选型建议不是所有场景都该无脑上这套最后说一说我自己的判断这套结合方案适合什么不适合什么。6.1 适合的目标场景需要同时兼顾不同尺度目标尤其小目标较多的通用检测任务。部署环境对延迟和显存有硬约束希望通过重参数化在低精度框架里保住精度。训练资源相对充裕能支撑 300 epoch 的长训练和两轮以上的消融实验。6.2 不建议的情况与替代思路如果模型只有 3M 以下参数、必须跑在端侧 CPU 上那么 RepVGG 的收益会被极少的参数量稀释BiFPN 的额外权重也会让端侧编译压力变大。这时候更合适的方案是保留原版 PANet仅把部分 3x3 卷积替换成可重参数化的轻量结构再配合量化感知训练往往能获得更直接的收益。不要被“架构革新”四个字绑架改装甲骨的前提是甲胄本身不失灵。如果数据集本身非常干净目标尺度分布集中在单一区间那么 BiFPN 的双向加权融合价值不大反而会因为融合节点增加而让优化难度上升。这时我更倾向直接微调检测头或加大输入分辨率性价比显然更高。6.3 个人体会架构改造的收益永远来自匹配而不是模块名本身。BiFPN 强在跨尺度融合的针对性和可学习权重RepVGG 强在把训练期的富余能力折叠进推理期它们拯救的是“又想要小目标 mAP、又想要低延迟”的两难处境。YOLOv11 作为载体把这些结构嵌进去之后整个模型的收益和成本都变得更容易度量。如果你也想动手建议从一份保存完好的 baseline 开始先加 BiFPN 看到涨点再加 RepVGG 看到延迟回落再决定要不要全量叠加。这种“先涨点、再省算力”的顺序是踩坑最少、结论最清晰的路径。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →