共计 1686 个字符,预计需要花费 5 分钟才能阅读完成。
1. 设计动机与计算量分析
标准卷积层的计算量可由公式 $FLOPs = H \times W \times C_{in} \times C_{out} \times K^2$ 量化,其中 $H,W$ 为特征图尺寸,$K$ 为卷积核大小。当 $C_{in}$ 和 $C_{out}$ 较大时(如 256→512 通道),3×3 卷积将产生显著的计算开销。

2. 结构对比与数学推导
2.1 标准残差块
参数量计算公式:
$$Params_{std} = K^2 \times C \times C \times 2$$
2.2 Bottleneck 结构
采用 1×1-3×3-1×1 的级联设计后,参数量降为:
$$Params_{btl} = (1^2 \times C \times \frac{C}{r}) + (3^2 \times \frac{C}{r} \times \frac{C}{r}) + (1^2 \times \frac{C}{r} \times C)$$
其中 $r$ 为压缩比率(通常取 4)
3. PyTorch 实现
import torch
import torch.nn as nn
class Bottleneck(nn.Module):
def __init__(self, in_channels, out_channels, stride=1, expansion=4):
super().__init__()
mid_channels = out_channels // expansion
self.conv1 = nn.Conv2d(in_channels, mid_channels,
kernel_size=1, stride=1, bias=False)
self.bn1 = nn.BatchNorm2d(mid_channels)
self.conv2 = nn.Conv2d(mid_channels, mid_channels,
kernel_size=3, stride=stride,
padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(mid_channels)
self.conv3 = nn.Conv2d(mid_channels, out_channels,
kernel_size=1, stride=1, bias=False)
self.bn3 = nn.BatchNorm2d(out_channels)
self.shortcut = nn.Sequential()
if stride != 1 or in_channels != out_channels:
self.shortcut = nn.Sequential(
nn.Conv2d(in_channels, out_channels,
kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(out_channels)
)
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = F.relu(self.bn2(self.conv2(out)))
out = self.bn3(self.conv3(out))
out += self.shortcut(x)
return F.relu(out)
4. 性能验证
4.1 FLOPs 对比(CIFAR-10)
| 结构类型 | FLOPs | 参数量 |
|---|---|---|
| 标准残差块 | 1.2G | 3.7M |
| Bottleneck(r=4) | 0.47G | 1.2M |
4.2 显存占用测量
torch.cuda.reset_max_memory_allocated()
model(inputs)
print(f"Max memory: {torch.cuda.max_memory_allocated()/1024**2:.2f}MB")
5. 关键注意事项
- 所有 1×1 卷积后必须接 BatchNorm 层,防止梯度消失
- 下采样时 identity mapping 需同步调整 stride 和通道数
- 膨胀系数 $r$ 与计算量呈二次方反比关系
6. 延伸思考
- 当 $r$ 过大时会导致特征表达能力下降,建议通过跨层连接补偿
- MobileNet 的深度可分离卷积在通道维度处理上与 bottleneck 有相似性,但计算方式存在本质差异
正文完
