一、池化层概述
在卷积神经网络中,池化层是核心组件之一,主要作用是逐步降低特征图的空间尺寸即宽和高,从而减少计算量、控制过拟合并增强模型的鲁棒性。
核心作用
- 降维与减少计算量
压缩特征图的尺寸,显著减少后续层的参数数量和计算负担。 - 引入平移不变性
对微小位置变化不敏感。如输入图像中目标物体稍微移动后,池化层仍能提取相同特征。 - 抑制噪声与保留主要特征
通过取局部区域的最大值或平均值,保留显著特征并过滤细节噪声。 - 扩大感受野
使后续层能融合更广阔区域的上下文信息。
1. 最大池化
原理:取滑动窗口内的最大值。
例子:对 4×4 特征图进行 2×2 窗口 + 步长2 的最大池化:
可以使用torch.nn.MaxPool2d构造最大池化层:
maxpool_layer = torch.nn.MaxPool2d(kernel_size, # 池化窗口的大小stride=None, # 池化操作的步长,默认等于窗口大小padding=0, # 零像素的边缘填充数量dilation=1, # 扩张元素的数量return_indices=False, # 返回池化取值的索引,并通过nn.MaxUnpool2d()进行反池化ceil_mode=False # 在输出尺寸中是否使用向上取整代替向下取整)
输入特征图(4×4):
[[1, 2, 5, 3],[4, 9, 6, 8],[7, 1, 4, 2],[3, 5, 2, 6]]池化操作:
| 1 2 | 5 3 | → 取 max(1,2,4,9)=9 | 取 max(5,3,6,8)=8
| 4 9 | 6 8 |
-------------------
| 7 1 | 4 2 | → 取 max(7,1,3,5)=7 | 取 max(4,2,2,6)=6
| 3 5 | 2 6 |输出特征图(2×2):
[[9, 8],[7, 6]]
效果:保留边缘、纹理等显著特征。
2. 平均池化
原理:取滑动窗口内的平均值。
例子:相同输入,进行 2×2 平均池化:
可以使用torch.nn.AvgPool2d构造平均池化层:
avgpool_layer = torch.nn.AvgPool2d(kernel_size, # 池化窗口的大小stride=None, # 池化操作的步长,默认等于窗口大小padding=0, # 零像素的边缘填充数量ceil_mode=False # 在输出尺寸中是否使用向上取整代替向下取整count_include_pad=True, # 计算均值时是否考虑填充像素divisor_override=None # 若指定将用作平均操作中的除数)
| 1 2 | 5 3 | → 平均 = (1+2+4+9)/4 = 4 | (5+3+6+8)/4 = 5.5
| 4 9 | 6 8 |
-------------------
| 7 1 | 4 2 | → 平均 = (7+1+3+5)/4 = 4 | (4+2+2+6)/4 = 3.5输出特征图(2×2):
[[4.0, 5.5],[4.0, 3.5]]
适用场景:全局信息平滑如图像分类的背景区域。
应用:
- 目标检测:最大池化保留物体的关键特征如猫的耳朵,即使位置轻微变化仍能被识别。
- 减少过拟合:通过降低特征图尺寸,强制网络学习更泛化的模式。
- 加速训练:减少全连接层的参数如将 200×200 特征图池化为 100×100,后续计算量减少75%。
改进:
- 趋势:部分网络(如ResNet)用步长>1的卷积层替代池化层,在降维的同时学习特征。
- 全局平均池化(Global Average Pooling):将整个特征图池化为一个值(替代全连接层),极大减少参数(如用于Inception、SqueezeNet)。
二、卷积层参数如何计算
2.1 参数数量计算
#例子
class CNN(nn.Module):def __init__(self, activation="relu"):super(CNN, self).__init__()self.activation = F.relu if activation == "relu" else F.selu#输入通道数,图片是灰度图,所以是1,图片是彩色图,就是3,输出通道数,就是卷积核的个数(32,1,28,28)self.conv1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3, padding=1)#输入x(32,32,28,28) 输出x(32,32,28,28)self.conv2 = nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=1)self.pool = nn.MaxPool2d(2, 2) #池化核大小为2(2*2),步长为2self.conv3 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, padding=1)self.conv4 = nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, padding=1)self.conv5 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, padding=1)self.conv6 = nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, padding=1)self.pool2 = nn.MaxPool2d(2, 2) #输入(128,7,7) 输出(128,3,3)self.flatten = nn.Flatten()# input shape is (28, 28, 1) so the fc1 layer in_features is 128 * 3 * 3self.fc1 = nn.Linear(128 * 3 * 3, 128)self.fc2 = nn.Linear(128, 10) #输出尺寸(32,10)self.init_weights()
1. **卷积层 **
参数数量 = (in_channels × out_channels × kernel_height × kernel_width) + out_channels
- 权重参数:
in_channels × out_channels × kernel_size²
- 偏置参数:
out_channels
(每个输出通道一个偏置)
2. **全连接层 **
参数数量 = (in_features × out_features) + out_features
- 权重参数:
in_features × out_features
- 偏置参数:
out_features
3. 池化层/ Flatten 层
参数数量 = 0
(无训练参数)
以下是CNN模型中每一层的参数数量计算和输出尺寸变化的详细说明(输入为 (batch_size, 1, 28, 28)
):
- Conv1 层
- 输入尺寸:
(batch_size, 1, 28, 28)
- 输出尺寸:
(batch_size, 32, 28, 28)
- Padding=1 保持空间尺寸不变(
(28 + 2 - 3)/1 + 1 = 28
)。
- Padding=1 保持空间尺寸不变(
- 参数数量:
- 权重:
1 × 32 × 3 × 3 = 288
- 偏置:
32
- 总计:
288 + 32 = 320
- 权重:
- Conv2 层
- 输入尺寸:
(batch_size, 32, 28, 28)
- 输出尺寸:
(batch_size, 32, 28, 28)
- Padding=1 保持尺寸不变。
- 参数数量:
- 权重:
32 × 32 × 3 × 3 = 9,216
- 偏置:
32
- 总计:
9,216 + 32 = 9,248
- 权重:
- Pool 层(MaxPool2d, kernel=2, stride=2)
- 输入尺寸:
(batch_size, 32, 28, 28)
- 输出尺寸:
(batch_size, 32, 14, 14)
- 下采样公式:
(28 - 2)/2 + 1 = 14
。
- 下采样公式:
- 参数数量:
0
(池化层无参数)
- Conv3 层
- 输入尺寸:
(batch_size, 32, 14, 14)
- 输出尺寸:
(batch_size, 64, 14, 14)
- Padding=1 保持尺寸不变。
- 参数数量:
- 权重:
32 × 64 × 3 × 3 = 18,432
- 偏置:
64
- 总计:
18,432 + 64 = 18,496
- 权重:
- Conv4 层
- 输入尺寸:
(batch_size, 64, 14, 14)
- 输出尺寸:
(batch_size, 64, 14, 14)
- 参数数量:
- 权重:
64 × 64 × 3 × 3 = 36,864
- 偏置:
64
- 总计:
36,864 + 64 = 36,928
- 权重:
- Pool 层(再次使用,kernel=2, stride=2)
- 输入尺寸:
(batch_size, 64, 14, 14)
- 输出尺寸:
(batch_size, 64, 7, 7)
- 下采样:
(14 - 2)/2 + 1 = 7
。
- 下采样:
- 参数数量:
0
- Conv5 层
- 输入尺寸:
(batch_size, 64, 7, 7)
- 输出尺寸:
(batch_size, 128, 7, 7)
- Padding=1 保持尺寸不变。
- 参数数量:
- 权重:
64 × 128 × 3 × 3 = 73,728
- 偏置:
128
- 总计:
73,728 + 128 = 73,856
- 权重:
- Conv6 层
- 输入尺寸:
(batch_size, 128, 7, 7)
- 输出尺寸:
(batch_size, 128, 7, 7)
- 参数数量:
- 权重:
128 × 128 × 3 × 3 = 147,456
- 偏置:
128
- 总计:
147,456 + 128 = 147,584
- 权重:
- Pool2 层(MaxPool2d, kernel=2, stride=2)
- 输入尺寸:
(batch_size, 128, 7, 7)
- 输出尺寸:
(batch_size, 128, 3, 3)
- 下采样:
(7 - 2)/2 + 1 = 3
(向下取整)。
- 下采样:
- 参数数量:
0
- Flatten 层
- 输入尺寸:
(batch_size, 128, 3, 3)
- 输出尺寸:
(batch_size, 128 × 3 × 3) = (batch_size, 1,152)
- 参数数量:
0
- FC1 层(全连接层)
- 输入尺寸:
(batch_size, 1,152)
- 输出尺寸:
(batch_size, 128)
- 参数数量:
- 权重:
1,152 × 128 = 147,456
- 偏置:
128
- 总计:
147,456 + 128 = 147,584
- 权重:
- FC2 层(全连接层)
- 输入尺寸:
(batch_size, 128)
- 输出尺寸:
(batch_size, 10)
- 参数数量:
- 权重:
128 × 10 = 1,280
- 偏置:
10
- 总计:
1,280 + 10 = 1,290
- 权重:
完整计算流程(输入 (1, 28, 28))
层 | 输出尺寸 | 参数计算 | 参数数量 |
---|---|---|---|
Conv1 | (32, 28, 28) | (1×32×3×3) + 32 | 320 |
Conv2 | (32, 28, 28) | (32×32×3×3) + 32 | 9,248 |
Pool1 | (32, 14, 14) | 无参数 | 0 |
Conv3 | (64, 14, 14) | (32×64×3×3) + 64 | 18,496 |
Conv4 | (64, 14, 14) | (64×64×3×3) + 64 | 36,928 |
Pool2 | (64, 7, 7) | 无参数 | 0 |
Conv5 | (128, 7, 7) | (64×128×3×3) + 128 | 73,856 |
Conv6 | (128, 7, 7) | (128×128×3×3) + 128 | 147,584 |
Pool3 | (128, 3, 3) | 无参数 | 0 |
Flatten | (1152,) | 无参数 | 0 |
FC1 | (128,) | (1152×128) + 128 | 147,584 |
FC2 | (10,) | (128×10) + 10 | 1,290 |
总计 | - | - | 435,306 |
2.2 每层输出尺寸变换原理
卷积输出尺寸公式:
假设输入尺寸为 Hin×Win
卷积核尺寸 K,步长 S,填充 P,输出尺寸 Hout×Wout 为:
Hout=⌊Hin+2P−KS⌋+1
Hout= \left\lfloor \frac{H_{in} + 2P - K}{S} \right\rfloor + 1
Hout=⌊SHin+2P−K⌋+1
Wout=⌊Win+2P−KS⌋+1 Wout=\left\lfloor \frac{W_{in} + 2P - K}{S} \right\rfloor + 1 Wout=⌊SWin+2P−K⌋+1
默认设置(最常见):
- kernel_size = 3
- stride = 1
- padding = 1
则:
Hout=Hin,Wout=WinH
即 尺寸不变
当卷积核尺寸 K=2,步长 S=2.填充 P=1时
这个操作会让图像尺寸缩小一半:
简化为:
Hout=⌊HinS⌋
Hout= \left\lfloor \frac{H_{in} }{S} \right\rfloor
Hout=⌊SHin⌋
例如:
- 输入
28×28
→14×14
- 输入
14×14
→7×7
- 输入
7×7
→3×3
Flatten 展平层
将形如 (batch_size, C, H, W)
的张量展平成 (batch_size, C × H × W)
,准备送入全连接层。
总结
层 | 输入尺寸 | 操作 | 输出尺寸 | 说明 |
---|---|---|---|---|
conv1 | 1×28×28 | Conv2d(3, p=1) | 32×28×28 | 尺寸不变,通道变为 32 |
conv2 | 32×28×28 | Conv2d(3, p=1) | 32×28×28 | 尺寸不变 |
pool1 | 32×28×28 | MaxPool2d(2,2) | 32×14×14 | 高宽缩小一半 |
conv3 | 32×14×14 | Conv2d(3, p=1) | 64×14×14 | 通道增加 |
conv4 | 64×14×14 | Conv2d(3, p=1) | 64×14×14 | 通道不变 |
pool2 | 64×14×14 | MaxPool2d(2,2) | 64×7×7 | 再次缩小 |
conv5 | 64×7×7 | Conv2d(3, p=1) | 128×7×7 | 通道增加 |
conv6 | 128×7×7 | Conv2d(3, p=1) | 128×7×7 | 通道不变 |
pool3 | 128×7×7 | MaxPool2d(2,2) | 128×3×3 | 尺寸变为最终 |
flatten | 128×3×3 | Flatten | 1152 | 送入全连接层 |
fc1 | 1152 | Linear(1152→128) | 128 | 隐藏层 |
fc2 | 128 | Linear(128→10) | 10 | 输出分类结果 |