[kaggle系列 五] 通过mnist来研究神经网络的一些细节(3)

本文深入探讨神经网络训练中的优化算法,包括SGD的问题、Momentum、Nesterov Momentum、AdaGrad、RMSProp和Adam算法。通过实验对比,展示不同算法在MNIST数据集上的表现,强调Adam的普适性。此外,文章还讨论了正则化和Dropout技术在缓解过拟合中的作用。
摘要由CSDN通过智能技术生成

题目

https://www.kaggle.com/c/digit-recognizer

前言

上一篇用了个简单的神经网络来解决mnist的问题,介绍了一下权重初始化的技巧,防止训练梯度到最后一层的时候变为nan,还使用了bn算法,取得了一些成效。这一章里,我会介绍一下训练中使用的更新梯度的优化算法,还有对神经网络进行正则化和dropout的操作。

SGD的问题

使用随机梯度下降算法,虽然能够使得梯度不断下降,让模型收敛到一个较优解,但是也存在不少问题,看下面的图:

假设中间的笑脸是最优解,sgd的更新的轨迹会像图中显示的那样,不断波动。这是因为我们每次选择了一个batch去更新,每次的更新完全是根据当前计算的loss,就会出现这种情况:当前batch让你往东南方向走,下一个batch让你往西南方向走,虽然大体的方向是对的,但是会不断波动,导致更新速度比较慢。

Momentum算法

上面也讲了,sgd算法的问题是因为每次更新都完全依赖当前的batch。momentum算法来源于物理世界中动量的概念,更新模拟了物体的惯性,更新的时候使用一部分上次更新的方向,使用当前batch的loss对更新方向进行调整,得到最终更新的方向。利用这种方式,减少梯度震荡带来的影响,对更新进行加速:

这里 ρ 是一个超参数,表示上一个梯度更新方向衰减的系数,一般用0.9左右。 α 是学习率。
更新方式用图像表示就是这样,真正的更新方向是上一个更新的方向与当前方向的合成,有点类似与力的合成:

Nesterov Momentum算法

nesterov momentum其实和momentum差不多,只是修改了更新方向的合成方式:

公式:

和momentum差不多,就不多说了

AdaGrad算法

与上面的算法不同的是,AdaGrad算法关注的不是更新方向的问题,而是更新速率的问题,下面看一小段AdaGrad的代码:

可以看到代码里在更新的时候除了一个数,这个数是梯度的平方累积的开方,而1e-7是一个常数,这个只是为了防止计算的时候除0,可以发现,更新的速率是不断递减的,当更新到最后最后的时候,更新基本就会停止。
使用AdaGrad主要的目的是让学习率在学习过程中进行一些调节,因为开始的时候,学习率大一些可以加快训练速度,但是到了训练后期,可能需要进行一些细微方向的调整,但是学习率比较大的话就无法做到。

RMSProp算法

可以看到,AdaGrad算法中,学习率是不断递减的,这样就带来了一些问题。初始学习率要设置的比较合理,学习率设置较小的话,全程训练都会很慢,尤其到了后期,训练基本进行不下去,但是学习率过大,前期又有可能导致梯度爆炸之类的问题。
为了解决这个问题,RMSProp在AdaGrad算法的基础上,对分母做了一些处理,把前面累积的和削减,再加上新的值,这样既可以保证后期更新速率不至于太慢,又能较为灵活的调整学习率。

Adam算法

Adam其实就是把Momentum算法和RMSProp算法结合到一起,各取所长,双剑合璧,可以达到比较好的效果~

实验及效果

使用tensorflow的话,这几种算法的api都是有的,直接调用就好了:

# SGD
opt = tf.train.GradientDescentOptimizer(learning_rate=self.learning_rate)
# Momentum
opt = tf.train.MomentumOptimizer(learning_rate=self.learning_rate,momentum=0.9)
# Nesterov
opt = tf.train.MomentumOptimizer(learning_rate=self.learning_rate,momentum=0.9, use_nesterov=True)
# Adagrad
opt = tf.train.AdagradOptimizer(learning_rate=self.learning_rate)
# RMSProp
opt = tf.train.RMSPropOptimizer(learning_rate=self.learning_rate)
# Adam
opt = tf.train.AdamOptimizer(learning_rate=self.learning_rate)

我用了[784,256,64,10]这个网络,使用了bn算法,学习率用的0.01,50batch,训了5个epoch看效果,下面是测试结果:

Sgd:

效果一般般吧~

Momentum

要比sgd效果好一些,更新速度要更高一些。

Nesterov

和momentum没多大差距,毕竟原理差不多

AdaGrad

AdaGrad就表现得不尽如人意了,但是个人认为这是学习率设置不合理导致的,最开始的学习率设置的低了,导致更新速度一直很慢。

RMSProp

相比之下,RMSProp就好多了,因为做了一些decay操作,使得更新不会太僵硬。

Adam

我寄予厚望的Adam这次实验表现的没有RMSProp好啊……但这个也应该是学习率的问题,后面用了这个,效果还是很好的。

小结

虽然做了几个简单的实验比较,但是我发现,这个结果并不是那么有说服力,这些算法对与学习率和各种参数的要求是不同的,很难拉到同一起跑线上去比较,所以具体用哪个还是看自己的需求和实际情况。不过Adam应该是比较普适的一个方案。

正则化与Dropout

在神经网络训练的时候,如果模型比较复杂,很有可能出现过拟合的情况:在训练集上效果很好,但是在测试集里就表现的很惨,如下图:

虽然前面我们介绍了bn算法可以有效地缓解过拟合的问题,但是不妨碍我们研究一下别的方法,而且这些方法与bn算法也不冲突,也是有机会登场的~

之前也说过,模型的规模过大,很容易导致过拟合的问题,因此我们可以对模型做一个人为的限制,使得模型的复杂度不要过大,不要去过分拟合测试数据。

通常使用的正则化方法有两种,L1正则化与L2正则化,这两个方法的公式都比较类似:
L1:

L2:

这两种方法基本差不多,都是引入了权重作为loss的一部分,这使得梯度会向着权重变小的方向偏移,这么做有什么用呢?在神经网络比较复杂的时候,拟合训练数据的方式可能并不只有一种,引入了正则化项,相当于把拟合方向往某一个方向拉扯,不让参数自由生长,无形之中就限制了模型的复杂度。
那么L1和L2两种正则化方法有什么不同?这个在整体上来看,可能差不多,但是L1正则化会另外一个特性,通过推导公式可以证明,L1正则化会把一部分参数衰减到0,也就是某个w为0,这个特性可以在特征选择中起到作用,比如卷积神经网络就可以用这个特性来提取图片某个区域的特征。

使用tensorflow代码也比较好写:

## add_to_collection可以收集参数到一个集合中,这是为了方便计算
## 因为我们每一层都有一个w
## tf.contrib.layers.l1_regularizer是tensorflow提供计算正则项的函数
## 0.002相当于公式中的lambda,会乘到w的累积上,是个超参数
tf.add_to_collection('loss', tf.contrib.layers.l1_regularizer(0.002)(w))
# other code ....
# 计算交叉熵损失函数
self.cross_entropy = -tf.reduce_sum(self.label*tf.log(self.y))
# 原来用的就是交叉熵,我们把它和之前求得的正则化项加在一起
tf.add_to_collection('loss', self.cross_entropy)
self.loss = tf.add_n(tf.get_collection(
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值