
044、CA 的 Reduction Ratio 超参实验4/8/16/32 下参数量与精度曲线一、一个让我熬夜到凌晨三点的 bug去年秋天我在给一个工业缺陷检测项目调优。模型是 YOLOv8s 改 CACoordinate Attention跑了两轮消融实验mAP 始终比 baseline 低 0.8 个点。我盯着 tensorboard 上的 loss 曲线训练 loss 降得比 baseline 还快但验证集就是拉胯。排查了两天最后发现是 CA 模块里的 reduction ratio 设成了 32。对于小模型来说这个压缩比直接把空间信息给压没了——通道数从 128 降到 4注意力图几乎变成均匀分布。改回 8 之后mAP 直接反超 baseline 1.2 个点。这个教训让我意识到CA 的 reduction ratio 不是随便抄个论文里的值就能用的。它跟模型大小、任务复杂度、甚至输入分辨率都有关系。今天这篇笔记我就把 ratio 从 4 到 32 的完整实验过程、代码实现、以及踩过的坑全部摊开来讲。二、CA 模块的 Reduction Ratio 到底在干什么先快速回顾一下 CA 的结构。CA 把空间注意力分解成两个方向高度方向和宽度方向。每个方向先做全局平均池化得到一维特征向量然后过一个 1x1 卷积降维再过一个 1x1 卷积升维最后用 sigmoid 生成注意力权重。这里的降维卷积就是 reduction ratio 发挥作用的地方。假设输入通道数是 C降维后的通道数就是 C / ratio。ratio 越大降维越狠参数量越少但信息损失也越大。论文里默认 ratio16但那是针对 ResNet 这种大骨干网络。YOLO 的 backbone 通道数本来就少比如 YOLOv11n 的 C3 模块输出才 128 通道ratio16 意味着降维到 8 个通道——这 8 个通道要同时编码高度和宽度两个方向的信息说实话有点强人所难。三、代码实现可配置 ratio 的 CA 模块下面是我在 YOLOv11 里实际使用的 CA 模块实现。注意看注释有些地方是踩过坑才改的。importtorchimporttorch.nnasnnclassCoordAtt(nn.Module):def__init__(self,inp,oup,reduction32): inp: 输入通道数 oup: 输出通道数通常等于inp reduction: 压缩比论文默认16但YOLO里建议调小 super(CoordAtt,self).__init__()# 这里踩过坑reduction不能设太大否则中间通道数小于1# 比如inp32, reduction32 - 中间通道1还能接受# 但inp16, reduction32 - 中间通道0.5会报错self.reductionmax(reduction,1)mid_channelsmax(inp//self.reduction,1)# 两个方向的池化self.pool_hnn.AdaptiveAvgPool2d((None,1))self.pool_wnn.AdaptiveAvgPool2d((1,None))# 共享的降维卷积self.conv1nn.Conv2d(inp,mid_channels,kernel_size1,stride1,padding0)self.bn1nn.BatchNorm2d(mid_channels)self.actnn.ReLU(inplaceTrue)# 两个方向的升维卷积self.conv_hnn.Conv2d(mid_channels,oup,kernel_size1,stride1,padding0)self.conv_wnn.Conv2d(mid_channels,oup,kernel_size1,stride1,padding0)defforward(self,x):identityx n,c,h,wx.size()# 别这样写x_h self.pool_h(x).squeeze(-1) # 直接squeeze会丢掉batch维度x_hself.pool_h(x)# [n, c, h, 1]x_wself.pool_w(x).permute(0,1,3,2)# [n, c, w, 1]# 拼接后降维ytorch.cat([x_h,x_w],dim2)# [n, c, hw, 1]yself.conv1(y)yself.bn1(y)yself.act(y)# 拆开两个方向x_h,x_wtorch.split(y,[h,w],dim2)x_wx_w.permute(0,1,3,2)# [n, c, 1, w]# 升维并生成注意力a_hself.conv_h(x_h).sigmoid()a_wself.conv_w(x_w).sigmoid()outidentity*a_h*a_wreturnout关键修改点加了max(reduction, 1)防止除零中间通道数至少为 1避免inp // reduction 0的情况池化后不要直接 squeeze保持维度一致性四、在 YOLOv11 中插入 CA 模块YOLOv11 的 backbone 结构跟 v8 类似但 C3 模块换成了 C2f。我通常把 CA 插在 C2f 后面或者替换 SPPF 后面的卷积。修改ultralytics/nn/modules.py在C2f类后面加一个包装类classC2f_CA(C2f):def__init__(self,c1,c2,n1,shortcutFalse,g1,e0.5,reduction16):super().__init__(c1,c2,n,shortcut,g,e)# 在C2f输出后加CAself.caCoordAtt(c2,c2,reductionreduction)defforward(self,x):xsuper().forward(x)returnself.ca(x)然后在ultralytics/nn/tasks.py的parse_model函数里注册这个模块。找到ch字典添加# 在 ch 字典里添加ch[C2f_CA]ch[C2f]最后在 yaml 配置文件里替换。比如yolov11s.yaml中把第 4 层和第 6 层的C2f改成C2f_CA并指定 reduction 参数# backbonebackbone:-[-1,1,Conv,[64,3,2]]# 0-P1/2-[-1,1,Conv,[128,3,2]]# 1-P2/4-[-1,3,C2f_CA,[128,True,8]]# 2reduction8-[-1,1,Conv,[256,3,2]]# 3-P3/8-[-1,6,C2f_CA,[256,True,8]]# 4reduction8-[-1,1,Conv,[512,3,2]]# 5-P4/16-[-1,6,C2f_CA,[512,True,8]]# 6reduction8-[-1,1,Conv,[1024,3,2]]# 7-P5/32-[-1,3,C2f,[1024,True]]-[-1,1,SPPF,[1024,5]]五、消融实验ratio 从 4 到 32 的完整数据实验配置数据集COCO 2017val 5000张模型YOLOv11s参数量约 9.2M训练300 epochsbatch size 64输入 640x640硬件单卡 A100 80G对比baseline无CA vs CA ratio4/8/16/325.1 参数量与计算量配置参数量 (M)GFLOPs相比 baseline 增加Baseline9.2121.5-CA ratio49.6822.10.47M / 0.6CA ratio89.4521.80.24M / 0.3CA ratio169.3321.60.12M / 0.1CA ratio329.2721.50.06M / 0.0注意看 ratio32 时参数量只增加了 0.06M几乎可以忽略不计。但精度呢5.2 mAP0.5:0.95 曲线配置mAP0.5:0.95相比 baseline 提升Baseline44.7%-CA ratio445.8%1.1%CA ratio846.2%1.5%CA ratio1645.5%0.8%CA ratio3244.9%0.2%5.3 不同尺度目标的 AP配置AP_smallAP_mediumAP_largeBaseline28.1%48.3%59.2%CA ratio429.0%49.5%60.1%CA ratio829.4%49.8%60.5%CA ratio1628.8%49.1%59.8%CA ratio3228.3%48.6%59.4%5.4 训练收敛速度观察前 50 个 epoch 的 mAP 曲线ratio8 在第 30 epoch 就超过了 baseline 第 50 epoch 的精度ratio4 收敛稍慢但最终精度接近 ratio8ratio32 收敛速度跟 baseline 几乎一样说明注意力没起作用六、实验结果分析ratio8 是最优选择原因如下信息保留与压缩的平衡YOLOv11s 的 backbone 通道数在 128-512 之间ratio8 意味着中间通道数为 16-64足够编码空间位置信息。小目标检测提升明显ratio8 对小目标的 AP 提升了 1.3%因为小目标需要更精细的空间注意力压缩太狠会丢失位置细节。参数量增加可控只增加了 0.24M 参数推理速度几乎不变。ratio4 为什么不如 ratio8按理说 ratio 越小信息保留越多但实验显示 ratio4 反而比 ratio8 低了 0.4%。我分析有两个原因中间通道数太大比如 512 通道的层中间通道 128导致降维卷积的参数量激增模型容易过拟合两个方向的注意力图可能产生冗余过多的通道反而引入了噪声ratio32 基本没用中间通道只有 4-16 个注意力图几乎变成均匀分布相当于给特征图乘了个常数。七、不同模型大小的 ratio 建议我还在 YOLOv11n3.2M和 YOLOv11m20.1M上做了验证模型最优 ratio提升幅度YOLOv11n41.8%YOLOv11s81.5%YOLOv11m161.0%规律很明显模型越小ratio 应该越小。YOLOv11n 的通道数只有 64-256ratio4 时中间通道 16-64刚好够用。YOLOv11m 通道数 256-1024ratio16 就能保留足够信息。八、实际部署时的经验不要无脑用 ratio16论文里的默认值是基于 ResNet-50 的YOLO 的通道数少直接套用效果不好。考虑硬件限制如果部署在边缘设备上参数量敏感可以尝试 ratio16 或 32虽然精度提升小但几乎不增加计算量。多尺度训练时注意如果用了多尺度训练比如 320-640建议用 ratio8因为小分辨率下特征图更小需要更精细的注意力。跟其他注意力模块组合我试过 CA SE 的组合效果反而下降。CA 本身已经很强了没必要叠床架屋。训练技巧加了 CA 之后建议把学习率调低 10-20%因为注意力模块会加速收敛学习率太高容易震荡。九、一个容易忽略的细节CA 模块里的 BN 层在训练和推理时的行为不同。如果你在训练时用了 CA但导出 ONNX 时发现精度下降检查一下 BN 的track_running_stats是否设置正确。我踩过这个坑在自定义的 CA 模块里忘了设self.training True导致 BN 层在训练时也用了推理模式注意力图完全失效。排查了两天才发现。解决办法在CoordAtt的__init__里显式设置self.bn1.track_running_stats True或者在forward里根据self.training手动控制。十、总结不是教科书式的CA 的 reduction ratio 这个超参说大不大说小不小但调好了能白捡 1-2 个点的 mAP。我的建议是先试 ratio8如果模型小于 5M 就试 ratio4大于 20M 就试 ratio16。别在 ratio32 上浪费时间除非你实在缺那 0.06M 的参数量。另外如果你在 YOLOv11 上复现我的实验记得把 backbone 里所有 C2f 都替换成 C2f_CA别只换一两个。我试过只换深层效果不如全换。最后说一句注意力模块不是越多越好也不是越复杂越好。CA 这种轻量级注意力调好 ratio 比换什么 CBAM、SE 都管用。至少在我经手的十几个项目里CA 是性价比最高的注意力模块没有之一。