这篇笔记主要是关于深度学习的实用层面的,包括以下内容:
目录
1. 训练、验证、测试集(Train\Development\Test sets)
3. 机器学习基础(Basic 'recipe' for machine learning)
5. 为什么正则化可以防止过拟合(Why regularization reduce overfiting)
6. Dropout正则化(Drop regularization)
7. 理解Dropout(Understanding Dropout)
8. 其它正则化方法(other regularization methods)
10. 梯度消失/梯度爆炸(Vanishing/exploding gradients)
11. 神经网络的权值初始化(Weight initialization for deep network)
12. 梯度的数值逼近(Numerical approximation of gradients)
14. 梯度检验的注意事项(Gradients checking implementation notes)
1. 训练、验证、测试集(Train\Development\Test sets)
在建模的时候,通常是在训练集上训练模型,在测试集上测试模型的泛化性能。对于一个数据集,既要训练又要验证最后再对模型进行测试,这就需要对数据集进行划分,把数据集划分为三个子数据集:训练集、验证集以及测试集。
1.1 吴恩达老师
处理小数据量时,可以对数据集进行以下划分:
无验证集:trainset:testset = 7:3
有验证集:trainset:dev:testset = 6:2:2
处理大数据量时,数据集中的绝大部分数据都用来训练模型,所以有以下划分:
trainset:dev:testset = 98:1:1
数据集的划分在机器学习中是一个比较重要又关键的步骤,但是吴恩达老师在这部分讲的东西并不是很多,所以在此补充了周志华老师西瓜书中的相关内容。
1.2 西瓜书
数据集的划分方式有多种,常见的方法有留出法、交叉验证法以及自助法。
1.2.1 留出法
“留出法”(hold-out)直接将数据集D划分为两个互斥的集合,其中一个作为训练集S,一个作为测试集T。在训练集S上训练模型,用T来评估其测试误差,对泛化误差进行估计。也可以将数据集D划分为三个互斥集合,一个为训练集S、一个为验证集V、一个为测试集T。
需要格外注意的是,训练集、验证集和测试集中的数据要有一致的分布,比如在分类任务中,三个数据集中的正负样本的比例要相似;各个特征所占的比例也要尽量相似。若从采样的角度看待数据集的划分,那么保留类别比例的采样方式通常称为“分层采样”。如数据集D中有500个正样本、500个负样本,那么对D进行分层抽样得到含60%的训练集S、20%的验证集V和20%的测试集T,那么训练集中包含300个正样本、300个负样本;验证集中包含100个正样本、100个负样本;测试集中包含100个正样本、100个负样本。
1.2.2 交叉验证法
“交叉验证法”(cross validation)将数据集D划分为k个大小相似的互斥子集,每个子集尽可能的保证有一致的数据分布。每次采用k-1个子集的并集作为训练集,余下的子集作为测试集;这样就可以获得k组训练/测试集,进行k次训练和测试,返回k个测试结果的均值。同常将这种交叉验证的方法称为“k折交叉验证”。通常k取值为10,此时称为10折交叉验证。
图片来自周志华老师的西瓜书。
1.2.3 自助法
“自助法”(bootstrapping)以可重复采样为基础,对包含m个样本的数据集D进行采样,每次从D中随机挑选一个样本,将其拷贝到D'中,然后把该样本放回数据集D,此样本在下次采样中仍然有可能被选中,重复m次这样的操作,会得到包含m个样本的数据集D'。显然有一部分样本会多次出现在数据集D'中,有一部分样本不会出现在数据集D'中。据统计,初始数据集D中大约有36.8%的样本不会出现在采样数据集D'中。这样用D'作为训练集、D\D'作为测试集。
自助法产生的数据集改变了初始数据集的分布,这会引入估计偏差,因此,自助法在数据集很小时是一种有效的划分方式,在数据集较大时,一般采用留出法和交叉验证法。
2. 偏差/方差(Bias/Variance)
在机器学习和深度学习中,我们要做的就是找到使得模型误差最小的参数。所以误差,是建模中最为关键的因素。一般情况下,误差由三部分组成,即:
其中为偏差,
为方差,
为噪声。
偏差度量了学习算法的预测值和真实值偏离程度,即刻画学习算法本身的拟合能力;方差度量了同样大小的训练集的变动所导致的学习性能的变化,刻画了数据扰动所造成的影响;噪声表达了当前任务上任何学习算法所能达到的预期泛化误差的下界,即刻画了学习问题本身的难度。而本小节只考虑偏差和方差之间的权衡。
在上面的左图中,线性模型不能很好的拟合数据,从而造成了high bias,这时候模型是欠拟合,也就是模型过于简单了;右图中的模型对所有的样本都正确的分类,模型的拟合能力很强以致于把训练样本自身的特征当做了所有潜在样本都会具有的一般特征,导致了模型的泛化能力差,即在测试集上的误差较大,这就造成了high variance,这时模型是过拟合。中图的模型是一个恰到好处的模型,既较好的拟合(没有过度拟合)了数据,也有较好的泛化能力。
可以看到,左图和右图是不能同时达到的,bias和variance之间有个权衡,其取值与训练程度之间的关系如下:
可以看到,在虚线处,模型的泛化误差最小,虚线之后,随着模型复杂度的增加,模型的偏差越来越低,方差越来越高,同时模型的泛化误差越来越高。
关于bias和variance的种类可以用下面的靶向图形象的表示
我们最不希望看到的情况就是模型的偏差和方差都很高,如下图所示:
上图中的模型将数据中的噪声(众多黑圈中的红叉和众多红叉中的黑圈)学习了,反而更一般的特征没有学习到。关于偏差和方差的更详细的分析可以参考这篇文章。
3. 机器学习基础(Basic 'recipe' for machine learning)
在建模中,解决high bias和high variance的过程:
4. 正则化(Regularization)
在上面一小节我们可以知道,可以通过增加数据量来防止过拟合(或者解决high variance问题),但是通常获取更多的数据成本比较高,因此常用的防止过拟合的策略是正则化。所谓正则化就是在代价函数后面加入正则化项。
4.1 逻辑回归:
加入正则化项后的代价函数:
正则化:
,
正则项前的系数之所以取
,是为了对
求导后能将系数2约掉。
正则化:
为正则化因子,其取值大于0。在python中,因为lambda是定义匿名函数的关键字,所以通常用变量lambd表示参数
4.2 神经网络
加入正则化项后的代价函数:
其中,
为
的维度,该矩阵范数称为:Frobenius norm.
因为在神经网络中需要进行反向传播,所以需要知道在加入正则项后的权值矩阵的梯度:
代入梯度更新公式得:
因为,所以
,那么
,这样会给原来的
一个衰减的参数,所以
正则化也被称为“权重衰减(Weight decay)”
4.3
正则和
正则
既然说到了正则化,就不得不说正则和
正则的区别和联系,尽管这不是吴恩达老师课程中的内容。
首先正则和
正则都可以防止过拟合(为什么正则化可以防止过拟合将在下一小节说明),除此之外
正则更容易获得“稀疏解”,即它求得的
会有更少的非零分量。可以从下面两个角度进行说明:
1. 贝叶斯角度
正则化相当于对模型参数
引入了拉普拉斯先验,而
正则化相当于对模型参数
引入了高斯先验,拉普拉斯分布和高斯分布的曲线图如下(图片来自百面机器学习):
可以看到高斯分布在极值点处是平滑的,也就是高斯先验分布认为在极值点附近取不同值的可能性是接近的,这就是
正则化只会让参数
接近0点,而不会等于0的原因;拉普拉斯分布在极值点处是一个尖峰,所以拉普拉斯先验分布中参数
取值为0的可能性要更高。
2. 解空间角度
带正则项和带约束条件是等价的,可以看做正则项是在最优化问题上加了个约束条件,正则化的解空间是一个菱形,而
正则化的解空间是一个圆形,如下图所示(图片来自西瓜书):
可以看到正则化与平方误差项等值线的交点常出现在坐标轴上,这就意味着
或
为0,而
正则化与平方误差项等值项的交点通常出现在某个象限中,即
和
都不为0,所以
正则化更容易得到稀疏解。
5. 为什么正则化可以防止过拟合(Why regularization reduce overfiting)
神经网络的有正则化项的代价函数如下:
我们的目的是要最小化代价函数,当取值较大时,为了是代价函数最小,就需要让参数
尽量小,这时神经网络中很多神经元的权重就变小了,即某些网络的影响变小了,相当于网络变的简单了,从而防止了过拟合。
另一种解释:
假设激活函数为,其函数图像如下:
可以看到在x取值较小时,即红色曲线部分,这部分曲线是接近于线性的,加入正则化项后,会变小,那么Z也会变小,此时激活函数的作用类似于线性函数,前面说过线性函数的线性叠加还是线性函数,所以这是神经网络类似于一个线性模型,这也简化了网络,从而达到防止过拟合的目的。
6. Dropout正则化(Drop regularization)
Dropout(随机失活)是为每一个神经元节点设置一个随机失活的概率,对于保留下来的节点,我们将会得到节点较少,规模较小的网络。下面的左图是一个全连接网络,右图是一个经过Dropout后的网络。
Inverted dropout:
# 神经元保留概率
keep_pro = 0.8
# d3为一个随机矩阵,矩阵中元素为True或False
d3 = np.random.rand(a3.shape[0],a3.shape[1])<keep_pro
# 使a3中的某些元素为0,即其对应的神经元失活
a3 = np.multiply(a3,d3)
a3 /= keep_pro
说明一下最后一句 a3 /= keep_pro. 上面将keep_pro设置为0.8,那么有20%的神经元会失活,即中有20%的元素为0,在下一层的计算中有
,为了不影响
的期望值,所以需要
除以keep_pro.
7. 理解Dropout(Understanding Dropout)
这里我们以单个神经元入手,单个神经元的工作就是接收输入,并产生一些有意义的输出,但是加入了Dropout以后,输入的特征都是有可能会被随机清除的,所以该神经元不会再特别依赖于任何一个输入特征,也就是说不会给任何一个输入设置太大的权重。
所以通过传播过程,dropout将产生和L2范数相同的收缩权重的效果。
对于不同的层,设置的keep_prob也不同,一般来说神经元较少的层,会设keep_prob=1.0,神经元多的层,则会将keep_prob设置的较小。
7.1 dropout为什么可以防止过拟合
dropout能防止过拟合的第一感觉是因为它使一些神经元失活了,从而使得网络规模更小,模型变的更简单,其实我们还可以从其它的角度去思考这个问题。
1. dropout有取平均的作用:dropout掉不同的隐藏神经元就类似在训练不同的网络,随机删掉一半隐藏神经元导致网络结构已经不同,整个dropout过程就相当于对很多个不同的神经网络取平均。而不同的网络产生不同的过拟合,一些互为“反向”的拟合相互抵消就可以达到整体上减少过拟合。
2. dropout可以减少神经元之间复杂的共适应关系:因为dropout程序导致两个神经元不一定每次都在一个dropout网络中出现。这样权值的更新不再依赖于有固定关系的隐含节点的共同作用,阻止了某些特征仅仅在其它特定特征下才有效果的情况 。迫使网络去学习更加鲁棒的特征 ,这些特征在其它的神经元的随机子集中也存在。换句话说假如我们的神经网络是在做出某种预测,它不应该对一些特定的线索片段太过敏感,即使丢失特定的线索,它也应该可以从众多其它线索中学习一些共同的特征。从这个角度看dropout就有点像L1,L2正则,减少权重使得网络对丢失特定神经元连接的鲁棒性提高。
8. 其它正则化方法(other regularization methods)
8.1 数据扩增
前面说过,防止过拟合的其中一种方式是增加数据量,但是有时候获取大量数据的成本比较高,这时我们就可以根据已有数据进行一些数据扩增,比如在做图像处理时,图像的数量较少时,我们可以通过翻转图像,裁剪图像等获得更多的数据。
8.2 Early stopping
early shopping就是提前停止训练神经网络,在泛化误差还没有增加的时候停止。
9. 归一化输入(Normalizing inputs)
归一化就是让数据点更加均匀的分布在原点附近,如下图所示。
具体的归一化公式为:,其中
为样本均值,
为样本方差。
那么为什么要对数据进行归一化呢,因为如果各个特征有不同的量纲有较大差距,那么每个特征对应的权重会有很大的差距,这样以来代价函数的反应到图像上就如左边两幅图,在这种情况下代价函数的优化速度会非常慢;而进行归一化后,其代价函数图像如右边两幅图,形状比较规则,在这种情况下梯度下降速度会很快。
10. 梯度消失/梯度爆炸(Vanishing/exploding gradients)
当神经网络较深时,容易发生梯度消失和梯度爆炸的现象,假设有如下的网络(每一层只有两个神经元):
为了计算简单,令激活函数为线性函数即,令偏差
,那么容易得到以下式子:
如果权重矩阵的元素大于1:
,那么有
,激活函数的值指数级增加
如果权重矩阵的元素小于1:
,那么有
,激活函数的值指数级减小
实际上,上面是前向传播的过程,是激活函数的值的增减变化,并不是梯度的指数级增加或指数级下降;对于反向传播,梯度的变化情况实际上是和前向传播中激活函数的变化情况是类似的。
在梯度函数上出现的以指数级递增或者递减的情况就分别称为梯度爆炸或者梯度消失。
11. 神经网络的权值初始化(Weight initialization for deep network)
利用权值初始化可以在一定程度上缓解梯度消失和梯度爆炸的问题,以单个神经元为例:
在忽略偏置的情况下,有:
,为了防止z过大或过大,当神经元的特征数n比较大时,我们希望
较小,这样才会使得每一项
比较小,从而使得每一项的和z不会太大。通常令
的方差为
,即
。
参数初始化的代码实现为:
# 服从标准正态分布的矩阵乘以wi的标准差
wl = np.random.randn(wl.shape[0],wl.shape[1])*np.sqrt(1/n)
当激活函数为tanh时,通常令;当激活函数为ReLu时,通常令
。其中n为第l层每个神经元的输入特征数,即
12. 梯度的数值逼近(Numerical approximation of gradients)
使用双边误差去逼近导数要比单边误差逼近导数精确的多。
双边误差(实际上就是高等数学中的导数的定义):
单边误差:
假设上面的函数为,那么
在
处的导数为:
,由双边误差求得的
,误差为
;而由单边误差求得的
,误差为
,明显可以看到由双边误差求得的导数的精度更高。
13. 梯度检验(Gradient checking)
在做梯度检验之前首先将参数连接为一个向量
;将梯度
连接为一个向量
.
接下来用上一小节中讲到的双边误差求得近似梯度,然后比较其与实际梯度的差值,判断近似梯度与实际梯度
的接近程度,具体的衡量公式为:
其中为欧几里得范数,其为向量各元素的平方的和,然后开平方。若上面公式的值小于
,那么说明梯度没问题,如果小于
,则需要检验程序中是否有bug
14. 梯度检验的注意事项(Gradients checking implementation notes)
1、不要在训练过程中使用梯度检验,只在debug的时候使用,使用完毕关闭梯度检验的功能;
2、如果算法的梯度检验出现了错误,要检查每一项,找出错误,也就是说要找出哪个与
的值相差比较大;
3、不要忘记了正则化项;
4、梯度检验不能与dropout同时使用。因为每次迭代的过程中,dropout会随机消除隐层单元的不同神经元,这时是难以计算dropout在梯度下降上的代价函数J;