TensorFlow 与深度学习 (10) Dropout 和过拟合

你可能已经注意到,在数千次迭代之后,测试和训练数据的交叉熵曲线开始不相连。学习算法只是在训练数据上做工作并相应地优化训练的交叉熵。它从来看不到测试数据了,所以看到这一点也不必感到奇怪:过了一会儿它不再对测试交叉熵产生影响,交叉熵不再下降,有时甚至反弹回来。

它不会立刻影响你模型的识别能力,但这将阻止你运行很多次迭代,这基本上是一个信号——告诉我们训练已经不能再为模型提供进一步改进了。这种现象通常会被标为过拟合(overfitting)。当发生过拟合时,你可以尝试采用一种正则化(regularization)技术,称为 dropout

在 dropout 里,每一次训练迭代的时候,你从网络中随机地放弃一些神经元。设置一个使神经元继续保留的概率 pkeep,通常介于 50% 到 75% 之间,然后每次迭代时,随机地把一些神经元连同它们的权重和偏置一起去掉。每次去掉的神经元是不同的(而且你也要按比例地增强剩余神经元的输出,以确保传到下一层的激活不会漂移)。测试网络性能的时候,再把所有的神经元都装回来 (pkeep=1)。

TensorFlow 提供一个 dropout 函数可以用在一层神经网络的输出上。它随机地清零一些输出并且把剩下的提升 1/pkeep。这里演示了如何把它用在一个两层神经网络上:

1
2
3
4
5
6
7
# feed in 1 when testing, 0.75 when training
pkeep = tf.placeholder(tf.float32)

Y1 = tf.nn.relu(tf.matmul(X, W1) + B1)
Y1d = tf.nn.dropout(Y1, pkeep)

Y = tf.nn.softmax(tf.matmul(Y1d, W2) + B2)

你现在可以在网络中每个中间层之后插入 dropout。这是可选的,如果你没时间深入阅读的话。

该解决方案可以在 mnist_2.2_five_layers_relu_lrdecay_dropout.py 里找到,如果你被卡住了可以看它。

你会看到测试损失已经被拉回来了,在可控范围内了,不过至少在这个例子中噪声重新出现了(如果你知道 dropout 的工作原理,这一点也不奇怪)。测试的准确度依然没变,令人有点小失望。过拟合一定还有其它原因。

在我们继续进行下一步之前,我们先扼要重述一下我们到目前为止用过的所有工具:

无论我们做什么,看上去都不可能很显著地解决 98% 的障碍,而且我们的损失曲线依然显示过拟合。什么是真正的过拟合?过拟合发生在该神经网络学得不好的时候,在这种情况下该神经网络对于训练样本表现很好,对真实场景却并不那么好。有一些像 dropout 一样的正则化技术能够迫使它学习得更好,不过过拟合还有更深层的原因。

基本的过拟合发生在一个神经网络有太多的自由度的时候。想象一下我们有如此多的神经元,以至于所组成的网络可以存储我们所有的训练图像并依靠特征匹配来识别它们。它会在真实世界的数据里迷失。一个神经网络必须有某种程度上的约束,以使它能够归纳推理它在学习中所学到的东西。

如果你只有很少的训练数据,甚至一个很小的网络都能够用心学习它。一般来说,你总是需要很多数据来训练神经网络。

最后,如果你已经做完了所有的步骤,包括实验了不同大小的网络以确保它的自由度已经约束好了、采用了 dropout、并且训练了大量的数据,你可能会发现你还是被卡在了当前的性能层次上再也上不去了。这说明你的神经网络在它当前的形态下已经无法从你提供的数据中抽取到更多的信息了,就像我们这个例子这样。

还记得我们如何使用我们的图像吗——所有的像素都展平到一个向量。这是个很糟糕的想法。手写的数字是由一个个形状组成的,当我们把像素展平后我们会丢掉这些形状信息。不过,有一种神经网络可以利用这些形状信息:卷积网络(convolutional network)。让我们来试试。