共计 1534 个字符,预计需要花费 4 分钟才能阅读完成。
背景痛点
传统卷积层在处理多通道输入时,需要为每个空间位置计算所有输入通道和输出通道的组合。这导致两个主要问题:

- 参数量爆炸 :假设输入通道为 C_in,输出通道为 C_out,卷积核大小为 K×K,参数量为 C_in×C_out×K×K。当通道数增加时,计算量呈平方级增长。
- 跨通道信息耦合 :标准卷积同时混合空间信息和通道信息,难以实现灵活的通道间交互。
核心作用
1. 通道维度调整
1×1 卷积可以灵活调整通道数,实现降维或升维:
- 降维:减少通道数从而降低计算量(如 ResNet 的 bottleneck)
- 升维:增加特征表达能力(如 Inception 的扩展分支)
2. 跨通道特征融合
由于 1×1 卷积在每个空间位置独立计算,它实际上是对不同通道的线性组合:
- 可实现通道间的信息交互
- 比全连接层更高效(参数共享)
3. 引入非线性
配合 ReLU 等激活函数,1×1 卷积能增加非线性表达能力:
self.conv = nn.Sequential(nn.Conv2d(in_channels, out_channels, 1),
nn.ReLU(inplace=True)
)
经典案例
ResNet Bottleneck
结构示意图:
Input
↓
1×1 conv (降维)
↓
3×3 conv (空间特征提取)
↓
1×1 conv (升维)
↓
Shortcut
Inception Module
并行使用不同尺度的卷积核,其中 1×1 卷积用于:
- 降维后接 3×3/5×5 卷积(减少计算量)
- 直接作为特征提取分支
代码实战
import torch.nn as nn
class Bottleneck(nn.Module):
def __init__(self, in_channels, out_channels, stride=1):
super().__init__()
self.conv1 = nn.Conv2d(in_channels, out_channels//4, kernel_size=1, stride=stride)
self.conv2 = nn.Conv2d(out_channels//4, out_channels//4, kernel_size=3, padding=1)
self.conv3 = nn.Conv2d(out_channels//4, out_channels, kernel_size=1)
# groups 参数说明:控制分组卷积,默认为 1(不分组)self.shortcut = nn.Sequential()
if stride != 1 or in_channels != out_channels:
self.shortcut = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride)
def forward(self, x):
out = nn.ReLU()(self.conv1(x))
out = nn.ReLU()(self.conv2(out))
out = self.conv3(out)
out += self.shortcut(x)
return nn.ReLU()(out)
性能对比
以输入 256 通道→输出 256 通道为例:
| 结构 | 参数量 | FLOPs |
|---|---|---|
| 直接 3×3 卷积 | 589,824 | 1,179,648 |
| 1×1→3×3→1×1 | 69,632 | 139,264 |
避坑指南
- stride 过大导致信息丢失
- 问题:下采样时直接对 1×1 卷积使用 stride=2
-
解决:应先通过池化层降采样
-
通道数骤减造成信息瓶颈
- 问题:降维比例过大(如 256→4)
-
解决:保持合理的压缩比(通常 4:1)
-
忽略非线性激活
- 问题:连续堆叠 1×1 卷积不带激活函数
- 解决:每个 1×1 卷积后添加 ReLU
延伸思考
- 如何结合 SE 模块的通道注意力机制,动态调整 1×1 卷积的权重?
- 在轻量化网络中,能否用 1×1 卷积完全替代传统卷积?
正文完
发表至: 未分类
近两天内
