前言
在用mnist训练vgg和alexnet数据集时,出现标题所示的错误。而再用Le-Net训练的时候则不会出现错误。另外这里我使用的输入图片维度为28x28x1(mnist的数据维度为784x1x1),修改为784x1x1依然出错;采用的填充方式是,卷积层采用补零填充,池化层不采用补零填充。
解决
先说解决办法。
- 修改输出图片的大小。往大的修改。
- 修改卷积层和池化层的填充方式。往填充零方向修改。
- 其他博客介绍的方法。
原因
首先先来看卷积层和池化层过滤后的矩阵输出维度公式,分为补零和不补零两种情况。
-
补充零公式:
o u t l o n g = ⌈ i n l e n g t h s t r i d e l e n g t h ⌉ out_{long} = \lceil{\frac{in_{length}}{stride_{length}}}\rceil outlong=⌈stridelengthinlength⌉
o u t w i d t h = ⌈ i n w i d t h s t r i d e w i d t h ⌉ out_{width} = \lceil{\frac{in_{width}}{stride_{width}}}\rceil outwidth=⌈stridewidthinwidth⌉
-
不补零公式:
o u t l o n g = ⌈ i n l e n g t h − f i l t e r l e n g t h + 1 s t r i d e l e n g t h ⌉ out_{long} = \lceil{\frac{in_{length}-filter_{length}+1}{stride_{length}}}\rceil outlong=⌈stridelengthinlength−filterlength+1⌉
o u t w i d t h = ⌈ i n w i d t h − f i l t e r w i d t h + 1 s t r i d e w i d t h ⌉ out_{width} = \lceil{\frac{in_{width}-filter_{width}+1}{stride_{width}}}\rceil outwidth=⌈stridewidthinwidth−filterwidth+1⌉
注:out表示输出矩阵维度,in表示输入矩阵维度,filter表示滤波器(卷积核),stride表示步长,long表示矩阵的长,width表示矩阵的宽。
AlexNet
先来看alexnet的情况。下面是AlexNet的神经网络每一层的输出维度。
卷积层补0 , 池化层不补0
输入维度 | [输出维度、移动步长s、卷积核尺寸f] | 输出维度 | |
---|---|---|---|
第一层卷积 | 28x28x1 | [48,4,11] | 7x7x48 |
第二层池化 | 7x7x48 | [48,2,3] | 3x3x48 |
第三层卷积 | 3x3x48 | [128,1,5] | 3x3x128 |
第四层池化 | 3x3x128 | [128,2,3] | 1x1x128 |
第五层卷积 | 1x1x128 | [192,1,3] | 1x1x192 |
第六层卷积 | 1x1x192 | [192,1,3] | 1x1x192 |
第七层卷积 | 1x1x192 | [128,1,3] | 1x1x128 |
第八层池化 | 1x1x128 | [128,2,3] | 出错 |
输入维度 | [输出维度、移动步长s、卷积核尺寸f] | 输出维度 | |
---|---|---|---|
第一层卷积 | 784x1x1 | [48,4,11] | 196x1x48 |
第二层池化 | 196x1x48 | [48,2,3] | 出错 |
可以看出,不管采用哪个维度都会出错,出错的原因也很简单,根据上面两个公式进行计算,最后得出的矩阵输出维度为负数,所以肯定会报错的。
VGG
同样的道理,采用vgg进行训练也会报错,vgg网络输出维度如下。
卷积层补0 , 池化层不补0
输入维度 | [输出维度、移动步长s、卷积核尺寸f] | 输出维度 | |
---|---|---|---|
第一层卷积 | 28x28x1 | [64,1,3] | 28x28x64 |
第二层卷积 | 28x28x64 | [64,1,3] | 28x28x64 |
第三层池化 | 28x28x64 | [64,2,3] | 13x13x64 |
第四层卷积 | 13x13x64 | [128,1,3] | 13x13x128 |
第五层卷积 | 13x13x128 | [128,1,3] | 13x13x128 |
第六层池化 | 13x13x128 | [128,2,3] | 6x6x128 |
第七层卷积 | 6x6x128 | [256,1,3] | 6x6x256 |
第八层卷积 | 6x6x256 | [256,1,3] | 6x6x256 |
… | … | … | … |
第十三层卷积 | 2x2x512 | [512,1,1] | 2x2x512 |
第十四层卷积 | 2x2x512 | [512,2,3] | 出错 |
输入维度 | [输出维度、移动步长s、卷积核尺寸f] | 输出维度 | |
---|---|---|---|
第一层卷积 | 784x1x1 | [64,1,3] | 784x1x64 |
第二层卷积 | 784x1x64 | [64,1,3] | 784x1x64 |
第三层池化 | 784x1x64 | [64,2,3] | 出错 |
发现了把,每次报错都是在池化层报错,因为池化层没有采用全零填充,所有导致输出的矩阵维度可能就变成负数,从而导致报错。这一点从公式也可以看的出来,采用全零填充的公式,不管输出矩阵的维度大小,因为向上取整的缘故,输出的矩阵维度至少为1。而不采用零填充,就有可能出现负数维度。
至于Le-Net为什么没有报错,因为Le-Net的网络层数没有那么多,到最后输出的矩阵维度还是为正,当然不会报错。
总结
报错原因:采用了非零填充的方式,导致矩阵的维度不断减小,出现负维度。
解决:1)增大输入图片的大小,使得最后神经网络的输出矩阵维度依然为正。不过增大图片的大小对训练速度有直接影响,而且影响还很大,训练速度会变慢。2)改变填充方法,变为零填充。因为改用零填充,池化层的输入输出维度不变,减小参数的作用就变小,一定程度上也会影响训练速度。(也就是把 padding=‘VALID’ 改为 padding=‘SAME’)