欢迎光临
我们一直在努力

深度学习入门第三关:Pooling 到底在“池化”什么?MaxPool 和 AvgPool 一次讲明白

从入门到入神|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 到底有什么区别?

从入门,到入神。

赞(0)
未经允许不得转载:171主机测评 » 深度学习入门第三关:Pooling 到底在“池化”什么?MaxPool 和 AvgPool 一次讲明白
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址