本周主要讲解神经网络机器学习中的问题,学习一些能够确保神经网络正确运行的技巧。例如在配置训练、验证和测试集的过程中做出正确的决策会在很大程度上帮助大家创建高效的神经网络。训练神经网络时,我们常需要做出很多决策,例如神经网络分多少层;每层含有多少个隐藏单元;学习速率是多少;各层采取哪些激活函数等。
深度学习的数据一般有三种用途:训练集、交叉验证集和测试集。其中训练集用于训练模型,然后通过验证集或者简单交叉验证集来选择最好的模型,经过选择之后再在测试集上进行无偏估计,评估算法的运行情况。
在机器学习的小数据时代,人们通常的做法是将数据三七分,就是0.7的训练集和0.3的交叉验证集,没有设置单独的测试集,或者0.6的训练集,0.2的交叉验证集和0.2的测试集。
但是在如今的大数据时代,人们往往不需要那么大比例的交叉验证集和测试集,假如有100W条数据,那么只需要1万条作为验证集,1万条作为测试集即可,即0.98;0.01;0.01的分配比例。对于更大的数据量,比例可以占到0.995;0.0025;0.0025。
深度学习需要大量的数据,训练过程中应尽量确保验证集和测试集的数据来自同一分布,因为最后要用验证集来评估不同的模型,尽可能的优化性能,因此验证集和测试集来自同一个分布会更好。
测试集的存在只是用于进行无偏评估算法的性能,如果不需要评估算法性能的话,不设置测试集也是可以的。
解释:
1.偏差:指的是数据真实值和预测值之间偏离的程度,偏差越大表示偏离真实值越大。
2.方差:指的是数据预测值的变化范围,离散程度,也就是离期望值的距离。方差越大,数据的分布越分散,对象是多个模型。如果均值是一样的,方差越大,则数据的离散程度就越大,数据就越分散。
在忽略噪声的情况下,泛化误差可分解为偏差、方差两部分。
偏差:度量学习算法的期望预测与真实结果的偏离程度,也叫拟合能力。
方差:度量了同样大小的训练集的变动所导致的学习性能的变化,即刻画了数据扰动造成的影响。
高偏差,低方差--欠拟合--high bias--换一个更大的神经网络;试着评估训练集或训练数据的性能;或者花费更多的时间和尝试更先进的优化算法。
高方差,低偏差--过拟合--high variance--获取更多的数据;正则化;找更好的神经网络框架。
方差和偏差并不是会只有一个高,有可能存在既有高偏差又有高方差的情况。比如一个准确性为0.99的训练集,学习出的准确率仅为0.80,交叉验证为0.70,这两个误差都特别的高,便是上述的那种两种都高的情况。最优误差也成为贝叶斯误差,这是一个比较准确的误差。这里接近于0%。
正则化是抑制模型过拟合/减少网络误差 的一个重要的方法。本节以逻辑回归为例讲解正则化的原理及作用。
在逻辑回归中,我们需要最小化代价函数:
正则化时我们需要最小化的代价函数形式为:
即加上了一个欧几里得范数的平方(所有平方的和):
此方法成为L2正则化,因为这里用了欧几里得法线,被称为向量参数W的L2范数。
有人可能会好奇,为什么只正则化参数w呢?因为w一般是一个高维参数矢量,已经可以表达高偏差问题了,而b只是单个数字,因此加不加上b,其实对结果没有太大的影响.
L2正则化是最常见的正则化类型,此外还有一种L1正则化,L1正则化加的不是L2范数,而是L1范数,即:
如果使用L1正则化,则W最终会是稀疏的,也就是说W中会有很多的0。lambda是正则化参数,通常使用验证集或交叉验证来配置这个参数。同时要考虑和训练集之间的均衡,把正则化参数正常值设置成较小值,可以避免过拟合。因此正则化参数是另一个需要调整的超参数。
在神经网络中应用正则化方法如下:
这个范数被称为‘Frobenius norm’(弗罗贝尼乌斯范数),表示一个矩阵中所有参数的平方和。在进行参数更新时
因此正则化也被称为权重衰减。
高偏差和高方差的直观数据体现如下:高偏差的的直观效果如左图所示,表现为预测的数据与真实数据相差太大,拟合能力较弱。高方差的效果如第三张图所示,表现为模型的预测值受数据干扰太大。
一个简单的深层神经网络示意图如下:假设现在这已经是一个过拟合的神经网络,通过添加一个正则项,可以避免数值权重矩阵过大。从公式上直观的理解就是添加了一个正则化参数,使得权重矩阵W中的很多元素变得特别小,相当于很多的神经元被消除,或者功能被减弱,最终使这个网络变得更加简单。实际上这些神经网络中的隐藏单元依然存在,只不过是对输出的影响变小了很多。
下面通过激活函数来直观的感受一下为什么正则化可以预防过拟合。假设我们用的是这样的激活函数:
当输出非常大的时候,其是一个比较复杂的非线性函数,在图中可以看出,当Z非常小的时候,我们可以利用双曲正切函数的线性状态。当Z变得更大或者更小的时候,激活函数便会变得非线性。因此只要Z保持在一个相对较小的范围内,这一层就大致呈线性关系,之前介绍过,如果每一层都大致呈线性关系,则这一个深层网络就是一个线性网络,因此他就不适合用于非常复杂的决策,便不会出现过拟合的问题。
工作原理:假设在如下的神经网络中进行训练,存在过拟合的现象,dropout工作时便是给每一层的每一个节点设置一个概率,类似于抛硬币的方式,随机使某些节点失活,从而减小神经网络的复杂度,抑制过拟合现象。
实施dropout:
1.inverted dropout (反向随即失活)
用一个三层的神经网络为例,本文只举例在某一层中实施dropout。首先定义一个向量d,d3表示一个三层的dropout向量:
d3 = np.random.rand(a3.shape[0],a3.shape[1]) < keep_prob
其中keep_prob是人为设置的一个具体数字,表示保留某个隐藏单元的概率,例如前面介绍的是0.5,在本节中我们将其设置成0.8,意思是有80%的节点被保留下来,20%的节点被失活。如下图所示。
然后从第三层中获取激活函数,让激活函数a3与d3相乘,让d3中的0元素与a3中相对的元素归零。
a3 = np.multiply(a3,d3)
要注意d3中的元素都是布尔值,但是python在进行乘法操作时会自动的将其转化成数字0或者1。如果我们将keep_prob设为0.8,则会有20%的神经元被失活,其中,如今a3已经下降了20%,为了不影响Z4的期望值,所以反向随机失活(inverted dropout)通过在训练阶段除以keep_prob的值来保证期望值不变。类似的方法还有正向随机失活(dropout),区别就是正向随机失活是在测试阶段乘上keep_prob的值,来保证测试和训练阶段的Z的期望值不变。所以应用反向随机失活的完整步骤:
d3 = np.random.rand(a3.shape[0],a3.shape[1]) < keep_prob
a3 = np.multiply(a3,d3)
a3 /= keep_prob
在测试集中我们不使用dropout,测试的步骤跟普通的神经网络相同。
为什么dropout可以起作用呢?
在直观上,通过dropout,神经网络不再依赖任何一种特征,因为任何一个特征都有可能被失活,然后为了不影响最终输出的期望值,dropout会稍微增加一些权重的值,通过传播所有权重,dropout会产生收缩权重的平方范数的结果,与之前讲的L2正则化类似,实施dropout的结果是它会压缩权重,并完成一些预防过拟合的外层正则化;不同的是L2正则化对不同权重的衰减是不同的,它取决于激活函数倍增的大小。
dropout的功能类似于L2正则化,但与L2正则化不同的是,dropout更加的灵活,可以对不同的层应用不同的输入范围。
dropout通常应用与计算机视觉领域内,由于没有获得足够的数据,所以经常 存在着过拟合的现象。但是不可避免的也存在着一个缺点:
由于使用了dropout,代价函数J 不再被明确定义,所以通常先不用dropout,当确定代价函数J单调递减之后,再打开dropout,其中并不会引入bug.
1. 增大数据集
通过旋转、裁剪、方法、反转等操作对原数据集进行处理,但是这些额外的假数据无法获得更多的信息,但是却更加的廉价,除了一些对抗性代价,其余代价几乎为零。以这种方式扩充数据集从而进行正则化的方法比较廉价。
2. early stopping
代表提早停止神经网络的训练,以防止过拟合。我们首先绘制出代价函数J的下降曲线,然后绘制出交叉验证集的误差,你会发现交叉验证集的误差通常会先成下降趋势,然后再某个节点处上升。early stopping 的作用是在中间点停止迭代过程,我们可以得到一个W值中等大小的弗罗贝尼乌斯范数,与L2正则化相似,选择参数范数较小的神经网络,就可以抑制过拟合。
机器学习可以看成是两个重要部分的组合,一是最小化代价函数J,例如梯度下降、Momentum、RMSprop、Adam等等;二是防止过拟合,例如前面所说的正则化,扩充数据集和early stopping等。在机器学习中,超参数数量剧增,导致选出合适的算法也变得越来越复杂,我们通常使用的是一种“正交化”的思想,就是同一时间只考虑一件事情。即在最小化代价函数的时候,我们只需要考虑w,b这两个参数,让代价函数J的值变得越小越好,其他的现象(比如是否发生了过拟合)都不需要考虑。然后再进行其他的任务,比如进行正则化来减小方差,抑制过拟合。
early stopping的主要缺点是不能独立的处理上述的两个问题,在抑制过拟合的过程中也停止了优化代价函数J,用一种方法考虑两种问题往往会使问题变得更加复杂。如果不使用early stopping的方法,那么另一种常见的方法就是正则化,然而,使用正则化往往会增加超参数的数量,增加神经网络训练的时间,尝试大量超参数的计算代价太高。
early stopping的优点便是,只运行一次梯度下降,就可以找出参数W的较小值、中间值和较大值,而无需尝试L2正则化参数的很多值。另外虽然L2正则化有很多的缺点,但是很多人仍然愿意去使用它。
训练神经网络其中一个加速训练的方法便是进行归一化输入。假设数据集有两个特征,输入特征为二维,如下图所示:
则归一化需要有两个步骤:
1. 零均值处理
对数据进行处理,使它们的均值为零,即关于x1,x2轴对称。具体的方法是令 ,然后让每一个x都减去u即可,向量化可表示为:
。完成零均值化后的效果如下:
2.归一化方差
由上图可见特征X1的方差要比X2的方差大很多,对方差执行归一化之后,使X1和X2分部的离散程度大致相同。我们要做的就是计算,由于我们已经完成了零均值处理,所以传统的方差计算公式也得到了简化,正如上面所示 。然后将所有的X 都除以向量
,最后数据呈现下图形式:
要注意的是,测试集中使用的 要和 训练集 保持一致,而不是在测试集中重新计算这两个参数。下图展示了应用归一化的好处:可以使用更大的学习率进行学习,并且不论从哪一点出发,下降速率都是相同的。
训练神经网络尤其是深度神经网络常常面临的一个问题便是梯度消失和梯度爆炸。即训练神经网络时,导数或者坡度会变得非常的大或者非常的小,加大了训练的难度.产生的原因比较容易理解,假如有这样一个深度神经网络:
容易看出最终的输出:。如果每一层的参数W都大于1,则梯度就会变得很大,反之梯度就会很小,这种现象称为梯度爆炸/梯度消失。这种现象在很长一段时间中是训练深度学习的一个障碍,虽然有一个不能彻底解决此问题的方案,但是在如何选择权重初始化问题上提供了很多帮助。
实际上在第一周作业的博客中已经体现了这一章的内容,为了避免梯度消失与梯度爆炸,权重初始化是当前一个比较好的解决方法,所谓的权重初始化就是在初始化W的时候再乘上一个系数:
1.激活函数为tanh时,令W的方差为 :
w[l] = np.random.randn(n[l],n[l-1])*np.sqrt(1/n[l-1])
2.激活函数是ReLU,权重w的初始化一般令其方差为 :
w[l] = np.random.randn(n[l],n[l-1])*np.sqrt(2/n[l-1])
3.Yoshua Bengio提出一种初始化w的方法,令其方差为 :
w[l] = np.random.randn(n[l],n[l-1])*np.sqrt(2/(n[l-1]+n[l]))
双边误差比单边误差更加的精确,所以计算逼近值时用的就是双边误差。
梯度检验时用的方法是计算被检验向量的偏导数与与其逼近值之间的欧几里得范数,一般情况下取 ,然后如果其欧几里得范数的值远小于
,则一般不会有问题。其中欧几里得范数计算方法如下:
注意:
1. 训练时不要使用梯度检验。
2.如果采用了正则化处理,在进行梯度检验时也要加上相应的项。
3.进行梯度检验时不要使用dropout。
4.有可能在w和b非常接近于0的时候,梯度检验是正常的,增大之后就会出现错误。所以可以首先在参数初始化的时候进行梯度检验,然后在运行一段时间之后再重新运行梯度检验。
使用梯度下降法需要讲所有的数据集一次性加载完毕,但是当数据集非常大的时候,人们的电脑往往无法满足计算需求,而且执行效率也低,因此人们通常会采用小批量多次训练的方法,即设置很多的Mini-batch,每个Mini-batch中含有的样本数量被称为batch_size。
首先按照batch_size大小将X,Y划分成一个个的小的数据集,通过for循环对这些小的数据集进行运算,每一个小数据集之内的数据还是使用向量化的方式进行运算,相当于把一个500万的数据集分成5000份,每份包含1000个数据,分别计算这5000份数据集前向和后向传播的相关参数,然后更新每一个小数据集的权值:
直到
然后计算损失函数J:
最后更新权值:
执行完这5000个小的数据集称为程序运行了一步(1 epoch)。如果想让程序运行更多步可以在上述循环外面再加一层循环。但是之前介绍过用for循环的方式程序执行起来速度比较慢,因此当数据量比较小的时候还是还用batch进行梯度下降比较好。
此外还要注意两点:
1. 在Mini-batch中代价函数并不像在梯度下降中代价函数那样稳步下降,而是在不停的震荡,但是整体应该呈现下降趋势,如下图所示。
2.batch_size的大小一般可以选择为2的N次方,例如64,128,256,512,1024等。
给定一些温度数据,如下图所示。
如果要用指数加权平均计算趋势的话,即计算温度的局部平均值,可以通过下列公式实现:
就是0.9倍的前一天的温度加上0.1倍的今天的温度。同时这个系数也是可以变换的:
其中的系数比较值得关注,如果
=0.9,代表的就是过去十天的温度(这个结合公式就可以得出)
首先我们看一下有偏差修正和无偏差修正的指数加权平均是如何工作的:
图中紫色的线是未经过偏差修正的,绿色的线是经过偏差修正的,同时比较大(即考虑前面的天数比较多),容易看出虽然后面的趋势几乎相同,但是前面未经过偏差修正的误差还是比较大的,不是很理想。因此进行偏差修正还是比较有必要的,进行偏差修正的过程如下:
将原来的参数Vt改为即可,可见在t比较小的时候,该值比较大,在后期分母几乎为一,与之前的没有什么差别。