今天在训练CIFAR10数据的时候,稍微调整了下网络,对卷积层增加了padding,最终结果得到了一定的改善:

CleanShot 2022-04-14 at 20.05.02

上图左边,是增加了padding='same'的结果,右图是上一次的模型。可以看到左边的训练精度明显好于右侧。而验证精度也略好于右侧。

参考keras的文档:

padding: one of “valid” or “same” (case-insensitive). “valid” means no padding. “same” results in padding with zeros evenly to the left/right or up/down of the input. When padding=”same” and strides=1, the output has the same size as the input.

padding设置为true后,会对输入的上下左右进行填充,并且如果strides(步幅)设置为1后,输入与输出的大小应该相同。如果拿4*4的输入为例,用3*3的卷积以步幅为1进行运算时。应该在上下左右四边每边补充一组数据,形成一个6*6的输入,这样在和3*3做以步幅为1做卷积的时候,输出才会是4*4(6-3+1=4)。

回到本例,CIFAR10的原始大小是32*32的,如果没有padding='same',那么在与3*3做步幅为1的卷积时,输出的大小就应该是32-2,也就是30*30了。

关于padding参数的解释就到这,至于为什么增加padding之后,训练的结果朝着更好的方向发展。或者说,如果不提供padding,会使训练结果变差,在《深度学习入门 基于Python的理论与实现》这本书中有这样的解释:

如果每次进行卷积运算都会缩小空间,那么在某个时刻输出大小就有可能为1,导致无法再应用卷积运算。

基于这种极限化的考虑,那么在每次卷积之后,保证输出大小不变就是必要的了。

不过我在测试API使用中还遇到一个问题,如果strides大于1,那么padding='same'也是无法保证输出与输入大小相同的。这个虽然在keras文档里说了,但是我却有点困惑,觉得这有悖于same的字面意思,只能留着以后再研究了。

最后附上今天使用的核心代码:

from keras import layers
from keras import models


model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3),padding='same'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu',padding='same'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu',padding='same'))
model.add(layers.Flatten())
model.add(layers.Dropout(0.5))
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(10, activation='softmax'))

model.summary()

model.compile(optimizer='Adam',
loss='categorical_crossentropy',
metrics=['accuracy'])


history = model.fit(train_images, train_labels, epochs=50, batch_size=64,validation_data=(test_images, test_labels))