034、SE 注意力模块:Squeeze-Excitation 的全局平均池化到 FC 到 Sigmoid 数学推导 034、SE 注意力模块Squeeze-Excitation 的全局平均池化到 FC 到 Sigmoid 数学推导一个让我调了三天三夜的 bug去年做 YOLOv5 轻量化部署的时候我在 Neck 部分插入了 SE 模块想着给特征图加个通道注意力结果模型收敛速度反而变慢了。当时我盯着 loss 曲线心里一万个草泥马——明明论文里说 SE 能提升几个点 mAP怎么到我这就翻车了后来 debug 到凌晨三点发现是全局平均池化后的维度处理出了问题。SE 模块的数学本质其实很简单但实现细节稍不注意就会踩坑。今天咱们就从数学推导到代码实现把 SE 的每个操作掰开揉碎。Squeeze 操作全局平均池化的数学本质SE 模块的第一步是 Squeeze用全局平均池化把每个通道的 H×W 特征图压缩成一个标量。假设输入特征图 X 的形状是 (C, H, W)全局平均池化的数学表达式是z_c (1 / (H × W)) * Σ_{i1}^{H} Σ_{j1}^{W} x_c(i, j)这里 z_c 是第 c 个通道的全局描述x_c(i, j) 是第 c 个通道在位置 (i, j) 的像素值。这个操作的本质是什么是把空间信息压缩成通道级的统计量相当于告诉模型“这个通道整体激活程度如何”。这里踩过坑很多人以为全局平均池化就是简单的求均值但在 PyTorch 里用nn.AdaptiveAvgPool2d(1)时输出形状是 (B, C, 1, 1)如果你直接 squeeze 掉最后两维batch 维度可能被误伤。正确做法是用view(B, C)或者flatten(2)。Excitation 操作从 FC 到 Sigmoid 的数学推导Excitation 部分是两个全连接层加一个 Sigmoid数学上可以写成s σ(W_2 · δ(W_1 · z b_1) b_2)其中 z 是 Squeeze 得到的 (C, 1) 向量W_1 是 (C/r, C) 的降维矩阵W_2 是 (C, C/r) 的升维矩阵r 是缩减率通常取 16δ 是 ReLU 激活函数σ 是 Sigmoid。别这样写直接把两个 FC 层堆叠起来中间不加 BatchNorm。SE 模块的设计哲学就是轻量级加 BN 反而会引入额外的参数量和计算量而且 ReLU 后的分布已经够用了。我们来推导一下 Sigmoid 的输入输出关系。假设经过第二个 FC 层后的输出是 u那么 Sigmoid 定义为σ(u) 1 / (1 e^{-u})这个函数的值域是 (0, 1)当 u 很大时 σ(u) 趋近于 1当 u 很小时趋近于 0。在 SE 模块中s 的每个元素代表对应通道的“重要性权重”范围在 0 到 1 之间。数学上的关键点为什么用 Sigmoid 而不是 Softmax因为通道之间不是互斥的多个通道可以同时被强调或抑制。Softmax 会强制所有通道权重和为 1这不符合注意力机制的本意。Scale 操作逐通道乘法的实现细节最后一步是 Scale把学习到的权重 s 乘到原始特征图上y_c s_c · x_c这里的乘法是逐通道的s_c 是标量x_c 是 H×W 的特征图。在 PyTorch 里实现时需要把 s 的形状从 (B, C) 扩展成 (B, C, 1, 1)然后直接做乘法。这里踩过坑如果你用s.unsqueeze(-1).unsqueeze(-1)扩展维度要确保 s 的维度顺序正确。我见过有人把 s 的形状搞成 (B, 1, C, 1)结果乘出来特征图全乱了。完整的数学推导链把三个步骤串起来SE 模块的完整数学表达式是y X · σ(W_2 · δ(W_1 · GAP(X) b_1) b_2)其中 GAP 是全局平均池化· 表示逐通道乘法。这个公式看起来简单但每个操作符的维度变化都需要精确控制。别这样写在 forward 函数里直接写x * self.sigmoid(self.fc2(self.relu(self.fc1(self.gap(x)))))。虽然一行代码能搞定但调试时根本看不出中间结果。建议拆成多行每步打印 shape 检查。我的经验性建议缩减率 r 的选择不是越小越好。r16 是论文里的默认值但在小模型上比如 YOLOv5sr 可以设到 8 甚至 4因为通道数本来就少降维太狠会丢失信息。我做过实验r4 在轻量级模型上比 r16 高 0.3 个 mAP。放置位置SE 模块放在 Backbone 的每个 stage 后面效果最好放在 Neck 部分反而可能干扰特征融合。YOLOv5 官方代码里只在 Backbone 加了 SENeck 没加这是有道理的。训练技巧加了 SE 模块后学习率要适当调低因为 SE 的 Sigmoid 输出对梯度敏感。我习惯把初始学习率降低 20%然后用余弦退火调度。调试方法训练时打印 SE 模块输出的权重分布如果大部分权重集中在 0.5 附近说明模块没学到有效信息可能是缩减率太大或者位置不对。部署优化SE 模块的两个 FC 层可以用 1×1 卷积替代这样在 TensorRT 部署时能利用卷积优化速度提升 15% 左右。最后说一句SE 模块虽然简单但数学推导和实现细节决定了它能不能真正发挥作用。下次遇到模型收敛慢先检查你的全局平均池化维度对不对。