卷积神经网络
卷积神经网络中,主要是卷积层和池化层,并解释填充、步幅、输入通道和输出通道的含义。
(一)卷积的运算
卷积核的尺寸通常小于输入数组,卷积核在输入数组上滑动,在每个位置上,卷积核与该位置处的输入子数组按元素相乘并求和,得到输出数组中相应位置的元素。图1展示了一个互相关运算的例子,阴影部分分别是输入的第一个计算区域、核数组以及对应的输出。
图1 二维互相关运算
(二)、特征图与感受野
二维卷积层输出的二维数组可以看作是输入在空间维度(宽和高)上某一级的表征,也叫特征图(feature map)。影响元素
x
x
x的前向计算的所有可能输入区域(可能大于输入的实际尺寸)叫做
x
x
x的感受野(receptive field)。
以图1为例,输入中阴影部分的四个元素是输出中阴影部分元素的感受野。我们可以通过更深的卷积神经网络使特征图中单个元素的感受野变得更加广阔,从而捕捉输入上更大尺寸的特征。
(三)、填充
填充(padding)是指在输入高和宽的两侧填充元素(通常是0元素),图2里我们在原输入高和宽的两侧分别添加了值为0的元素。
图2 在输入的高和宽两侧分别填充了0元素的二维互相关计算
如果原输入的高和宽是 n h n_h nh和 n w n_w nw,卷积核的高和宽是 k h k_h kh和 k w k_w kw,在高的两侧一共填充 p h p_h ph行,在宽的两侧一共填充 p w p_w pw列,则输出形状为:
( n h + p h − k h + 1 ) × ( n w + p w − k w + 1 ) (n_h+p_h-k_h+1)\times(n_w+p_w-k_w+1) (nh+ph−kh+1)×(nw+pw−kw+1)
我们在卷积神经网络中使用奇数高宽的核,比如 3 × 3 3 \times 3 3×3, 5 × 5 5 \times 5 5×5的卷积核,对于高度(或宽度)为大小为 2 k + 1 2 k + 1 2k+1的核,令步幅为1,在高(或宽)两侧选择大小为 k k k的填充,便可保持输入与输出尺寸相同。
(四)、步幅
在互相关运算中,卷积核在输入数组上滑动,每次滑动的行数与列数即是步幅(stride)。此前我们使用的步幅都是1,图3展示了在高上步幅为3、在宽上步幅为2的二维互相关运算。
图3 高和宽上步幅分别为3和2的二维互相关运算
一般来说,当高上步幅为 s h s_h sh,宽上步幅为 s w s_w sw时,输出形状为:
⌊ ( n h + p h − k h + s h ) / s h ⌋ × ⌊ ( n w + p w − k w + s w ) / s w ⌋ \lfloor(n_h+p_h-k_h+s_h)/s_h\rfloor \times \lfloor(n_w+p_w-k_w+s_w)/s_w\rfloor ⌊(nh+ph−kh+sh)/sh⌋×⌊(nw+pw−kw+sw)/sw⌋
当 p h = p w = p p_h = p_w = p ph=pw=p时,我们称填充为 p p p;当 s h = s w = s s_h = s_w = s sh=sw=s时,我们称步幅为 s s s。
(五)多输入通道和多输出通道
之前的输入和输出都是二维数组,但真实数据的维度经常更高。例如,彩色图像在高和宽2个维度外还有RGB(红、绿、蓝)3个颜色通道。假设彩色图像的高和宽分别是
h
h
h和
w
w
w(像素),那么它可以表示为一个
3
×
h
×
w
3 \times h \times w
3×h×w的多维数组,我们将大小为3的这一维称为通道(channel)维。
图4 多输入卷积运算
多输出通道
卷积层的输出也可以包含多个通道,设卷积核输入通道数和输出通道数分别为 c i c_i ci和 c o c_o co,高和宽分别为 k h k_h kh和 k w k_w kw。如果希望得到含多个通道的输出,我们可以为每个输出通道分别创建形状为 c i × k h × k w c_i\times k_h\times k_w ci×kh×kw的核数组,将它们在输出通道维上连结,卷积核的形状即 c o × c i × k h × k w c_o\times c_i\times k_h\times k_w co×ci×kh×kw。
对于输出通道的卷积核,我们提供这样一种理解,一个 c i × k h × k w c_i \times k_h \times k_w ci×kh×kw的核数组可以提取某种局部特征,但是输入可能具有相当丰富的特征,我们需要有多个这样的 c i × k h × k w c_i \times k_h \times k_w ci×kh×kw的核数组,不同的核数组提取的是不同的特征。
即在图4中有多个立方体(3维的核),提取不同的特征
(六)卷积层的简洁实现
我们使用Pytorch中的nn.Conv2d
类来实现二维卷积层,主要关注以下几个构造函数参数:
in_channels
(python:int) – Number of channels in the input imagout_channels
(python:int) – Number of channels produced by the convolutionkernel_size
(python:int or tuple) – Size of the convolving kernel , (nn) or (nm)stride
(python:int or tuple, optional) – Stride of the convolution. Default: 1padding
(python:int or tuple, optional) – Zero-padding added to both sides of the input. Default: 0 若为int,则padding为(nn),tuple为(nm),参数为可选的,默认为0即不填充bias
(bool, optional) – If True, adds a learnable bias to the output. Default: True
forward
函数的参数为一个四维张量,形状为
(
N
,
C
i
n
,
H
i
n
,
W
i
n
)
(N, C_{in}, H_{in}, W_{in})
(N,Cin,Hin,Win),返回值也是一个四维张量,形状为
(
N
,
C
o
u
t
,
H
o
u
t
,
W
o
u
t
)
(N, C_{out}, H_{out}, W_{out})
(N,Cout,Hout,Wout),其中
N
N
N是批量大小,
C
,
H
,
W
C, H, W
C,H,W分别表示通道数、高度、宽度。
代码讲解
X = torch.rand(4, 2, 3, 5) #4个样本,每个样本2个通道,每个通道的大小为3*5(图片尺寸)
print(X.shape)
#print(X)
conv2d = nn.Conv2d(in_channels=2,out_channels=3,kernel_size=(3, 5), stride=1, padding=(1, 2))
#padding=(1,2) 意思是在输入的上方和下方各添加一行0,在左边和右边各添加两列0
Y = conv2d(X)
print('Y.shape: ', Y.shape)
print('weight.shape: ', conv2d.weight.shape)
print('bias.shape: ', conv2d.bias.shape)
#输出
torch.Size([4, 2, 3, 5])
Y.shape: torch.Size([4, 3, 3, 5])
weight.shape: torch.Size([3, 2, 3, 5])
bias.shape: torch.Size([3])
(七)池化层的简洁实现
我们使用Pytorch中的nn.MaxPool2d
实现最大池化层,关注以下构造函数参数:
kernel_size
– the size of the window to take a max overstride
– the stride of the window. Default value is kernel_sizepadding
– implicit zero padding to be added on both sides
forward
函数的参数为一个四维张量,形状为
(
N
,
C
,
H
i
n
,
W
i
n
)
(N, C, H_{in}, W_{in})
(N,C,Hin,Win),返回值也是一个四维张量,形状为
(
N
,
C
,
H
o
u
t
,
W
o
u
t
)
(N, C, H_{out}, W_{out})
(N,C,Hout,Wout),其中
N
N
N是批量大小,
C
,
H
,
W
C, H, W
C,H,W分别表示通道数、高度、宽度。
代码
X = torch.arange(32, dtype=torch.float32).view(1, 2, 4, 4) #一个样本,2个通道,4*4大小
pool2d = nn.MaxPool2d(kernel_size=3, padding=1, stride=(2, 1)) #在高这个维度上移动2,宽移动1步
Y = pool2d(X)
print(X)
print(Y)
tensor([[[[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[12., 13., 14., 15.]],
[[16., 17., 18., 19.],
[20., 21., 22., 23.],
[24., 25., 26., 27.],
[28., 29., 30., 31.]]]])
tensor([[[[ 5., 6., 7., 7.],
[13., 14., 15., 15.]],
[[21., 22., 23., 23.],
[29., 30., 31., 31.]]]])
平均池化层使用的是nn.AvgPool2d
,使用方法与nn.MaxPool2d
相同。
-
池化层有参与模型的正向计算,同样也会参与反向传播
-
池化层直接对窗口内的元素求最大值或平均值,并没有模型参数参与计算
-
池化层通常会减小特征图的高和宽
-
池化层的输入和输出具有相同的通道数