在深度学习的计算机视觉领域,我们一直在追求更深、更宽的网络结构(如 ResNet, DenseNet)。然而,传统的卷积操作(Convolution)大多关注于如何在**空间维度(Spatial Domain)上融合特征,却往往忽略了通道维度(Channel Domain)**之间的相互关系。

2017年,由胡杰等人提出的 SE-Block 改变了这一现状。它通过极小的计算开销,显著提升了模型的准确率,并一举夺得了当年 ImageNet 竞赛的收官冠军。


一、 核心思想:通道注意力机制

传统的卷积核在工作时,会默认特征图(Feature Map)的每一个通道都是同等重要的。但实际情况是:

  • 在识别“狗”的任务中,代表“毛发纹理”的通道应该获得更高的权重;
  • 在识别“天空”的任务中,代表“蓝色色彩”的通道更为关键。

SE-Block 的核心逻辑就是:让模型自动学习每个特征通道的重要程度,然后根据这个重要性去“加强有用特征,抑制无用特征”。


二、 数学原理

SE-Block 的具体实现分为三个关键步骤:Squeeze(压缩)Excitation(激励)Scale(加权)

假设输入特征图为 U∈RH×W×C\mathbf{U} \in \mathbb{R}^{H \times W \times C}URH×W×C

1. Squeeze: 全局信息嵌入

由于卷积只在局部感受野内工作,无法利用全局信息。我们使用 全局平均池化 (Global Average Pooling, GAP) 将每个 H×WH \times WH×W 的通道压缩成一个实数 zcz_czc

zc=Fsq(uc)=1H×W∑i=1H∑j=1Wuc(i,j)z_c = F_{sq}(\mathbf{u}_c) = \frac{1}{H \times W} \sum_{i=1}^H \sum_{j=1}^W u_c(i, j)zc=Fsq(uc)=H×W1i=1Hj=1Wuc(i,j)

此时,特征图从 H×W×CH \times W \times CH×W×C 变成了 1×1×C1 \times 1 \times C1×1×C 的向量,这个向量包含了每个通道的全局统计信息。

2. Excitation: 自适应重新校准

为了捕获通道间的非线性依赖关系,我们使用两个全连接层(FC)和一个 Bottleneck 结构:

s=Fex(z,W)=σ(g(z,W))=σ(W2δ(W1z))\mathbf{s} = F_{ex}(\mathbf{z}, \mathbf{W}) = \sigma(g(\mathbf{z}, \mathbf{W})) = \sigma(\mathbf{W}_2 \delta(\mathbf{W}_1 \mathbf{z}))s=Fex(z,W)=σ(g(z,W))=σ(W2δ(W1z))

其中:

  • W1\mathbf{W}_1W1 的维度为 Cr×C\frac{C}{r} \times CrC×C(降维层,减少计算量);
  • δ\deltaδReLU 激活函数;
  • W2\mathbf{W}_2W2 的维度为 C×CrC \times \frac{C}{r}C×rC(升维层,恢复原通道数);
  • σ\sigmaσSigmoid 函数,将输出映射到 (0,1)(0, 1)(0,1) 之间,得到每个通道的权重系数

3. Scale: 特征重加权

最后,将学习到的权重 scs_csc 乘回原来的特征通道上:

x~c=Fscale(uc,sc)=sc⋅uc\tilde{\mathbf{x}}_c = F_{scale}(\mathbf{u}_c, s_c) = s_c \cdot \mathbf{u}_cx~c=Fscale(uc,sc)=scuc


三、 代码实现 (PyTorch)

SE-Block 的迷人之处在于其简洁。以下是使用 PyTorch 实现的代码:

import torch
import torch.nn as nn

class SEBlock(nn.Module):
    def __init__(self, channels, reduction=16):
        super(SEBlock, self).__init__()
        # 1. Squeeze: Global Average Pooling
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        
        # 2. Excitation: FC -> ReLU -> FC -> Sigmoid
        self.fc = nn.Sequential(
            nn.Linear(channels, channels // reduction, bias=False),
            nn.ReLU(inplace=True),
            nn.Linear(channels // reduction, channels, bias=False),
            nn.Sigmoid()
        )

    def forward(self, x):
        # x shape: (Batch, Channel, Height, Width)
        b, c, _, _ = x.size()
        
        # Squeeze
        y = self.avg_pool(x).view(b, c)
        
        # Excitation
        y = self.fc(y).view(b, c, 1, 1)
        
        # Scale: 将权重应用到原图
        return x * y.expand_as(x)

# 测试代码
input_tensor = torch.randn(1, 64, 32, 32) # 假设输入64通道
se = SEBlock(channels=64)
output = se(input_tensor)
print(f"输入形状: {input_tensor.shape}, 输出形状: {output.shape}")

四、 应用事例:SE-ResNet

SE-Block 并不是一个独立的网络,而是一个可以“插”在任何现有结构中的模块。最经典的案例就是将其嵌入到 ResNet 中,形成 SE-ResNet

结构对比

  • 普通 ResNet Block: Input -> Conv -> BN -> ReLU -> Conv -> BN -> (+) -> Output
  • SE-ResNet Block: Input -> Conv -> BN -> ReLU -> Conv -> BN -> (SE-Block) -> (+) -> Output

实际效果

在 ImageNet 图像分类任务上:

  1. ResNet-50 的 Top-1 错误率为 24.80%
  2. SE-ResNet-50 的 Top-1 错误率为 23.29%

代价是什么?
对于 ResNet-50,引入 SE-Block 仅增加了约 0.26% 的参数量和极微小的计算量(GFLOPs),却带来了接近 1.5% 的性能提升。这种“高性价比”使得 SE-Block 至今仍是工业界优化模型的首选方案之一。


五、 总结

SE-Block 证明了:并不是只有增加深度或宽度才能提升网络性能。通过巧妙地设计注意力机制,让网络学会“筛选”信息,同样能达到事半功倍的效果。

如果你正在处理图像识别、目标检测等任务,不妨在你的主干网络里尝试加上几个 SE-Block,或许会有意想不到的惊喜!

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐