任务描述
本关任务:实现池化层的前向传播。
相关知识
为了完成本关任务,你需要掌握:
- 感受野与下采样;
- 池化层的定义。
本实训内容可参考《深度学习入门——基于 Python 的理论与实现》一书中第7章的内容。
感受野与下采样
在上一个实训中,我们对卷积层进行了学习,也了解了使用卷积层对图像进行处理的基本原理。在本实训中,我们更进一步,来思考卷积神经网络中的感受野和下采样。在卷积神经网络中,通过卷积层的堆叠,特征图中的每个像素会受到输入图像中一个特定区域的影响。这个区域的大小就叫做感受野。感受野的大小,决定了网络提取到的特征的范围大小。试想,如果一个网络模型的感受野非常小,你又如何能够期望这个网络模型可以识别出比其感受野更大的目标呢?因此,在很多视觉任务中,如图像分类、语义分割、目标检测等,感受野的大小对网络模型的性能有非常大的影响。
通过卷积层的堆叠,网络模型的感受野实际上是在不断增大的,但是这个增大的速度非常慢。为了加快感受野的扩大,可以采用将卷积的步长设为2的方式,对特征图进行下采样。通过下采样,可以将网络模型的感受野扩大2倍。然而,尽管卷积计算比全连接层快了很多,其仍然有很大的计算开销。那么有没有一种快速的方法可以实现下采样和扩大感受野呢?这个答案就是池化层。
池化层
池化层与卷积层相似,通过在输入特征图上进行滑窗,并将窗口内的特征值通过一个归结函数计算得到一个输出值。使用 max 作为归结函数的池化层叫做最大值池化,使用平均值作为归结函数的池化层叫做平均值池化,二者都非常常用。通常,池化层会设置窗口大小Kh×Kw以及步长和填充。一种常用的池化层的配置是Kh=Kw=2,步长为2,填充为0,这种池化层可以将特征图的长和宽都缩小一半。下图展示了最大值池化的计算方式。
图1 最大值池化
在池化层中,输入特征图和输出特征图一一对应,池化操作只在每个通道内进行,而没有通道间的交叉,因此池化层的计算非常快。另外,因为池化层无差别的对待池化窗口内的特征值,所以相比于卷积层计算加权和的方式,池化层能提供更好的平移和旋转不变性。
池化层的实现
在实现池化层时,与实现卷积层相似,为了充分利用矩阵计算的便利性,通常会先将输入特征图通过一个im2col
操作转化成一个大矩阵,这个矩阵的每一行对应池化时的一个窗口。这样,卷积操作就变成了逐行进行的 max 或者平均值操作。具体的分析可以参考教材第7.3和7.5节中的介绍。
在本实训中,你只需要实现最大值池化。实训已经预先定义了一个MaxPool
类,在该类的构造函数中,其接受对应的池化窗口大小pool_h
和pool_w
,步长stride
和填充pad
。
在本实训中,你需要实现前向传播函数forward()
。forward()
函数的输入x
是一个维度等于4的numpy.ndarray
,形状为(B,C,H,W),其中B是 batch size。首先,你需要对x
进行im2col
操作,实训已经提供了一个im2col
操作,其将输入特征图转化成一个(B×Ho×Wo,Kh×Kw×C)的矩阵,每一行代表一个卷积窗口。
编程要求
根据提示,在右侧编辑器 Begin 和 End 之间补充代码,实现上述全连接层的前向传播。
测试说明
平台会对你编写的代码进行测试,测试方法为:平台会随机产生输入x
,然后根据你的实现代码,创建一个MaxPool
类的实例,然后利用该实例进行前向传播计算,并与标准答案进行比较。因为浮点数的计算可能会有误差,因此只要你的答案与标准答案之间的误差不超过10−5即可。
样例输入:
x:
[[[[0.7 0.25 0.87 0.76]
[0.13 0.87 0.02 0.29]
[0.81 0.92 0.7 0.13]
[0.67 0.01 0.69 0.46]]
[[0.41 0.78 0.91 0.3 ]
[0.56 0.73 0.88 0.2 ]
[0.47 0.06 0.41 0.24]
[0.79 0.2 0.84 0.2 ]]]
[[[0.94 0.19 0.83 0.79]
[0.93 0.65 0.68 0.98]
[0.16 0.9 0.15 0.71]
[0.49 0.21 0.89 0.33]]
[[0.56 0.82 0.6 0.11]
[0.98 0.73 0.86 0.29]
[0.48 0.31 0.96 0.73]
[0.86 0.94 0.78 0.57]]]]
pool_h: 2
pool_w: 2
stride: 2
pad: 0
则对应的输出神经元为:
[[[[0.7 0.87]
[0.92 0.7]]
[[0.78 0.91]
[0.79 0.84]]]
[[[0.94 0.98]
[0.49 0.89]]
[[0.98 0.86]
[0.94 0.96]]]]
开始你的任务吧,祝你成功!
import numpy as np
from utils import im2col
class MaxPool:
def __init__(self, pool_h, pool_w, stride=1, pad=0):
r'''
池化层的初始化
Parameter:
- pool_h: int
- pool_h: int
- stride: int
- pad: int
'''
self.pool_h = pool_h
self.pool_w = pool_w
self.stride = stride
self.pad = pad
def forward(self, x):
r'''
池化层的前向传播
Parameter:
- x: numpy.array, (B, C, H, W)
Return:
- y: numpy.array, (B, C, H', W')
H' = (H - Kh + 2P) / S + 1
W' = (W - Kw + 2P) / S + 1
'''
########## Begin ##########
N, C, H, W = x.shape
out_h = int(1 + (H - self.pool_h + 2 * self.pad) / self.stride)
out_w = int(1 + (W - self.pool_w + 2 * self.pad) / self.stride)
col = im2col(x, self.pool_h, self.pool_w, self.stride, self.pad)
col = col.reshape(-1, self.pool_h * self.pool_w)
out = np.max(col, axis=1)
out = out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)
return out
########## End ##########