首页 > 程序开发 > 综合编程 > 其他综合 >

王小草【深度学习】笔记第三弹--神经网络细节与训练注意点

2016-10-25

神经网络的训练的有两种方式,第一种是自己从头到尾训练一遍;第二种是使用别人训练好的模型,然后根据自己的实际需求做改动与调整。后者我们叫做fine-tuning

1. 权重的初始化

1.1 fine-tuning

神经网络的训练的有两种方式,第一种是自己从头到尾训练一遍;第二种是使用别人训练好的模型,然后根据自己的实际需求做改动与调整。后者我们叫做fine-tuning.

在model zoo有大量训练好的模型(不知道的可以百度一下model zoo)

fine-tuning相当于站在巨人的肩膀上,使用别人已经训练好了的优秀的模型去实现自己的需求。一般分为以下两种调整方式:
1.只修改FC(全连接层),比如原来的模型是1000个类别,而你实际上只需要分2个类别,那么你就可以调整FC的神经元个数与权重。并且设置前面所有没有改动的层的学习率为0,然后加大FC层的学习率。

2.包括了调整卷基层,激励层,池化层的神经元个数,或者减少或增加层级的数量。并且调低前面所有没有改动的层的学习率,然后加大后面层的学习率。这种方式的收敛更快和好。

QQ截图20160817174007.png-196.4kB

1.2 随机数很小数初始化权重

在做逻辑回归时我们一般都将权重初始化为0,但是,在做神经网络的时候,权重如果很小或等于0,就会出现问题了。

假设权重为0,那么每个节点上的z = wx + b 这个线性函数运算结果都几乎为z = b。然后无论接下来使用什么类型的激励函数,这个函数的值y = g(z)在每个节点上都是相同的。也就是说BP的前向运算的时候得到的结果的是一样的,那么在反向传播时作用在每个权重的更新都是一致的,这绝必不是我们想要的。还记得上一个笔记中提到过,每个神经元会各自去负责一类特征(比如颜色,纹理等),只有在这个特征出现的时候,我们才希望对应的那个神经元被激活了。所以,正常情况下,每个神经元的输出应该是各不相同的。

下面做了一个实验,搭了一个10层的神经网络,并且设置w的初始值是一个很小很小的数,目的是想看看将w初始化成非常小的随机数会对神经网络的运作产生什么不好的影响?

下图10个蓝色的柱状图表示的是在过了激活函数后的每层神经元的输出结果的柱状图。每个柱状图是该层的所有神经元输出结果的一个分布图。蓝色的线形图是10层神经网络每层神经元输出结果的均值。旁边红色的线形图是每层的方差。

QQ截图20160817180056.png-178.4kBvc+089S8tcjT2jAuMqOs1vnXtM280rLP1Mq+o6y12tK7suO1xMnxvq3UqrXEyuSz9tKyyse31rK8sci9z7fWyaK1xKOsse3Kvrj3uPbJ8b6t1Kq1xMrks/a499LsoaM8L3A+DQo8cD7U2b+0tdq2/rLjoaO++da10rvPwtfTyc/J/bW9wcstMC4wMDAwMDGjrLb4t72y7tbovbW1vcHLMC4wNKOstNO31rK8zby/tLjDsuO1xMnxvq3Uqsrks/ax5LXDuty8r9bQo6y8uLr1try31rK81NrSu7j2utzPwdWttcS3ts6nxNqho9Kyvs3Kx8u1o6zU2rXatv6y47XEyrG68qOssrvNrMnxvq3UqrXEyuSz9sf309rSu9bCwcujrMv7w8e1xLvuwaa9tbXNwcuhozwvcD4NCjxwPr3T18W/tM/Cw+a8uLLjo6y++da10KG3+cnPyf2jrLe9su68zND4z8K9taOsyfG+rdSqtcTK5LP2veG5+9S9wLTUvdK70fmjrLW9tdoxMLLjtcTKsbryt72y7tLRvq3OqjAgo6y+zcrHy7XL+dPQyfG+rdSqtcTK5LP2trzN6sirz+C1yMHLoaOjqL/ewbOjqTwvcD4NCjxwPsnPw+bV4rj2yrXR6c7Sw8e/ydLUtcOz9tK7uPa94cLbo7q1scio1ti1xLP1yrzWtbfHs6PQobXEyrG68qOsttTT2sezsuO1xMnxvq3N+MLnu7nD48e/xNy5u8qkyM6jrLWryse21NPaye6y47XEyfG+rc34wue+zcmlyqfBy7vuwaajrNXivs3Kx8nPuPaxyrzH1tDM4bW9tcQmbGRxdW87zN22yMPWyaImcmRxdW87z9bP86Gjs/bP1sHLJmxkcXVvO8zdtsjD1smiJnJkcXVvO6OsvKTA+Lqvyv2+zbTTtMu50rX0o6zV+7j2xKPQzbXE0bXBt9KyuLbWrrarwffBy6Gjo6jU2b/ewbOjqTwvcD4NCjxoMyBpZD0="13-随机数较大数初始化权重">1.3 随机数较大数初始化权重

既然权重的初始化不能是很小的数,那么将权重设成比较大的数是否可以呢?

再来做一个实验,此时将权重设置为1。我们来看下面的结果图,仍然是均值,方差,和每层输出的柱状分布图。

QQ截图20160818143041.png-359.3kB

看均值和方差的分布到也正常,但是,看到柱状分布图的是心里那个一惊,每层的输出分布呈现一个凹形,数据被分布在了0或者1的两边。这种极端的现象来自于偏大的权重使得z = wx + b的z值变得很大或者很小,而z值越大或越小,g(z)激励函数的输出就越接近于1或者-1,所以呈现了两个极端化的现象。(这里我们使用的激励函数是tanh)

1.4 哈维尔xavier初始化

现在我们知道权重的初始值设置得太大或者太小都不适合。

于是Xavier在2010年发表论文提出了一种解决办法。z值是由于上级指向它的节点Xi 乘以对应的权重的总和。也就是说,影响z值大小的有两个因素:输入层节点的个数,权重值。为了平衡这两个因素,我们认为当输入节点很多的时候,相应的初始权重应当小一点;当节点比较的时候,初始权重应当相应增大。

所以,我们仍然从个高斯分布中得到一个随机的权重值,然后将这个权重值除以输入的节点个数的开根号,得到的新的值为权重的初始值。我们采用这个新的方法去又做了个实验,结果如下:

QQ截图20160818143910.png-424.5kB

第一个柱状图是输入的高斯分布图。从第2个到第11个柱状图是10层神经网络的输出,可以发现,他们分布并没有之前那样集中了,并且方差线性图中可见方差下降也没有之前那么快速了。说明这个方法产生了一定的效果。

1.5 哈维尔初始化与ReLU

前面的实验我们都是使用tanh激励函数,但是之前也说过,tanh存在梯度弥散的问题,在神经网络中更常用的是ReLU。现在我们使用ReLU激励函数来做实验。下图是实验结果。

QQ截图20160818145520.png-363.4kB

柱状分布图与我们之前看的有所不同,再来回顾一样ReLU激励函数的函数图,它是一个由y=x,和 y = 0 两条射线组成的,当x < 0 时,y = 0. 所以柱状图的分布值也是 >= 0 。
QQ截图20160816100542.png-22.7kB

那么问题来了,方差图上显示,方差仍然随着神经网络层次的深入变得越来越小,在10层处已经接近于0了,这表示,该层各个神经元的输出值非常相近,出现了梯度弥散。

哭是没有用的,问题是要解决的。于是2015年的时候有人发论文提出了解决方案。
在想想ReLU这个激励函数的图形,当数据进来的时候,如果小于0,那么这些数据就会被拦下不再前进了,所以每次经过这个激励函数,总有一部分数据因为小于0被拦截,所以剩下的数据就一层一层地越来越少,数据少了,对应的方差也就越来越小了。

为了解决这个问题,可以将之前输入节点n的开根号,还成n/2的开根号,表示每一此过激励函数都会被斩掉一半的输出,也就是下层激励函数的输入,既然节点数被斩掉了一半,那么自然权重应该相对应地增加了。

根据新的方法,输出了以下结果:

QQ截图20160818150410.png-382.2kB

从图中可见,均值和方差都随机波动,不再连续减小或上升,处于正常状态。另外,柱状分布图从第1层到第2层都的区别也不大了。

所以,真是棒棒的!

1.6 batch normalization

虽然如此,但是ReLU仍然非常脆弱。在15年的时候google的同学发表论文提出了一个新的方案——batch normalization。

如果激励函数的输入是满足高斯分布的,激励函数的输出也就可以避免上述过程中的这些问题了。(因为输入的变化的幅度保持在一个范围之内)
但事实上输入的x的分布有时并无章可循,所以我们要人为地将激励函数的输入的分布转换为高斯分布。
(注意,这里的输入是激励函数的输入,也就是全链接FC后的输出,也就是线性函数z = wx +b 之后的z.)

那么如何对输入做一个高斯变化呢?变化的公式如下,其实就是标准正太分布的一个转化。原来的输入值减去均值再除以标准差,最后得到的结果就服从了均值为0,方差为1 的标准正态分布了。
QQ截图20160818165203.png-17kB

但是呢,强行地转化可能会存在问题,因为我们之前说过每一个神经元都有自己的特性关注着独有的特征,如果强制地高斯分布的转换,就会使他们丧失这种特性了。所以在通过上述公式转化之后,再加上下面这一步。这是一个线性的转换,将高斯分布的值乘以一个数再加上一个数,产生了一个新的值y。这个y值就是输入激活函数的值。
QQ截图20160818165530.png-15.8kB
&gamma;和&beta;这两个系数是可以被学习的。这样的转化使得之前因为强行转换而丧失的特性可以被还原回来。

batch normalization 的优点
自从有了batch normalization,大家都开始大规模地训练神经网络了。
1.梯度传递更为顺畅,也就是说计算更方便了
2.学习率设高一点也没有关系。如果使用哈维尔初始化方法,就算运用n/2的开根号,学习率太大的时候,也会存在在层次比较深的时候出现loss的低空震荡。
3.对于初始值的依赖减少了。
4.可以看作是一种正则化,对dropout的需求减少了

batch normalization要注意的地方
1.如果层次深,那么所有激励函数前使用BN会加长时间30%
2.是否需要加batch normalization需要视情况而定

2. 正则化与dropout

2.1 正则化

之前我们提到过过拟合的问题,神经网络想秀一秀自己对训练数据的拟合程度与精确程度,结果秀过了。我们的目的并不是得到一个模型去和训练数据完全拟合,而是需要这个模型能够对所有数据都有一个良好地拟合。对训练数据过于精确地拟合反而对其他数据就不那么精确了。就像量身定制的衣服只适合于一个人,而通用的S,M,L码对大部分人都是可以适合的。

为了避免过拟合,我们可以使用正则化。正则化的目的是不让模型过于拟合也不是它偏移。正则化的类型有但不限于以下几种:
1.L1正则化,即在损失函数上加上&lambda;|w|,如此噪声就会因为得不到权重而被抑制。这叫做“阶段效应”。
2.L2正则化,即在损失函数上加上&lambda;w^2,也就是&lambda;||w||2,这回产生“缩减效应”
3.L1+L2
4.最大范数约束。

但其实在神经网络中很少用正则化的方式去避免过拟合,原因如下:
1.神经网络中的权重w非常多,对每个权重都去加上&lambda;|w|,会使计算变复杂。
2.在正则化中产生了一个超参数&lambda;,是需要人为却设定的,它的大小会影响模型的训练。

2.2 dropout

dropout是2014年的一篇论文中提出的。它的原理是不一次性开启所有学习单元。如下图:
左图中的是全链接的神经网络,会非常精确地去训练与预测,右图却中关闭了一些神经元。也就是说别让你的神经元去记忆所有东东,要有一些泛化能力。也可以理解为,不要让你的神经网络去听信一家之言,对不同的模型做一个融合。(因为每个batch过来关闭掉的节点是不同的)

QQ截图20160818174635.png-98.6kB

3. 训练检查与监控

3.1 在小数据集上“过拟合”

在真正训练模型前我们需要做这样一步:
在所有输入数据集中取出一小部分数据,比如500张图片,将这些小数传入关闭了正则化的神经网络。如果神经网络的神经元够多,层次够深,它是可以完全记忆住每一张图片的,也就是误差为0。我们需要去保证对于小数据集,这个神经网络是能够实现“过拟合”的,因为这可以证明这个神经网络的实现是正确的。

QQ截图20160819092305.png-763.1kB

3.2 加入小强度的正则化

在确保了神经网络在小数据集上的过拟合,接下来就加入正则化,但强度不要太大,去观察输出的损失loss是否在下降.如果逐渐下降则该神经网络通过检验。

下面那是python代码和输出结果。
QQ截图20160819092319.png-805.9kB

使用一些开源的工具可以很方便地去画图,左图是监控的loss图,横坐标是迭代次数,纵坐标是损失值。因为我们是将所有数据分成许多batch去分批训练的,所以一次迭代每个batch都会有一个损失值,画在坐标上就会形成一条垂直的小段线,经过100次的迭代,我们发现总体而言这条粗壮的损失线是波动下降的,它之所以是波动的,是因为每个batch的数据训练难度不同,在迭代第50次的时候,可能有些数据集难度特别大所以损失比迭代第30次的时候要高。但是总体来说,损失是下降的。

QQ截图20160819093533.png-149.2kB

右上图的四条线分别代表了不同的学习率的损失的变化。学习率就是在SGD求梯度时下山的步长。如果学习率很高步长很大,一下子就跨到另一座山上了,损失就增大了。如果学习率稍微下降一点但还是偏高的话,损失一开始会下降地很快,但下降到一定维度就下降地非常非常缓慢甚至不变了。如果学习率很低步长很小,那么在下山的过程中走得会非常慢,所以损失的确会下降,但是下降地很慢,要非常多次迭代后才能降到最低点,这个就极度消耗了计算机的资源和训练的事件了。一个好的学习率,应该是像红线一样,逐渐快速下降,并短迭代内到达最低点。

3.3 对比训练集与验证集上的准确率

第三个监控与检查的方向是对比训练集与验证集的准确率

最好的状态是,训练集与验证集随着迭代次数的上升都往较高的准确率走,且两者准确率相差不大。像下图的红线与绿线,训练集的准确率比验证集的稍微高了一点,其实就出现了一点点的过拟合了。

如果验证集的准确率出现了蓝色线样,就说明出现了严重的过拟合。神经网络对于训练集能够很好的拟合并且准确率很高,但是在验证集上却准确率很低。此时就要重新训练模型。

QQ截图20160819094834.png-130kB

4. 最优化与参数更新

4.1 SGD 与学习率

只要学习率很小,就能保证loss一直减小,但是收敛会非常慢
QQ截图20160819100145.png-5.6kB

动量更新Momentum update
这是受物理学启发的优化方法,加快了收敛速度。这个方法加入了一步分加速度:
QQ截图20160819100048.png-20.2kB
v可以初始化为0,mu可以设置为0.9左右值。

Nesterov Momentum
另一种优化的方法是基于凸优化理论,它的收敛更好。
当我们下山中每向下走一步,走完之后的位置其实已经变化了,所以应该用心的位置去做求导,如下公式
QQ截图20160819100437.png-16.1kB

QQ截图20160819100458.png-77.2kB

学习率衰减
除了使用以上两种方式外,我们还可以让学习率去自己衰减。随着迭代次数上升,学习率应该也随之下降。

1.步伐衰减
比如每20轮完整训练周期,就下降20%的学习率

2.指数衰减
随着t的上升,a会逐渐下降
QQ截图20160819100922.png-21.7kB

3.1/t衰减
QQ截图20160819101034.png-22.4kB

5. 深层网络与ResNet

ResNet是微软亚洲研究院提出的,获得了ILSVRC2015年的冠军,比之前风靡的VGG还要深8倍,全称为Deeo Residual Learning network.

当神经网络的层次很深,残差传回来的信号就会越来越弱。为了解决这个问题,能不能造一条桥直接将input送到后面那些层次。文字好难解释,来看下图,x是input,weight layer就是卷积计算层,rele是激励层。x首先输给了卷基层,然后经过激励层用给了卷基层,在进入下一个激励层之前,我们将卷基层里出来的数f(x)加上一个由最开始的输入层过来的x,加和的数再作为输入传入下一个激励层。x保留了原始的信号,使其避免了在传递过程中信号越来越弱的问题。

QQ截图20160819102207.png-29kB

左图画出了不同层次的ResNet的损失图,实线是验证集,虚线是训练集,可以看出层次越深的话损失就越低了。
但是一个有趣的事情是,当层次继续上升,到了1202层的时候,它的损失反而比110层的要大了。

QQ截图20160819103038.png-240.3kB

下面是ResNet的python代码

QQ截图20160819103231.png-98.1kB
QQ截图20160819103242.png-161.1kB
QQ截图20160819103255.png-162.9kB

相关文章
最新文章
热点推荐