4

改善深层神经网络的方法:消除过拟合、加速训练与超参数调试

 2 years ago
source link: https://yerfor.github.io/2020/02/28/improve_network/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本博客是在学习《吴恩达深度学习第二课—改善深层神经网络:超参数调试、正则化以及优化》时写的学习笔记。

主要内容有:

  • 消除过拟合的方法,这包括:
    • 正则化:L1、L2、Dropout
    • Early Stopping
    • 批标准化(算半个)
  • 加速网络训练的方法,这包括:
    • 归一化输入(也叫标准化输入)
    • 妥善初始化权重,以避免梯度消失和梯度爆炸
  • 优化器,这包括:
    • Momentum法
    • RMSprop法
    • Adam法
    • 学习率衰减
  • 超参数调试

1.消除过拟合的方法

1.1 过拟合和欠拟合

1.1.1 欠拟合 (Underfitting)

欠拟合:模型在训练集上表现就很糟糕,在测试集上也很糟糕。

通常出现在任务比较复杂而模型结构过于简单的情况,也可能是数据标注的关联度不够高,或者是训练的iteration还不够,当然代码上的bug也常常导致欠拟合(所以出现欠拟合时首先检查代码,血和泪的教训)。

解决方法:

  1. 换用更复杂、更深的网络;
  2. 对数据集质量进行优化,进行数据清洗,检查标注数据;
  3. 训练更多的迭代步数;
  4. 检查代码bug。

1.1.2 过拟合 (Overfitting)

过拟合:模型在训练集上表现很好,但在测试集上表现却很糟糕,模型的泛化能力不够。

通常出现在数据量不够,任务过于简单而模型的参数量过大的情况,因为复杂的模型会拟合过多的噪声数据(因此大量采用全连接层经常会导致过拟合,而采用卷积层可以避免这一点)。

解决方法:

  1. 获取更多的数据,做数据增强;
  2. 降低模型的复杂度,减少模型的层数、神经元数,减少使用全连接层等;
  3. early stopping,实时追踪训练集准确率和验证集准确率的曲线,在后者出现拐点时立刻停止。
  4. 正则化方法,降低权重的大小,包括L1正则化L2正则化
  5. DropOut方法,每次训练随机弃用固定比例的神经元,避免网络对某一些神经元过于依赖,提升了模型泛化能力。
  6. Batch Normalization可以轻微地减轻过拟合,并且batch_size越小效果越强。

1.2 数据增强(data augmentation)

常见的数据增强方式有:

  • 将原始图片旋转一个小角度;
  • 加入随机噪声;
  • 修改图像的H、S、V通道;

  • 截取(crop)原始图片的一部分;

    • 论文《Deep learning face representation from predicting 10,000 classes》中,从一副人脸图中,截取出了100个小patch作为训练数据,极大地添加了数据集。
  • 一些有弹性的畸变(elastic distortions)
    • 论文《Best practices for convolutional neural networks applied to visual document analysis》对MNIST做了各种变种扩增。

1.3 正则化 (Regularization)

1.3.1 L2正则化

  • 在损失函数后面加上一个L2正则化项,所谓的L2正则化项为:需要正则化的层的权重矩阵的二阶范数,取平均后,乘以一个超参数$\lambda$,公式如下:

其中,二阶范数$||w||_2$的定义式为:

L2正则化又名为权重衰减(Weight Decay),这是因为在反向传播时L2正则化项的作用就是使对应层的权重按比例缩减,下面进行证明。

先计算梯度:

在更新权重时:

相当于每次更新权重的时候,都会对其做一次等比例缩放,让权重的数值慢慢减小,这也是权重衰减这个别名的由来。

下面解释为什么减小权重数值就可以减轻过拟合。

过拟合的时候,拟合函数的系数往往非常大,为什么?过拟合,就是拟合函数须要顾忌每个点。因此过拟合的拟合函数波动非常大。在某些非常小的输入区间里,函数值的变化非常剧烈。这就意味着函数在这些小区间里的导数值(绝对值)非常大,由于自变量值可大可小,所以仅仅有系数足够大的时候,才会保证导数值非常大。

而L2正则化是通过约束參数的范数使其不要太大,当参数的范数较小的时候,自变量波动导致的输出变化也会相应变小,不会拟合出过拟合时那种陡峭的曲线,所以能够在一定程度上降低过拟合情况。

1.3.2 L1正则化

  • L1正则化L2正则化类似,不同点在于采用的是一阶范数。

其中,一阶范数的定义为:

在实验中发现L2正则化总是优于L1正则化,这可能是因为L1正则化对权重缩减的形式是每次改变固定大小,而L2正则化是按比例缩放。

下面证明使用L1正则化后,每次迭代时权重都会缩小一个固定的值。

先计算梯度:

上式中的$\frac{\lambda} {2\times batch_{-}size} \cdot signal(w)$始终是一个大小固定的常数,因此使用L1正则化后,每次迭代时权重都会变化一个固定的值。

关于上式中的$signal$函数,当$w$为正时,$signal(w)=1$, 则更新后的$w$变小。当$w$为负时,$signal(w)=-1$, 则更新后的w变大,特别的,当$w=0$时,$signal(w)=0$。L1正则化的效果就是让$w$往0靠近,使网络中的权重尽可能为0,也就相当于减小了网络复杂度,防止过拟合。

这里可以看出L1正则化和L2正则化的区别:当迭代步数足够大的时候,采用L1正则化的参数矩阵是非常稀疏(sparse)的,即很多地方都是0;而L2正则化的参数矩阵是整体范数比较小,各个神经元之间比较均衡,基本没有地方是0。

1.4 Dropout

正则化是通过修改损失函数,从而对权重进行修改。而Dropout则是直接对网络结构进行修改。

Dropout的思想非常简单,每次迭代的时候都临时禁用掉一定比例的神经元,在测试的时候再启用全部神经元(当然,需要按比例缩减权重大小)。

dropout

为什么Dropout可以解决过拟合?

参考资料:深度学习中Dropout原理解析

(1)取平均的作用: 先回到标准的模型即没有dropout,我们用相同的训练数据去训练5个不同的神经网络,一般会得到5个不同的结果,此时我们可以采用 “5个结果取均值”或者“多数取胜的投票策略”去决定最终结果。例如3个网络判断结果为数字9,那么很有可能真正的结果就是数字9,其它两个网络给出了错误结果。这种“综合起来取平均”的策略通常可以有效防止过拟合问题。因为不同的网络可能产生不同的过拟合,取平均则有可能让一些“相反的”拟合互相抵消。dropout掉不同的隐藏神经元就类似在训练不同的网络,随机删掉一半隐藏神经元导致网络结构已经不同,整个dropout过程就相当于对很多个不同的神经网络取平均。而不同的网络产生不同的过拟合,一些互为“反向”的拟合相互抵消就可以达到整体上减少过拟合。

(2)减少神经元之间复杂的共适应关系: 因为dropout程序导致两个神经元不一定每次都在一个dropout网络中出现。这样权值的更新不再依赖于有固定关系的隐含节点的共同作用,阻止了某些特征仅仅在其它特定特征下才有效果的情况 。迫使网络去学习更加鲁棒的特征 ,这些特征在其它的神经元的随机子集中也存在。换句话说假如我们的神经网络是在做出某种预测,它不应该对一些特定的线索片段太过敏感,即使丢失特定的线索,它也应该可以从众多其它线索中学习一些共同的特征。从这个角度看dropout就有点像L1,L2正则,减少权重使得网络对丢失特定神经元连接的鲁棒性提高。

(3)Dropout类似于性别在生物进化中的角色:物种为了生存往往会倾向于适应这种环境,环境突变则会导致物种难以做出及时反应,性别的出现可以繁衍出适应新环境的变种,有效的阻止过拟合,即避免环境改变时物种可能面临的灭绝。

1.5 Early Stopping

实时追踪训练集准确率和验证集准确率的曲线,在后者出现拐点时立刻停止。

Early stopping方法的具体做法是,在每一个Epoch结束时(一个Epoch集为对所有的训练数据的一轮遍历)计算validation dataaccuracy,当accuracy不再提高时,就停止训练。在实际训练的时候中,会记录到目前为止最好的validation accuracy,当连续10次Epoch(或者更多次)没达到最佳accuracy时,则可以认为accuracy不再提高了。此时便可以停止迭代了。

2.加速网络训练

2.1 归一化输入(标准化)

假设网络的输入数据的各个元素服从$N(\mu,\sigma^2)$的高斯分布(但不是标准高斯分布)。例如现在输入一个$x=[1,10,100,1000,10000]$的向量,那么我们说它服从$N(2222.22,15263847.35)$的高斯分布。

归一化输入的思想就是将原来服从高斯分布的数据转换成服从标准高斯分布,即$N(0,1)$的数据。根据概率论知识,通过下面的处理可以将$N(\mu,\sigma^2)$的数据转换成$N(0,1)$:

归一化的实质是将输入分布的各个特征修改到了相同量级,(因为所有特征现在都服从$N(0,1)$分布了),下面给一个启发式的示例:

normal2

可以看到原来量级差异为指数级的输入向量,经过归一化后各个特征之间量级差异变小了,但彼此之间的差异信息还是被保留(参见1和10000经过归一化后的元素是-0.56853297和1.99078685,二者之间的差异相对于当前分布而言还是很大)。

接下来看为什么归一化输入可以加速训练。

当输入向量的特征之间量级差异很大的话,它的loss函数如左下图所示,在某些维度上十分陡峭,而在某些维度上十分平缓,这样单一的学习率就无法满足在所有维度上的训练都很合适了。如左下图所示,梯度下降在较窄方向上的迭代存在非常明显的震荡。

而标准正态分布因为输入数据在同一量级,它的Loss函数一般呈一个圆型的碗装,如右下图所示,各个维度上的陡峭程度差异不大,这样我们可以找一个合适的学习率,不论初始化在哪个点,都可以快速收敛到极小值,且很少有震荡。

normal

总之,当输入数据的各个特征之间量级差异很大,那么归一化势在必行。如果数据的各个特征在一个量级,那么归一化的效果就不那么明显了。但归一化并不会带来什么危害,所以我通常都会对数据进行归一化处理。

  • 归一化处理的meanvariance可以直接对所有数据进行计算平均得到,测试集归一化用的meanvariance必须和训练集一致。

2.2 梯度消失和梯度爆炸:权重初始化角度的解决方案

梯度消失和梯度爆炸的原因是,前面层的权重梯度的计算公式,往往要乘上后面所有层的权重的连乘(全连接和卷积等所有涉及$y = w\cdot x + b$计算的层都是如此)。这样只要后面层的权重$w>1$,层数一深,前面层的权重梯度就会非常巨大,这是梯度爆炸;而只要后面层的权重$w<1$,层数一深,前面层的权重梯度就成了0,这是梯度消失

  • 梯度消失还和以前常用的激活函数Sigmoid有关,因为它在饱和时梯度接近于0,所以现在隐藏层的激活函数广泛使用的是Relu,但这里我们只介绍隐藏层权重对梯度消失和梯度爆炸的影响。

由上面的论述可知,隐藏层的权重大小只要略大于1或略小于1,在网络较深的情况下都会导致梯度爆炸和梯度消失,如果能够控制权重大小满足下述分布,就可以缓解这个问题:

其中$n^{l}$是当前层的神经元个数,$n^{l-1}$是上一层的神经元个数。

对于不同的激活函数,采用不同的方差效果会更好,下面是一些建议:

  • 对于$Relu$,$\sigma^2=\frac{2}{n^{l-1}}$
  • 对于$tanh$,$\sigma^2=\sqrt{\frac{1}{n^{l-1}}}$ (Xavier初始化)或$\sigma^2=\sqrt{\frac{1}{n^{l-1}+n^{l}}}$ (来自Yoshua Bengio)

2.3 批标准化(Batch Normalization)

关于Batch Normalization的基本概念,之前已经在一篇文章里介绍过了,可以去看那篇文章的第二章。

这里只记录新的理解:

2.3.1 BatchNormalization层在训练和测试时的不同

BatchNormalization在训练阶段时使用的$mean$和$variance$都是通过对当前batch的数据进行计算得到的。但在测试/使用阶段,输入数据通常都是一张一张给的,所以$mean$和$variance$需要额外给出。一个常见的做法时在训练阶段额外计算两个参数:分别是平均数和方差的移动平均(Moving Average),移动平均的概念会在第三章介绍。

下面以Tensorflow为例,解释训练阶段和测试阶段时BatchNormalization层运行方式的不同:

import tensorflow as tf

batch_size = 128
width,height,channel = 128,128,3
# 训练数据x_1,有128个图像
x_1 = tf.random.normal([batch_size,width,height,channel])
# 测试数据x_2,只有1个图像
x_2 = tf.random.normal([1,width,height,channel])
# 训练阶段,training=True, mean和variance计算自x_1
y_1 = tf.keras.layers.BatchNormalization(axis=-1)(x_1, training=True)
# 测试阶段,training=False,mean和variance来自储存于类里的属性:mean和variance的移动平均值
y_2 = tf.keras.layers.BatchNormalization(axis=-1)(x_2, training=False)

2.3.2 强调通道之间不能融合

批标准化能够work的重要原因就是和归一化输入相同的:将输入的分布转化成一个特定的高斯分布,方便加速训练。

批标准化和归一化输入都坚持了一个重要原则:

  • 通道之间不能融合。即mean、variance都是按照不同通道计算的。

下面分别结合输入归一化和BatchNormalization的实例,加深一下印象:

1.输入归一化时,RGB通道分别计算均值方差,得到两个$[3,1]$的向量:

normal2

2.批标准化时,在API里设定axis属性,指向的是channel(在[b,w,h,c]的format中就是axis=-1):

import tensorflow as tf
bn = tf.keras.layers.BatchNormalization(axis=-1)

3. 优化器(Optimizer)

3.1 指数加权平均(移动平均, Moving Average)

指数加权平均,也即金融领域常用的移动平均。移动平均法是利用一组最近的实际数据值来预测未来数值的一种常用方法,移动平均法能有效地消除预测中的随机波动。在股市中的10日均线、20日均线等都是用移动平均法得到的。下面给出计算公式:

其中,$\beta$为超参数,经验值为0.9;$\theta_t$为$t$时刻的实际数据;$v_t$为移动平均值。这里直接说结论:当$\beta=0.9$时,得到的是10日平均值;当$\beta=0.98$时,得到的是50日平均值。至于为什么会是这样,因为理论推导也是用的近似,我觉得没什么必要写出来。在深度学习中,我们通常使用默认值$\beta=0.9$,很少会去调整这个参数。

上面的计算公式是迭代公式,如果将其转换成通项公式,则有:

可以看到每个时刻对应一个指数加权项,时间越是久远的项,其$\beta$项次数越高,值越小,说明时间过去越久的值对当前的移动平均值影响越小。

  • 移动平均的偏差修正

当t较小的时候,例如t=1时,因为$v_0=0$,我们有$v_1=0+(1-\beta)\cdot \theta_1$,远远小于合理的值$\theta_1$,这个问题只出现在t较小(如$\beta=0.9$时,就是$t<20$)的时候,当t较大的时候,由于有问题的前面几项对移动平均带来的影响已经可以忽略不计,已经基本没有误差。

3.2小节介绍的动量梯度下降法中,不修复这个偏差也没有问题;不过在3.4小节介绍的Adam中,还是修复了这个偏差。下面介绍如何修复这个t较小时的偏差,只需要将$v_t$的迭代计算公式修改为:

将$v_t$的权重由$\beta$修改为$\frac{\beta}{1-\beta^t}$,这个新的系数在t较小的时候可以增大$v_t$,在t较大的时候收敛于$\beta$。

3.2 动量梯度下降法 Momentum

  • Momentum法将梯度的移动平均作为修改参数的方向,使训练更加稳定、快速。

传统梯度下降法面临的一大问题是梯度方向的变化过于频繁,震荡现象比较明显,如下图所示:

momentum

动量梯度下降法将移动平均法应用到了梯度下降法中。我们将当前时刻的梯度$grads^t$表示为3.1小节提到的真实数据$\theta_t$,Momentum法关注$\theta_t$的变化,计算它的移动平均$v_t$:

然后将$v_t$应用梯度下降法:

在上图中,你会发现这些纵轴上的摆动平均值接近于零,所以在纵轴方向,你希望放慢一点,平均过程中,正负数相互抵消,所以平均值接近于零。但在横轴方向,所有的微分都指向横轴方向,因此横轴方向的平均值仍然较大,因此用算法几次迭代后,你发现动量梯度下降法,最终纵轴方向的摆动变小了,横轴方向运动更快,因此你的算法走了一条更加直接的路径,在抵达最小值的路上减少了摆动。

3.3 RMSprop (Root Mean Square prop)

  • 在我看来,RMSprop可以看作是引入了“自适应学习率”这个概念,在梯度大的地方减小学习率,在梯度小的地方增大学习率。

回忆一下下图这个例子,如果你执行梯度下降,虽然横轴方向正在推进,但纵轴方向会有大幅度摆动,我们想减缓 纵轴方向的学习,同时加快,至少不是减缓横轴方向的学习。

momentum

Momentum法使用移动平均的方法,求梯度的移动平均,将梯度中震荡的维度给中和了;而本节介绍的RMSprop法则是将梯度较大的维度(即上图中的纵向,该方向等高线密集,梯度大)的步长减小,梯度较小的维度(即上图中的横向,该方向等高线稀疏,梯度小)步长增大,从而在纵向上走得少,在横向上走的多,更快且更稳定地到达了极小值。

下面介绍RMSprop具体的计算方法。

首先,跟踪计算梯度大小(也就是梯度的平方)的移动平均值:

这样我们得到了梯度的平方的移动平均值$v_{grads^2}$,我们之前提到要让梯度下降法在梯度较大的方向走得较慢,在梯度较小的地方走得较快,即:

可以看到,上式中的梯度除以了梯度的均方根(root mean square),这也是本方法的名字(RMSprop)的由来。

此外,为了防止梯度较小时的$Division~by~zero$,还会在上式的分母的根号里面加一个极小值$\epsilon=10^{-8}$。

3.4 Adam(Adaptive Moment Estimation)

Adam法是将RMSpropMomentum结合起来的优化算法。

我们看到这两个算法都使用了移动平均的概念,不同在于RMSprop求取的是梯度的平方的移动平均,Momentum求取的是梯度的移动平均。因为两种方法中都用到了符号$\beta$。所以我们做出约定:对Momentum法的参数,我们用$\beta_1$表示;对RMSprop法的参数,我们用$\beta_2$表示。

对每个iteration,Adam都会计算梯度的移动平均梯度平方的移动平均

然后,通过下式更新参数:

可以看到,Adam结合了RMSpropMomentum的特点,与RMSprop相比,它将梯度$grads$换成了梯度的移动平均$v_{grads}$,参数改变的方向更加稳定;与Momentum​相比,它在梯度的移动平均$v_{grads}$下面除以了梯度的“均方根”$\sqrt{v_{grads^2}}$,在平坦的地方自动增大学习率,在陡峭的地方减小学习率。二者相辅相成,所以我一般都使用Adam作为训练的优化算法。

Adam的超参数有:

  • 控制梯度的移动平均的$\beta_1=0.9$,越大则梯度方向的变化越平滑;
  • 控制”自适应学习率“的$\beta_2=0.999$,越小则自适应学习率对当前梯度的大小越敏感,所以为了训练的稳定性不宜设置的太小;
  • 防止”除以0“情况发生的$\epsilon=10^{-8}$。

上述超参数的默认值已经足以应对大多数情况,一般我们不会去改变上述的任何一个默认参数。

3.5 学习率衰减

另一个加速训练的方法是学习率衰减,即一开始采用较大的学习率,随着时间慢慢衰减,注意这里时间的单位是epoch,即对数据集做一次完整的遍历。

下面提供几个常用的学习率衰减公式:

  • $\eta = \frac{1}{1+\gamma\cdot t}\eta_{0}$,其中$\eta_{0}$是初始学习率,$t$是epoch数,$\gamma$是衰减因子,这是我们要设置的超参数。
  • $\eta = 0.95^{t}\cdot \eta_0$,这也叫做指数衰减,学习率呈指数下降,我在一篇论文里看到过,看起来效果应该不错。
  • $\eta=\frac{k}{\sqrt{t}}\cdot\eta_0$
  • 此外还有离散学习率,如前十个epoch学习率是$\eta=10^-2$,之后学习率是$\eta=10^-4$。

3.6 局部最优问题对梯度下降法而言是问题吗?

在深度学习研究早期,人们总是担心优化算法会困在极差的局部最优,不过随着深度学习理论不断发展,我们对局部最优的理解也发生了改变。本小节介绍现在我们怎么看待局部最优以及深度学习中的优化问题。

2d

上图是曾经人们在想到局部最优时脑海里会出现的图,在图中似乎各处都分布着局部最优。梯度下降法或者某个算法可能困在一个局部最优中,而不会抵达全局最优。

这些低维的图曾经影响了我们的理解,但是这些理解并不正确。事实上,如果你要创建一个神经网络,通常梯度为零的点并不是这个图中的局部最优点,而是鞍点,如下图所示:

saddle

很明显上图中的鞍点并不是局部最小值,这是因为虽然在该点的梯度为零,但该点在$w_2$方向上并不是极小值,相反在该点$w_2$方向上的其他点都比它更小。

假设我们现在的网络有20000个参数,即上面这张图片中参数的维度要从二维扩充到20000维。计算梯度时,我们对这两万维求偏导,当导数为零的时候,对该点而言,每一维都有两种可能,即该点在这个维度是极大值还是极小值,只有当该点在这两万维的方向上都是极小值的时候,我们才来到了局部最优值,否则只要有一维是极大值,那么该点就只不过是鞍点而已。所以局部最优解没有这么多,不应成为否定梯度下降法的问题。

如果局部最优不是问题,那么问题是什么?问题是梯度下降法在平稳段会减缓学习,传统的梯度下降法在鞍点处由于梯度为0,甚至会直接停止在那里。

综上,本小节的主旨有两点:

  1. 不用担心模型收敛在极差的局部最优中,只要你是在训练较大的神经网络,存在大量参数,并且成本函数被定义在较高的维度空间;
  2. 损失函数的平稳段是一个问题,传统梯度下降在这些地方学习十分缓慢,这也是像Momentum或是RMSpropAdam这样的算法,能够加速学习算法的地方。在这些情况下,优化算法能够加快速度,让你尽早往下走出平稳段。

4.超参数调试

常见的超参数和它的重要度分级为:

  • 第一档:
    • 学习率$\eta$
  • 第二档
    • Momentum中的$\beta=0.9$
    • 隐藏单元的个数# hidden units
    • batch size
  • 第三档
    • 网络层数# layers
    • 学习率衰减因子$\gamma$
  • 第四档(从来不改)
    • Adam优化器中的$\beta_1,\beta_2,\epsilon$

4.1 用随机数选取调试值

在早一代的机器学习算法中,如果有两个超参数,最常见的做法时画出正交的网格,然后再网格中取样点,例如左下图所示,你可以尝试所有的点,然后选择哪个参数效果最好,当参数的数量相对较少时,这个方法很实用。

random-values

然而在深度学习领域,我们通常使用随机数选择超参数(如右上图所示)。之所以这么做是因为,对于我们要解决的问题而言,很难提前知道哪个超参数最重要。举个例子,假设超参数1是学习率$\eta$,取一个极端的例子,假设超参数2是Adam算法中,分母中的$\epsilon$。在这种情况下,学习率的取值很重要,而$\epsilon$的取值则无关紧要。如果你在网格中取点,接着,你试验了相同学习率的5个取值,那你会发现,无论$\epsilon$取何值,结果基本上都是一样的。我们试验了25次,但试验的学习率却只有5个。对比而言,如果随机取值,我们会试验25个独立的学习率,似乎更有可能发现效果最好的那个。

4.2 由粗糙到精细

此外,我们经常采用由粗糙到精细的策略。在之前的例子中,我们进行了取值,也许会发现效果最好的某个点,而这个点周围的其他一些点效果也很好,那我们接下来要做的是放大这块小区域,然后在其中更密集地随机取值。。

4.3 选择合适的标尺

对学习率,选择范围可能是$[10^{-2},10^{-5}]$,最常见的做法应该是将其取$lg$,转移到$[-2,-5]$的范围随机取点。

对$\beta\in[0.9,0.999]$,可以把$1-\beta$取$lg$,转移到$[-3,-1]$的范围随机取点。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK