深度学习三人行(第7期)----深度学习之避免过拟合(正则化)

上一期,我们一起学习了深度学习中的学习率的悲惨命运,

深度学习三人行(第6期)----深度学习之学习率的命运

今天我们一起学习下深度学习中如何避免过拟合,我们多多交流,共同进步。本期主要内容如下:

  • 提前停止训练
  • L1和L2范数正则化
  • DroupOut
  • 最大范数正则化
  • 数据增强
  • 小结

我们知道,深度学习有成千上万个参数,甚至数百万。由于有巨大的参数,所以整个网络有着难以置信的自由度,能够拟合复杂的数据集。也就是说,这个巨大的灵活度意味着在训练集上很容易过拟合。这里,我们主要通过几个常见的方法,来看下如何避免过拟合,主要有:过早的停止训练,L1和L2范数的正则化,DroupOut, 最大范数正则化,数据增强等。


一. 提前停止训练

为了避免对训练集造成过拟合,一个很好的方法就是在过拟合之前就停止对网络的训练(之前文章有介绍过)。也就是说在测试集上的性能开始下降之前的时候停止对训练数据集的训练。

在TensorFlow中就是在训练的时候,通过对测试集上性能的评估,每隔一定的间隔进行保存一个当前最优的网络,如果该网络比上一个间隔更好,则替换掉上一个网络。这样在训练结束后,将保存一个整个网络训练过程中出现最优的一个模型。尽管在实践中提前停止训练的方法能够的很好,但是通常情况下,如果能够结合其他正则化技术的话,能够变现更佳。


二. L1和L2范数正则化

正如之前我们学习线性模型的时候一样,我们也可以在神经网络用L1和L2范数进行约束权重(一般 不对偏置项)。在TensorFlow中实现正则化还是比较简单的,只需要在损失函数中加上合适的正则项即可。比如:假如我们的网络只有一个隐藏层,权重为weights1,一个输出层,权重为weight2。那么我们就能对权重用L1正则化,如下:

1[...] # construct the neural network
2base_loss = tf.reduce_mean(xentropy, name="avg_xentropy")
3reg_losses = tf.reduce_sum(tf.abs(weights1)) + tf.reduce_sum(tf.abs(weights2))
4loss = tf.add(base_loss, scale * reg_losses, name="loss")

上面的方法虽然没问题,但是,如果我们的网络有很多层,那么上面的这种方法就不是太方便了。幸运的是,对于多层的正则化,TensorFlow有更好的方法。TensorFlow中有很多创建变量的函数在创建的时候都会接受一个正则化的参数。我们可以传输带权重的函数作为一个参数,并且返回相应的正则化是损失。如下:

1with arg_scope( 
2     [fully_connected],weights_regularizer=tf.contrib.layers.l1_regularizer(scale=0.01)):
3     hidden1 = fully_connected(X, n_hidden1, scope="hidden1")
4     hidden2 = fully_connected(hidden1, n_hidden2, scope="hidden2")
5     logits = fully_connected(hidden2, n_outputs, activation_fn=None,scope="out")

上面的代码创建一个有两个隐藏层和一个输出层的神经网络,并且对于每一层的权重,都在图中创建了节点和计算L1正则化损失。TensorFlow自动把所有的正则化损失加到一个特定的集合中。我们只需要将这些正则化损失加到整体损失中,如下:

1reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
2loss = tf.add_n([base_loss] + reg_losses, name="loss")

三. DropOut

然而,在深度神经网络中,最流行的正则化技术则是DropOut, 这项技术在2012年由G.E.Hinton提出。后来被Nitish Srivastava发展并证明是非常成功的。即使最优秀的网络,如果增加DropOut技术,准确率也能够提升1~2个百分点。这看起来提高并不是太多,但是如果一个模型已经达到95%的准确率,那么提高两个百分点意味着错误率降低了40%(从5%到3%)。

其实,这是一个蛮简单的算法:就是在training的每一步,每一个神经元(包括输入神经元,但是不包括输出神经元),都有一个概率p被丢弃。被丢弃的神经元,意味着在本次training中完全被放弃,但是可能在下次迭代中被激活。这个超参数p成为DropOut率,一般设置为50%。如下图:

咋一看,这种粗鲁的做法能够很好的工作有些不可思议。但是事实证明确实可行!由于每一个神经元随机的放弃,最终训练的结果会使每一个神经元都不会过分依赖于其他的神经元,而是使努力使自己达到最优。最终网络对输入的轻微变化不在敏感,进而得到一个鲁棒性很强的网络。

因为我们训练的时候会随机的丢弃一些神经元(概率为p),但是预测的时候就没办法随机丢弃了。如果丢弃一些神经元,这会带来结果不稳定的问题,也就是给定一个测试数据,有时候输出a有时候输出b,结果不稳定,这是实际系统不能接受的,用户可能认为模型预测不准。那么一种”补偿“的方案就是每个神经元的权重都乘以一个(1-p),或者在训练的时候除以(1-p),这样在“总体上”使得测试数据和训练数据是大致一样的。比如一个神经元的输出是x,那么在训练的时候它有(1-p)的概率参与训练,p的概率丢弃,那么它输出的期望是(1-p)x+p0=(1-p)x。因此测试的时候把这个神经元的权重乘以(1-p)可以得到同样的期望。

在TensorFlow中如何运用dropout呢?只需要简单的在输入层和隐藏层之前加上dropout函数即可。在training的 过程中,这个函数会随机将一些神经元置为0,并且自动除以(1-p)。下面代码展示了如何在TensorFlow中运用dropout正则化技术,如下:

 1from tensorflow.contrib.layers import dropout
 2[...]
 3is_training = tf.placeholder(tf.bool, shape=(), name='is_training')
 4keep_prob = 0.5
 5X_drop = dropout(X, keep_prob, is_training=is_training)
 6hidden1 = fully_connected(X_drop, n_hidden1, scope="hidden1")
 7hidden1_drop = dropout(hidden1, keep_prob, is_training=is_training)
 8hidden2 = fully_connected(hidden1_drop, n_hidden2, scope="hidden2")
 9hidden2_drop = dropout(hidden2, keep_prob, is_training=is_training)
10logits = fully_connected(hidden2_drop, n_outputs, activation_fn=None,scope="outputs")

正如之前batch Normalization一样,在训练的时候我们需要设置is_training 为true,测试的时候设为false。当我们观察到模型出现过拟合的时候,我们可以增加dropout率,也就是说减小keep_prob率。相反,如果模型欠出现拟合的时候,可以增加keep_prob率,减小dropout率。通常对于大的网络增加dropout率,小的网络减少dropout率往往会有帮助。

然而,一般情况下,加入dropout的话,会使训练收敛明显放慢。但是往往会得到更好的模型,很值!


四. 最大范数正则化

另外一个神经网络中常见的正则化技术就是最大范数正则化。对于每一个神经元的权重,都受到如下的约束:

其中||w||_2为L2范数,r为最大范数。通常情况下,我们通过计算w的L2范数来进行达到目的。如下:

最大范数正则化,往往能够降低过拟合,如果不适用batch正则化的话,也可以减轻梯度消失和梯度爆炸的问题。

TensorFlow并没有提供一个现成的最大范数正则化函数,但是实施起来也并不麻烦。如下:

1threshold = 1.0
2clipped_weights = tf.clip_by_norm(weights, clip_norm=threshold, axes=1)
3clip_weights = tf.assign(weights, clipped_weights)

上面代码创建一个clip_weights的节点,用来调整weights变量。我们可以在每一次迭代之后加上这个操作,如下:

1with tf.Session() as sess:
2    [...]
3    for epoch in range(n_epochs):
4        [...]
5        for X_batch, y_batch in zip(X_batches, y_batches):
6            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
7            clip_weights.eval()

我们可以通过scope来观察权重的变化,如下:

1hidden1 = fully_connected(X, n_hidden1, scope="hidden1")
2with tf.variable_scope("hidden1", reuse=True):
3    weights1 = tf.get_variable("weights")

也可以通过root scope来得到权重,如下:

1hidden1 = fully_connected(X, n_hidden1, scope="hidden1")
2hidden2 = fully_connected(hidden1, n_hidden2, scope="hidden2")
3[...]
4with tf.variable_scope("", default_name="", reuse=True): # root scope
5    weights1 = tf.get_variable("hidden1/weights")
6    weights2 = tf.get_variable("hidden2/weights")

如果我们不知道变量的名字,那么可以通过TensorBoard或者简单的用global_variables()函数来查看变量名,如下:

1for variable in tf.global_variables():
2    print(variable.name)

尽管上面的方法可行,但是显得有些繁琐。一个更为简洁的方法就是创建一个最大范数正则化的函数,就好比前面学的L1,L2范数正则化函数一样,如下:

1def max_norm_regularizer(threshold, axes=1, name="max_norm",
2collection="max_norm"):
3    def max_norm(weights):
4        clipped = tf.clip_by_norm(weights, clip_norm=threshold, axes=axes)
5        clip_weights = tf.assign(weights, clipped, name=name)
6        tf.add_to_collection(collection, clip_weights)
7        return None # there is no regularization loss term
8    return max_norm

上面的函数返回的是一个参数化的函数max_norm(),可以向其他正则化一样:

1max_norm_reg = max_norm_regularizer(threshold=1.0)
2hidden1 = fully_connected(X, n_hidden1, scope="hidden1",weights_regularizer=max_norm_reg)

注意到最大范数正则化不需要在全局的损失函数上增加正则项,所以max_norm()函数返回为None。但是我们仍需要在每次迭代之后运行clip_weights,这就是为什么max_norm()函数中将clip_weights增加到collection中。我们需要获取clip操作并在每次迭代之后运行,如下:

1clip_all_weights = tf.get_collection("max_norm")
2with tf.Session() as sess:
3    [...]
4    for epoch in range(n_epochs):
5        [...]
6        for X_batch, y_batch in zip(X_batches, y_batches):
7        sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
8        sess.run(clip_all_weights)

五. 数据增强

最后一个正则化技术就是数据增强,主要是认为的从已有的数据中人为的产生新的数据,进而来增加数据集,降低过拟合。比如,我们要对蘑菇图片进行分类,我们可以通过轻微的旋转,平移,缩放等方法来产生新的数据,如下图:

这就去迫使模型能对蘑菇的位置,大小,方位等适应,也可以通过改变图片的对比度来使得模型适应不同的光照。通过这些方法,可以使大大扩大数据集。

TensorFlow中提供了一些图像操作的方法,比如平移,旋转,缩放,翻转,剪切,调整光照对比度,饱和度,色度等,这就使得对数据增强比较方便。


六. 小结

这期我们主要通过几个常见的方法:过早的停止训练,L1和L2范数的正则化,DroupOut, 最大范数正则化,数据增强等。来避免模型过拟合,几种方法各有千秋,找到最适合自己模型的方法才是王道。


原文发布于微信公众号 - 智能算法(AI_Algorithm)

原文发表时间:2018-11-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏机器之心

盘点 | 对比图像分类五大方法:KNN、SVM、BPNN、CNN和迁移学习

选自Medium 机器之心编译 参与:蒋思源、黄小天、吴攀 图像分类是人工智能领域的基本研究主题之一,研究者也已经开发了大量用于图像分类的算法。近日,Shiyu...

1.2K80
来自专栏数据派THU

计算机视觉怎么给图像分类?KNN、SVM、BP神经网络、CNN、迁移学习供你选(附开源代码)

原文:Medium 作者:Shiyu Mou 来源:机器人圈 本文长度为4600字,建议阅读6分钟 本文为你介绍图像分类的5种技术,总结并归纳算法、实现方式,并...

760100
来自专栏TensorFlow从0到N

TensorFlow从0到1 - 4 - 第一个机器学习问题

上一篇 3 机器人类学习的启示借鉴人类学习的模式,描绘了数据驱动的机器学习方法论:通过大量数据来确定模型,从而让模型具有预测价值。本篇提出第一个机器学习问题,...

41470
来自专栏SIGAI学习与实践平台

理解Spatial Transformer Networks

随着深度学习的不断发展,卷积神经网络(CNN)作为计算机视觉领域的杀手锏,在几乎所有视觉相关任务中都展现出了超越传统机器学习算法甚至超越人类的能力。一系列CNN...

31020
来自专栏FSociety

通过一元线性回归模型理解梯度下降法

关于线性回归相信各位都不会陌生,当我们有一组数据(譬如房价和面积),我们输入到excel,spss等软件,我们很快就会得到一个拟合函数:

18730
来自专栏数据派THU

手把手教你估算深度神经网络的最优学习率(附代码&教程)

来源:机器之心 作者:Pavel Surmenok 学习率(learning rate)是调整深度神经网络最重要的超参数之一,本文作者Pavel Surmen...

40370
来自专栏AI科技大本营的专栏

深度学习系列:卷积神经网络结构变化——可变形卷积网络deformable convolutional

作者 | 大饼博士X 上一篇我们介绍了:深度学习方法(十二):卷积神经网络结构变化——Spatial Transformer Networks,STN创造性地...

481100
来自专栏ATYUN订阅号

机器学习黑客系列:模型比较与选择

训练机器学习并生成模型以供将来预测的科学被广泛使用。为了更好地解决我们的问题,我们引入了不会太复杂的代码,更高级的学习算法和统计方法。 模型的比较和选择在我关于...

47450
来自专栏marsggbo

论文笔记系列-Neural Architecture Search With Reinforcement Learning

神经网络在多个领域都取得了不错的成绩,但是神经网络的合理设计却是比较困难的。在本篇论文中,作者使用 递归网络去省城神经网络的模型描述,并且使用 增强学习训练RN...

41030
来自专栏机器学习之旅

应用:数据预处理-缺失值填充

2.直接根据没有缺失的数据线性回归填充,这样填充的好会共线性,填充的不好就没价值,很矛盾

10430

扫码关注云+社区

领取腾讯云代金券