前言:经过前几章的理论加实战的演练,少奶奶相信大家都对机器学习和神经网络的原理和实现都有了比较深入的了解,那么少奶奶恭喜你已经完成了利用Tensorflow 2.0为驱动,系统的学习到了深度学习的大部分知识。但是,当大家在实际中会发现,自己和别人写的是同样的逻辑和网络结构,为什么别人的准确率总是大于自己的,这不是你模型的问题,而是你在训练神经网络时,没有用到一些网络调优的小技巧,本篇博文,少奶奶将给大家详细介绍一下,平时在训练神经网络时,需要注意的一些常用调优小技巧。
在现实生活中,数据往往服从着某种分布,例如:房子的面积和价格大致服从着线性分布,班级的成绩和人数大致服从着高斯分布,虽然我们不知道这些模型当中具体参数的数值,但经验告诉我们它们就是服从着这样的分布。所以,当我们在拟合这些数据时,模型直接设置成线性模型或者高斯模型就好,不需要没有方向的设置成其他简单或者复杂的模型。但实际情况是,我们即不知道样本服从什么模型,也不知道模型中的对应参数,所以,对于一个新的样本集,我们第一步就是要选取适合的模型去拟合该样本群。这时候,我们的模型就会出现过拟合和欠拟合的情况。
若我们采用的模型过于简单表达能力不强,那么就会出现欠拟合的情况。例如:我们在构建网络对cifar 100数据集进行训练时,使用了一个线性模型 y = wx +b, 由于该数据集十分的复杂,我们会发现训练的准确率会很低,loss值会很高,并且随着训练次数的增加,其值不会有太大的改变。但是,当我们把模型换成非线性模型时,准确率会上升,loss值会下降。所以,当发现欠拟合时,我们只需要把模型变得复杂就好。
若我们采用的模型过于复杂,即模型的表达能力过强,那么就会出现过拟合的情况。例如:我们在构建网络对MNIST数据集进行训练时,采用了一个6次方的非线性模型:。由于MNIST数据集比较简单,我们会发现在训练时,准确率和loss都会很好,但测试结果却很差(此时,我们通常把该现象叫做泛化能力过低),但是,我们把模型改成简单的线性模型y = wx +b时,其效果会变得十分理想。所以,一种避免过拟合的方法是降低模型的复杂度。其他方法还有:加大训练数据,添加正则项,使用dropout,数据增强,early stopping等。
由于现代计算机的计算能力十分的强大,使得欠拟合很少出现,通常情况下出现过拟合的情况会很多。所以,如何检测过拟合的发生是十分必要的。
通常情况下,我们把数据集分成训练集和测试集,前者用来训练网络并求取网络参数,后者用于测试训练结果。但是,我们在判断当前模型是否已经达到预期时,使用的是测试集的数据,换句话讲,我们一直在利用测试数据集挑选最好的模型,所以,最后测试的准确率很高,这种方法其实也是一种过拟合的表现,为了防止过拟合的发生,我们会选择把整体数据集分割成三部分:训练数据,validation集,测试数据。其中,训练数据用于训练模型,validation集用于挑选模型表现最好的时刻,测试数据集用于测试当前模型的准确率。并且,测试数据集是和其他两种数据集完全分开的。
当数据集被分成三部分后,用于训练的数据就会变少,但额外增加训练数据集的成本太高了,所以,在每次进行训练完一个批次后,我们都会重新划分训练集和validation集,这样就可以保证,除了测试集的数据外,其他数据都会用于训练。
下面是实现三种数据集划分的代码
我们也可以在fit函数中设置一下参数即可:
补充:当我们在参加AI挑战赛时,只会得到80%的数据集,剩下的20%数据集作为测试数据集由赛方拥有。当我们拿到数据集时,会先人为的把80%数据集划分成训练集和测试集。这就是我们上面讲的三种数据集。大家参加过这种比赛的话都会注意到有这样一条规定,一天之内最多提交三次结果用于测试。这是因为,若我们一天之内能提交无限多次的话,我们就可以从官方反馈的测试结果中,得到一个最好的准确率,然后利用这个准确率,人为的挑选一些数据集用于训练网络,进而达到一个更好的效果,这种方式其实是AI挑战赛中最常用的一种作弊方式,别名数据污染,即人为的干涉神经网络中参数的学习。这种方式用于打榜很好,但用于现实场景就不行了。
模型过于复杂的话,在训练过程中很容易出现过拟合的现象,为了防止模型过于复杂,我们通常采用正则化的优化方法,即为原来的loss函数添加一个正则项,正则项主要由模型的一范数和二范数构成,例如:
原来的loss函数:
添加正则项为一范数的loss函数:
添加正则项为二范数的loss函数:
通过在loss函数的后面添加一个关于权重的一范数或者二范数进而构建一个新的loss函数,可以实现对模型的惩罚。因为,优化器会使得loss的值最小,此时,正则项也会达到最小。loss函数的前半部分最小的话,可以使得网络的表达能力变强,而后者最小的话,可以使得复杂模型退化成简单模型,最后使得模型更加平滑。
在Tensorflow中,若我们使用keras中的模型接口的话,可以这样设置:
当然也可以自己构建loss函数,实现手动选择需要正则化的参数,具体如下:
即通过network模型中的trainable_variables参数,手动求取需要正则化的权重,然后添加到loss函数中即可。
动量在物理意义上可被解释为惯性,运用在神经网络中就是梯度下降方向的惯性。即k时刻梯度的方向是由k-1时刻的方向和k时刻的方向共同决定的。即k时刻的梯度下降方向要受到k-1时刻的梯度方向影响。公式如下:
不考虑动量的情况(传统的更新权重的方式):
loss值的更新过程
特点:前期更新时,loss方向变化很大,并在最后趋于稳定,但只能得到一个局部最小值点。
考虑动量的情况:
即在原来权重更新的基础上再减了一个,为上一时刻的梯度。通过引入上一时刻的梯度,我们就能避免loss函数停留在局部最小值的情况。
特点:loss值的方向在整个更新过程中,变化不会太尖锐,并且在最后,越过了局部最小值。
动量一般是作为一个优化技巧被添加在众多优化器中,我们只需要指定动量的比重就好了(通常为0.9)。当然有些优化器自己已经包含了动量,例如Adam,我们就不需要人为指定。
我们在训练神经网络时,常常出现这样的情况,少奶奶以MNIST数据集为例,我们把学习率设置成较大值时(lr=0.1),网络收敛得很快,但是准确率和loss值都不是很理想,当我们把学习率设置成较小时(lr=0.001),网络的收敛速度很慢,但准确率和loss值都很理想。所以,学习率的大小关系着网络的学习速率和准确率。通常情况下,学习率是根据经验设置成固定的值,但这很容易出现网络最终的训练效果并不是最好的,即网络最终的训练结果会在极小值周围来回波动,为了解决这样的问题,我们采用学习率衰减的优化方法。其思路是,随着训练次数的增加,学习率会按照某一种方式进行减少(按照线性,非线性,指数等方式),当训练完毕时,学习率会变成0。
当我们在训练过程中使用学习率衰减的话,就会出现上图中的效果,loss值会突然下降,采用该优化方式,能使训练的时间缩减,网络的准确率更高。下面将介绍代码中的具体实现。
先设置优化器的全局学习率为0.2,然后随着训练次数的增加,学习率开始以线性的方式减少,当训练完毕时,学习率为0。
训练网络时,测试准确率和训练准确率都在上升,直到某个点开始,测试准确率开始下降,这个时候就有可能开始出现过拟合,当然,后续训练也可能出现测试准确率上升的情况,若经验告诉我们此时的测试准确率已经不再上升了,那么我们需要在测试准确率下降的点马上停止训练。
dropout和正则项都能使得模型更加简单,但dropout是使得模型中使用的参数变少,正则项是使得你模型的范数变小。其实质是网络在前向传播时,有一定概率使得某一神经元输出的权重值暂时为0,但不改变该神经元原有的值。通过这样的方式,可以使得网络的范化能力变强。
在Tensorflow中的实现也很简单。
第一步:在keras中的Sequential中设置layers.Dropout
第二步:设置开启dropout的时候,训练时开启dropout,测试或者validation时,关闭dropout
到这里,少奶奶就利用学习Tensorflow 2.0框架,比较系统的介绍完二维图像中的神经网络的知识,希望大家能动起手来,边看边实战。多看几遍博文,不懂的,或者不对的希望大家留言,少奶奶将做出回复。下一章,少奶奶将介绍用于处理时序信息的RNN神经网络,但马上到9月份了,少奶奶也要出去找实习工作,所以更新会延后,不过能帮助大家的话,少奶奶也会很高兴。
共勉!
开篇:开启Tensorflow 2.0时代
第一章:Tensorflow 2.0 实现简单的线性回归模型(理论+实践)
第二章:Tensorflow 2.0 手写全连接MNIST数据集(理论+实战)
第三章:Tensorflow 2.0 利用高级接口实现对cifar10 数据集的全连接(理论+实战实现)
第四章:Tensorflow 2.0 实现自定义层和自定义模型的编写并实现cifar10 的全连接网络(理论+实战)
第五章:Tensorflow 2.0 利用十三层卷积神经网络实现cifar 100训练(理论+实战)
第六章:优化神经网络的技巧(理论)
第七章:Tensorflow2.0 RNN循环神经网络实现IMDB数据集训练(理论+实践)
第八章:Tensorflow2.0 传统RNN缺陷和LSTM网络原理(理论+实战)