Overfitting and regularization(过拟合和正则化)
诺贝尔奖得主,物理学家Enrico Fermi曾和他的同事讨论过一个作为一个重要的物理问题的解决方案的数学模型。 这个模型取得了很好的实验效果,但是Fermi 有质疑。他问这个模型可以设置多少个自由变量。回答是4个。Fermi回复,我记得我的一个朋友Johnny von Neumann曾经说过,使用4个参数我可以模拟一个大象,用5个参数我可以让大象摆动鼻子。
就是说如果那个模型能支持大量的参数,就能过描述相当多的现象。即使这样一个在可靠的数据下表现良好的模型,也不能证明是好的模型。它可能意味着这个模型有足够大的freedom,使它能够描述给定的几乎所有的数据集,却不能捕捉到任何表象下的本质。这时,这个模型在给定的数据下可以很好的工作,却不能适用于新的情景。真正的模型测试是去测试它在从未接触到的新场景中做预测的能力。
Fermi和von Neumann 对这四个参数的模型表示怀疑。我们的含有30个隐藏层识别MNIST数字的网络有将近24000个参数!这已经很多了。我们的100个隐藏层的网络有将近80000个参数,更有甚者,深度神经网络有时可以包含上百万甚至数十亿个参数。那么这个网络的结果可信吗?
我们简化一下这个问题,构造一个网络在新场景中效果不好的情况。我们使用30个隐藏层,它有23860个参数。但是我们不会使用所有的50 000个MNIST训练图片。相反,我们只用前1000张训练图片。使用限定的集合和让这个问题表现的更明显。我们用和之前相似的方式去训练,使用cross-entropy消耗函数,续写率eta=0.5,最小集为10.不过,我们训练400次,比之前多一些,以为我们没有那样多的训练数据。让我们使用network2去看看消耗函数的变化:
>>> import mnist_loader
>>> training_data, validation_data, test_data = \
... mnist_loader.load_data_wrapper()
>>> import network2
>>> net = network2.Network([784, 30, 10], cost=network2.CrossEntropyCost)
>>> net.large_weight_initializer()
>>> net.SGD(training_data[:1000], 400, 10, 0.5, evaluation_data=test_data,
... monitor_evaluation_accuracy=True, monitor_training_cost=True)
根据结果我们可以画出消耗函数随着网络学习的变化曲线:
看起来鼓舞人心,消耗光滑地下降,正是我们期望的。注意我们只展示了训练次数为200到339的结果。这使我们看到一个良好的后期学习结果趋势,就像我们看到的一样,证明了我们感兴趣之处。
现在我们看一下识别精度如何随着测试数据的改变变化:
我已经放大了许多。在前200次训练(未展示)中精度上升到了82%。然后学习逐步下降。最后,在第280次时,识别精度停止了增长。后面只能看到识别精度在280次的识别精度附近较小的随机波动。和签名的图片对比,消耗一直在平滑地下降。单从消耗看起来我们的模型一直在变得“更好”。但测试精度表明这些改善都是假象。就像Fermi 否定的模型一样,我们的模型在280次之后的训练已经不再概括数据了。已经不是有效的学习了。我们称这为网络在280次后过拟合或过训练。
你可能会怀疑问题在于我拿训练数据的消耗和测试数据的识别精度作对比。话句话说,问题是因为我们做了一个牛头不对马嘴的比较。如果我们对比训练数据和测试数据的消耗如何?这样我们的比较度量就相似了?或者我们应该比较训练数据和测试数据的识别精度?事实上,同样的现象表明如何比较并不重要。细节总在变化。比如,我们看测试数据的消耗:
可以看到测试数据的消耗的改善直到15次,之后开始变坏,即使训练数据的消耗在变好。这也表明我们的模型过拟合了。这是一个难题,我们应该把第15次还是第280次看作影响学习的过拟合点?从实践来看,我们真正关心的是提升测试数据的识别精度,测试数据的消耗不足以代表识别精度。所以把第280次看作我们网络的过拟合点更有意义。
从训练数据的识别精度也可以看到过拟合:
精度一路飙升到了100%!就是说我们的网络能够正确地识别出所有1000张训练图片!同时,我们的测试精度之后82.27%。所以我们的网络学习到了训练数据的特性,而不是识别数据的共性。就好像我们的网络只是记住了训练数据,并没有足够地地理解数字以去归纳测试数据。
过拟合是神经网络的一个主要问题。特别是在总是有大量权重和偏移量的现代网络。为了能够有效的训练,我们需要一种方式去检测并预防过拟合。我们希望有技术手段可以减少过拟合的影响。
上面的方式是一种有效的检测过拟合的方法,在训练网络时跟踪测试数据的识别精度。如果我们发现测试数据的识别精度不再提高,就应该停止训练。当然,严格来说,这并不是是过拟合的标志。可能是测试数据和训练数据的识别精度同时停止了增长。不过,采用这种方式仍能够有效地预防过拟合。
实际应用中,我们会对这个方法做一些改变。记住我们加载的MNIST数据有三个集合:
>>> import mnist_loader
>>> training_data, validation_data, test_data = \
... mnist_loader.load_data_wrapper()
直至现在我们只用到了训练数据和测试数据,忽略了validation_data(确认数据)。确认数据集包含了10 000张数字图片,它们与50 000张训练图片和10 000张测试图片都不同。我们使用确认数据集取代测试数据集去预防过拟合。