ResNet中使用expansion放大维度特征
ResNet中使用expansion放大维度特征
在 ResNet 的实现里,block.expansion
用来表示“该残差块输出通道数相对于它内部基准通道数(base_channels
)的放大倍数”,它主要用于两处:
注意:34-layer输入BasicBlock,152-layer属于Bottleneck,BasicBlock的kenel卷积核始终保持3*3,而Bottleneck的卷积核会进行一个降维,压缩在升维的操作,使用expansion定义提升的倍率可以放大特征。
-
Bottleneck 模块内部维度变化
-
对于标准的 BasicBlock,它只有两层 3×3 卷积,输入和输出通道数是相同的,所以我们设定
class BasicBlock(nn.Module):expansion = 1…
这意味着:
- 内部的
out_channels = base_channels * expansion = base_channels * 1
- shortcut 分支对齐时,若需要扩维或缩维,也就是对齐到
base_channels * 1
。
- 内部的
-
对于 Bottleneck 块,它有三层卷积:
1×1
降维到base_channels
3×3
保持base_channels
1×1
升维到base_channels * expansion
因此,我们设定
class Bottleneck(nn.Module):expansion = 4…
这意味着:
- 最后一层 1×1 卷积的输出通道数 =
base_channels * 4
- shortcut 分支(如果通道或步幅不匹配时)也需要对齐到这个
base_channels * 4
。
-
-
在
_make_layer
中维护in_channels
每次堆叠一个新的 block,都要更新下一次 block 的输入通道数:for s in strides:layers.append(block(self.in_channels, base_channels, stride=s))# 更新 in_channels 为上一个 block 的输出通道数self.in_channels = base_channels * block.expansion
- 对于 BasicBlock:
self.in_channels = base_channels * 1
- 对于 Bottleneck:
self.in_channels = base_channels * 4
- 对于 BasicBlock:
小结
expansion
告诉我们 “每个 block 输出的通道数是base_channels
的多少倍”。- BasicBlock 输出通道 =
base_channels × 1
。 - Bottleneck 输出通道 =
base_channels × 4
。 - 它在 shortcut 对齐和后续层
in_channels
的设置上都起到了关键作用。
为什么要进行扩展输出维度
此外,在 Bottleneck 设计里把输出通道数“放大”四倍,看似多余,实际上是一个精心权衡了 表达能力 与 计算效率 的技巧:
-
“先降维再升维”——降低整体计算量
- 直接在高维空间(比如 256 或 512 通道)上做大量的 3×3 卷积,计算量和显存占用都非常高。
- Bottleneck 模块的第一步用 1×1 卷积把维度 降到
base_channels
(通常是 1/4 的输出维度),在低维空间里做一次 3×3 卷积,然后再用 1×1 卷积 升回 高维。 - 这样一来,绝大多数耗算力的 3×3 操作都在低维空间进行,节省了计算成本,却保留了处理高维特征的能力。
-
扩展输出维度——提供更丰富的特征表达
- 在经过 3×3 卷积后,我们需要一个更高维度的表示来捕捉更多元的特征关系。
- 把输出通道数放大(
expansion=4
),就能在每个残差块输出端生成更多的特征图,丰富了信息流入后续层的内容。
-
残差结构与通道扩张的平衡
- 1×1 降维 + 3×3 挤压 + 1×1 升维 的组合,既能 保持信息流(借助残差跳跃连接),又在 计算开销(FLOPs)和 参数量 上比直接在高维做 3×3 卷积要经济得多。
- 实践证明,这种 Bottleneck 形式能在更深的网络(ResNet‑50/101/152)上稳定训练并取得优秀性能。
小结:
- 降维:让大部分 3×3 卷积在低维空间进行,节省大量计算;
- 升维:输出端通道扩张,保证足够的特征表达能力;
- 通过残差连接再把“原始高维信息”与“压缩后重建的信息”相加,两者优势兼得。