从入门到入神|PyTorch 基础系列 03
前两关我们已经认识了 Tensor 和 Conv2d。
比如:
x = torch.randn(8, 64, 128, 128)
表示当前特征 Tensor 的尺寸是:
[B, C, H, W] = [8, 64, 128, 128]
在 CNN 中,卷积之后经常会紧接着看到:
nn.MaxPool2d(kernel_size=2, stride=2)
经过这一层后:
[8, 64, 128, 128]
↓
[8, 64, 64, 64]
为什么 128×128 突然变成了 64×64?
Pooling 到底“池化”了什么?
这一关,我们就把它讲清楚。
一、先看完整代码
先运行一个最简单的例子:
import torch
import torch.nn as nn
# 输入 Tensor
x = torch.randn(8, 64, 128, 128)
print("Pooling 前:", x.shape)
# 最大池化
pool = nn.MaxPool2d(
kernel_size=2,
stride=2
)
# Pooling
y = pool(x)
print("Pooling 后:", y.shape)
运行结果:
Pooling 前:
torch.Size([8, 64, 128, 128])
Pooling 后:
torch.Size([8, 64, 64, 64])
可以先记住这个变化:
[8, 64, 128, 128]
↓
MaxPool2d(2, 2)
↓
[8, 64, 64, 64]
接下来解释为什么。
二、一张图看懂 Pooling、Max Pooling 和 Average Pooling
假设现在有一个 2×2 的局部区域:
1 3
2 4
Pooling 的核心思想其实非常简单:
把一个局部区域中的多个数,压缩成一个数。
如果使用 Max Pooling:
1 3
→ 4
2 4
保留最大值:
max(1, 3, 2, 4) = 4
如果使用 Average Pooling:
1 3
→ 2.5
2 4
保留平均值:
(1 + 3 + 2 + 4) / 4 = 2.5
所以两者最简单的区别就是:
Max Pooling:保留局部区域中最强的响应。
Average Pooling:保留局部区域的平均响应。
这就是 Pooling 最基本的工作。
三、为什么 Max Pooling 经常取最大值?
假设某张 Feature Map 中有这样一个区域:
0.1 0.2
0.3 0.9
其中:
0.9
代表当前位置对某种特征的响应最明显。
经过 Max Pooling:
0.1 0.2
→ 0.9
0.3 0.9
所以可以把 Max Pooling 粗略理解为:
这个区域里,只要某个位置明显检测到了这个特征,就把最强的响应保留下来。
它不关心这个强响应具体出现在 2×2 区域的左上角还是右下角,而更关注:
这里有没有明显的特征。
四、为什么 Pooling 可以让 H、W 减半?
来看最常见的参数:
nn.MaxPool2d(
kernel_size=2,
stride=2
)
这里:
kernel_size = 2
表示每次观察:
2×2
区域。
而:
stride = 2
表示窗口横向和纵向每次移动 2 格。
例如输入:
□ □ □ □
□ □ □ □
□ □ □ □
□ □ □ □
每个 2×2 区域压缩成一个数:
□ □
□ □
于是:
4×4
↓
2×2
同样:
128×128
↓
64×64
因此:
[8, 64, 128, 128]
↓
MaxPool2d(kernel_size=2, stride=2)
↓
[8, 64, 64, 64]
Pooling 本质上是在减少空间位置的数量。
五、Pooling 会改变 Channel 吗?
看尺寸变化:
[8, 64, 128, 128]
↓
[8, 64, 64, 64]
拆开来看:
B:8 → 8
C:64 → 64
H:128 → 64
W:128 → 64
可以发现:
普通的 Pooling 通常不会改变 Channel,只会压缩 H 和 W。
原因也很简单。
Pooling 是每个通道分别进行的:
Feature Map 1 → Pooling
Feature Map 2 → Pooling
Feature Map 3 → Pooling
…
Feature Map 64 → Pooling
所以:
64 张特征图进去
↓
还是 64 张特征图出来
只是每一张特征图从:
128×128
变成:
64×64
六、Pooling 有可学习参数吗?
以:
nn.MaxPool2d(2, 2)
为例。
Max Pooling 使用的是固定规则:
取最大值。
它不像 Conv2d 那样存在需要训练的卷积核参数。
我们可以直接验证:
import torch.nn as nn
pool = nn.MaxPool2d(2, 2)
params = sum(
p.numel()
for p in pool.parameters()
)
print(params)
输出:
0
也就是说:
普通 Max Pooling 本身没有可学习参数。
Average Pooling 同样是固定规则求平均,也没有需要训练的卷积核。
七、Pooling 和 stride=2 的卷积有什么区别?
这个问题很重要。
因为上一篇我们已经知道:
nn.Conv2d(
64,
128,
kernel_size=3,
stride=2,
padding=1
)
也可以把:
128×128
变成:
64×64
那它和 Pooling 有什么区别?
Pooling
例如:
nn.MaxPool2d(2, 2)
使用固定规则:
取最大值
主要负责:
下采样
stride=2 的 Conv
例如:
nn.Conv2d(
64,
128,
kernel_size=3,
stride=2,
padding=1
)
不仅能够:
128×128
↓
64×64
还可以:
64 Channel
↓
128 Channel
并且卷积核中的参数是可以学习的。
所以可以简单理解成:
Pooling
→ 固定规则下采样
stride=2 Conv
→ 学习特征 + 下采样 + 可改变通道
八、既然 stride=2 卷积也能下采样,还需要 Pooling 吗?
不一定。
很多经典 CNN 会看到:
Conv
↓
Pooling
↓
Conv
↓
Pooling
但一些网络也会直接使用:
stride=2 Conv
完成下采样。
所以以后看到一个网络没有 MaxPool,不要马上认为:
这个网络没有下采样。
还要检查 Conv2d 中有没有:
stride=2
九、Pooling 会不会丢失信息?
会。
还是这个区域:
1 3
2 4
经过 Max Pooling:
→ 4
原来的:
1、2、3
就没有被保留下来。
所以 Pooling 本质上是一种:
有损的信息压缩。
但是它也带来几个明显好处:
特征图尺寸更小
↓
计算量减少
↓
显存占用减少
↓
后续网络处理范围更大
所以 Pooling 做的是一个取舍:
牺牲部分空间细节,换取更紧凑的特征表示。
十、Max Pooling 和 Average Pooling 应该怎么选?
两者没有绝对的谁更好。
可以先建立一个简单理解:
Max Pooling
更强调:
最强响应
适合突出局部显著特征。
Average Pooling
更强调:
整体平均信息
对整个区域进行综合。
在传统 CNN 的中间层中,Max Pooling 很常见。
而在网络最后,我们还经常会看到一种特殊的 Average Pooling:
Global Average Pooling
简称:
GAP
这个我们后面会单独讲。
十一、现在的 CNN 还一定需要 Pooling 吗?
不一定。
Pooling 是一种经典的下采样方式,但不是唯一方式。
现在看到 CNN 时,可以先检查它采用的是:
Max Pooling
还是:
stride=2 Conv
或者其他下采样方法。
所以更重要的不是背:
CNN 一定要有 Pooling。
而是理解:
网络为什么需要逐渐降低 H、W,以及它采用了什么方法完成这件事。
十二、把 Conv 和 Pooling 串起来看
现在我们已经可以真正看懂一段基础 CNN:
import torch
import torch.nn as nn
x = torch.randn(8, 3, 256, 256)
conv = nn.Conv2d(
in_channels=3,
out_channels=64,
kernel_size=3,
stride=1,
padding=1
)
pool = nn.MaxPool2d(
kernel_size=2,
stride=2
)
x = conv(x)
print("Conv 后:")
print(x.shape)
x = pool(x)
print("Pooling 后:")
print(x.shape)
输出:
Conv 后:
torch.Size([8, 64, 256, 256])
Pooling 后:
torch.Size([8, 64, 128, 128])
整个数据流:
[8, 3, 256, 256]
↓ Conv2d
[8, 64, 256, 256]
↓ MaxPool2d
[8, 64, 128, 128]
现在可以明显看出两层的分工:
Conv2d
→ 提取特征
→ 可以改变 Channel
Pooling
→ 压缩空间尺寸
→ Channel 通常不变
总结
这一关最重要的内容其实只有几句话:
Pooling:
把一个局部区域中的多个值压缩成一个值。
Max Pooling:
保留局部最大值。
Average Pooling:
保留局部平均值。
常见的 MaxPool2d(2,2):
H、W 减半
C 通常不变
例如:
[8,64,128,128]
↓
[8,64,64,64]
而 Pooling 和 stride=2 Conv 最大的区别是:
Pooling 使用固定规则下采样;stride=2 Conv 可以一边学习特征,一边完成下采样。
下一关:Flatten 到底把什么“拍平”了?
下一篇我们继续顺着 Tensor 往后走:
深度学习入门第四关:torch.flatten(x, 1) 到底是什么意思?
重点解决:
-
torch.flatten(x, 1) 为什么从第 1 维开始?
-
[8,128,8,8] 为什么会变成 [8,8192]?
-
为什么全连接层前经常出现 Flatten?
-
Flatten 会不会破坏空间结构?
-
Flatten 和 GAP 到底有什么区别?
从入门,到入神。

![[特殊字符]DeepSeek‑Harness(DSH)小白保姆教程-171主机测评](https://www.171host.com/wp-content/uploads/2026/08/20260816085112-6a817a009aabf-220x150.png)
