改善神经网络:超参数调参、正则化以及优化
本文是看完吴恩达老师deeplearning.ai的新课程后,按照所讲知识点,结合各个博文进行的总结梳理。
文章目录
防止过拟合
这一部分参考了博文:(https://www.cnblogs.com/alexanderkun/p/6922428.html)
避免过拟合的方法有很多:early stopping、数据集扩增(Data augmentation)、正则化(Regularization)包括L1、L2(L2 regularization也叫weight decay),dropout。
1. Early stopping
Early stopping 即不等到函数在训练集上收敛,而是使用验证集去评估函数,在验证集准确率下降的时候停止训练。比如下图,每间隔20个epoch,就在验证集评估一下准确率,然后保存一下快照,如果输出性能优于前面的快照,记住最后一次保存快照时候迭代的steps的数量,当到达step的limit次数的时候,restore最后一次胜出的快照。
上图就体现了验证集从最低点上升的epoch数为大约240,于是在这个点停止训练即可。
2. 正则化
L2 regularization
L2正则化就是在代价函数后面再加上一个正则化项:
C
=
C
0
+
λ
2
n
∑
w
w
2
C = C_0 + \dfrac{\lambda}{2n}\sum_w{w^2}
C=C0+2nλw∑w2
C
0
C_0
C0代表原始的代价函数,后面那一项就是L2正则化项,它是这样来的:所有参数w的平方的和,除以训练集的样本大小n。
λ
\lambda
λ就是正则项系数,权衡正则项与
C
0
C_0
C0项的比重。另外还有一个系数1/2,1/2经常会看到,主要是为了后面求导的结果方便,后面那一项求导会产生一个2,与1/2相乘刚好凑整。
对以上公式求导更新w:
w
:
=
(
1
−
α
λ
n
)
w
−
∂
C
0
∂
w
w := (1 - \dfrac{\alpha\lambda}{n})w - \dfrac{\partial C_0}{\partial w}
w:=(1−nαλ)w−∂w∂C0在不使用L2正则化时,求导结果中w前系数为1,现在w前面系数为
(
1
−
α
λ
n
)
(1 - \frac{\alpha\lambda}{n})
(1−nαλ) ,因为
α
\alpha
α、
λ
\lambda
λ、n都是正的,所以
(
1
−
α
λ
n
)
(1 - \frac{\alpha\lambda}{n})
(1−nαλ)小于1,它的效果是减小w,这也就是权重衰减(weight decay)的由来。当然考虑到后面的导数项,w最终的值可能增大也可能减小。
到目前为止,我们只是解释了L2正则化项有让w“变小”的效果,但是还没解释为什么w“变小”可以防止overfitting?一个所谓“显而易见”的解释就是:更小的权值w,从某种意义上说,表示网络的复杂度更低,对数据的拟合刚刚好(这个法则也叫做奥卡姆剃刀),而在实际应用中,也验证了这一点,L2正则化的效果往往好于未经正则化的效果。
当然,对于很多人(包括我)来说,这个解释似乎不那么显而易见,所以这里添加一个稍微数学一点的解释(引自知乎):过拟合的时候,拟合函数的系数往往非常大,为什么?如下图所示,过拟合,就是拟合函数需要顾忌每一个点,最终形成的拟合函数波动很大。在某些很小的区间里,函数值的变化很剧烈。这就意味着函数在某些小区间里的导数值(绝对值)非常大,由于自变量值可大可小,所以只有系数足够大,才能保证导数值很大。
而正则化是通过约束参数的范数使其不要太大,所以可以在一定程度上减少过拟合情况。
L1 regularization
在原始的代价函数后面加上一个L1正则化项,即所有权重w的绝对值的和:
C
=
C
0
+
λ
n
∑
w
∣
w
∣
1
C = C_0 + \dfrac{\lambda}{n}\sum_w{|w|_1}
C=C0+nλw∑∣w∣1对以上公式求导更新w:
w
:
=
(
1
−
α
λ
n
s
i
g
n
(
w
)
)
w
−
∂
C
0
∂
w
w := (1 - \dfrac{\alpha\lambda}{n}sign(w))w - \dfrac{\partial C_0}{\partial w}
w:=(1−nαλsign(w))w−∂w∂C0其中:
s
i
g
n
(
w
)
=
{
1
w
>
0
−
1
w
<
0
0
w
=
0
sign(w) = \begin{cases} 1 & w > 0 \\ -1 & w < 0 \\ 0 & w = 0 \end{cases}
sign(w)=⎩⎪⎨⎪⎧1−10w>0w<0w=0上式比原始的更新规则多了
α
λ
n
s
i
g
n
(
w
)
\frac{\alpha\lambda}{n}sign(w)
nαλsign(w)。当w为正时,更新后的w变小。当w为负时,更新后的w变大——因此它的效果就是让w往0靠,使网络中的权重尽可能为0,也就相当于减小了网络复杂度,防止过拟合。那么参数稀疏有什么好处呢?参考博文:解决过拟合的方式(一):正则化
- 特征选择大家对稀疏规则化趋之若鹜的一个关键原因在于它能实现特征的自动选择。一般来说,xi的大部分元素(也就是特征)都是和最终的输出yi没有关系或者不提供任何信息的,在最小化目标函数的时候考虑xi这些额外的特征,虽然可以获得更小的训练误差,但在预测新的样本时,这些没用的信息反而会被考虑,从而干扰了对正确yi的预测。稀疏规则化算子的引入就是为了完成特征自动选择的光荣使命,它会学习地去掉这些没有信息的特征,也就是把这些特征对应的权重置为0。
- 可解释性 另一个青睐于稀疏的理由是,模型更容易解释。例如患某种病的概率是y,然后我们收集到的数据x是1000维的,也就是我们需要寻找这1000种因素到底是怎么影响患上这种病的概率的。假设我们这个是个回归模型:y=w1x1+w2x2+…+w1000x1000+b(当然了,为了让y限定在[0,1]的范围,一般还得加个Logistic函数)。通过学习,如果最后学习到的w就只有很少的非零元素,例如只有5个非零的wi,那么我们就有理由相信,这些对应的特征在患病分析上面提供的信息是巨大的,决策性的。也就是说,患不患这种病只和这5个因素有关,那医生就好分析多了。但如果1000个wi都非0,医生面对这1000种因素,累觉不爱。
3. Dropout
L1、L2正则化是通过修改代价函数来实现的,而Dropout则是通过修改神经网络本身来实现的,它是在训练网络时用的一种技巧(trike)。它的流程如下:
假设我们要训练上图这个网络,在训练开始时,我们随机地“删除”一半的隐层单元,视它们为不存在,得到如下的网络:
![](https://i-blog.csdnimg.cn/blog_migrate/537ad6a36848e74331501f0e06747371.png)
梯度消失与梯度爆炸
吴恩达老师在课中对于梯度消失和梯度爆炸给出了一个非常直观的解释:假设
w
[
l
]
w^{[l]}
w[l]是第
l
l
l层的权重值,那么可近似得到:
y
^
=
w
[
l
]
w
[
l
−
1
]
w
[
l
−
2
]
.
.
.
.
.
.
w
[
2
]
w
[
1
]
w
[
0
]
x
\hat{y} = w^{[l]}w^{[l-1]}w^{[l-2]}......w^{[2]}w^{[1]}w^{[0]}x
y^=w[l]w[l−1]w[l−2]......w[2]w[1]w[0]x当
w
[
l
]
w^{[l]}
w[l] < I 时,随着网络加深
y
^
\hat{y}
y^ 的值将指数性的减少,造成了梯度消失;
当
w
[
l
]
w^{[l]}
w[l] > I 时,随着网络加深
y
^
\hat{y}
y^ 的值将指数性的增加,造成了梯度爆炸。
解决方法
1. 权重初始化
权重初始化时不能全部初始化为0,由于对称性,如果都初始化为0,反向传播时每个神经元将以同样的函数进行更新权重,导致每个神经元的运算都相同,神经网络每层的多个神经元就失去了意义。
Python初始化权重的代码如下:
w[l] = np.random.randn(shape) * np.sqrt(var)
其中,shape是第
l
l
l层权重矩阵的大小,var是想要初始化的方差值,根据所选激活函数进行改变:若激活函数是
1
n
\frac{1}{n}
n1,var =
1
n
[
l
−
1
]
\frac{1}{n^{[l-1]}}
n[l−1]1;
若激活函数是relu函数,var =
2
n
[
l
−
1
]
\frac{2}{n^{[l-1]}}
n[l−1]2;若激活函数是tanh函数,var =
1
n
[
l
−
1
]
\frac{1}{n^{[l-1]}}
n[l−1]1 or var =
2
n
[
l
−
1
]
∗
n
[
l
]
\frac{2}{n^{[l-1]}*n^{[l]}}
n[l−1]∗n[l]2
这里的var值也是可以作为一个超参数进行调节的,但是效果不会非常明显。
2. Batch-Normalization
参考博文:【深度学习】深入理解Batch Normalization批标准化
机器学习领域有个很重要的假设:IID独立同分布假设,就是假设训练数据和测试数据是满足相同分布的,这是通过训练数据获得的模型能够在测试集获得好的效果的一个基本保障。那BatchNorm的作用是什么呢?BatchNorm就是在深度神经网络训练过程中使得每一层神经网络的输入保持相同分布的。
“Internal Covariate Shift” 问题
“Covariate Shift” 概念:如果ML系统实例集合<X,Y>中的输入值X的分布老是变,这不符合IID假设,网络模型很难稳定的学规律。对于深度学习这种包含很多隐层的网络结构,在训练过程中,因为各层参数不停在变化,所以每个隐层都会面临covariate shift的问题,也就是在训练过程中,隐层的输入分布老是变来变去,这就是所谓的“Internal Covariate Shift”,Internal指的是深层网络的隐层,是发生在网络内部的事情,而不是covariate shift问题只发生在输入层。
BatchNorm的本质思想
接着就提出了BatchNorm的基本思想:**能不能让每个隐层节点的激活输入分布固定下来呢?**深层神经网络在做非线性变换前的激活输入值(就是那个x=WU+B,U是输入)随着网络深度加深或者在训练过程中,其分布逐渐发生偏移或者变动,之所以训练收敛慢,一般是整体分布逐渐往非线性函数的取值区间的上下限两端靠近(对于Sigmoid函数来说,意味着激活输入值WU+B是大的负值或正值),所以这导致反向传播时低层神经网络的梯度消失,这是训练深层神经网络收敛越来越慢的本质原因。如下图sigmoid函数所示,激活输入值为比较大的正值或负值时梯度接近为0。
![](https://i-blog.csdnimg.cn/blog_migrate/7acedca1b9250bb54ab26f5f67067caa.png)
训练阶段如何做BatchNorm
看到上文的BatchNorm实现方法以后相信很多人包括我当时刚刚接触BatchNorm时都会提出一个疑问:**如果都通过BN,那么不就跟把非线性函数替换成线性函数效果相同了?**这意味着什么?我们知道,如果是多层的线性函数变换其实这个深层是没有意义的,因为多层线性网络跟一层线性网络是等价的。这意味着网络的表达能力下降了,这也意味着深度的意义就没有了。所以BN为了保证非线性的获得,对变换后的满足均值为0方差为1的x又进行了scale加上shift操作(y=scale*x+shift),每个神经元增加了两个参数scale和shift参数,这两个参数是通过训练学习到的,意思是通过scale和shift把这个值从标准正态分布左移或者右移一点并长胖一点或者变瘦一点,每个实例挪动的程度不一样,这样等价于非线性函数的值从正中心周围的线性区往非线性区动了动。核心思想应该是想找到一个线性和非线性的较好平衡点,既能享受非线性的较强表达能力的好处,又避免太靠非线性区两头使得网络收敛速度太慢。
BN操作流程
![](https://i-blog.csdnimg.cn/blog_migrate/c3b507d0c61f7e954901ea26f2e3f5ee.png)
x
^
(
k
)
=
x
(
k
)
−
E
[
x
(
k
)
]
V
a
r
[
x
(
k
)
]
\hat{x}^{(k)} = \dfrac{x^{(k)} - E[x^{(k)}]}{\sqrt{Var[x^{(k)}]}}
x^(k)=Var[x(k)]x(k)−E[x(k)]这里
x
(
k
)
x^{(k)}
x(k)是指t-1层经过激活函数之后的输出。
经过这个变换后某个神经元的激活x形成了均值为0,方差为1的正态分布,目的是把值往后续要进行的非线性变换的线性区拉动,增大导数值,增强反向传播信息流动性,加快训练收敛速度。但是这样会导致网络表达能力下降,为了防止这一点,每个神经元增加两个调节参数(scale和shift),这两个参数是通过训练来学习到的,用来对变换后的激活反变换,使得网络表达能力增强,即对变换后的激活进行如下的scale和shift操作,这其实是变换的反操作:
y
(
k
)
=
γ
(
k
)
x
^
(
k
)
+
β
(
k
)
y^{(k)} = \gamma^{(k)}\hat{x}^{(k)} + \beta^{(k)}
y(k)=γ(k)x^(k)+β(k)
这里的
γ
(
k
)
\gamma^{(k)}
γ(k)即scale,
β
(
k
)
\beta^{(k)}
β(k)即为shift。
BatchNorm的好处
一、不仅仅极大提升了训练速度,收敛过程大大加快;二、还能增加分类效果,一种解释是这是类似于Dropout的一种防止过拟合的正则化表达方式,所以不用Dropout也能达到相当的效果;
三、另外调参过程也简单多了,对于初始化要求没那么高,而且可以使用大的学习率等。
优化方法
课上主要讲了Momentum, Adam和RMSprop三个优化算法,详细比较了与Gradient Descent 的区别以及推导过程。这里图和推导过程有点多,偷个懒直接转了红石头哥的博客:优化算法
超参数调节
超参数重要性
上文已经介绍了许多需要调节的超参数,包括学习率等数值、还有优化方法的选择等,那么如何选择这些超参数呢?吴恩达老师对各个超参数的重要性进行了一个排序:
- 学习率 α \alpha α;
- Momentum的加权指数 β \beta β(大多数情况选择0.9);隐藏节点数;mini-batch size;
- 隐藏层数量;学习率衰减。
- Adam的参数 β 1 ( 0.9 ) \beta_1(0.9) β1(0.9), β 2 ( 0.99 ) \beta_2(0.99) β2(0.99), ε ( 1 0 − 8 ) \varepsilon(10^{-8}) ε(10−8) 一般就使用上面这些默认值,不需要调整。
具体参数调节
搜索参数组合
最初在机器学习行业进行参数选择的时候,往往用左图的方式去寻找最合适的参数,但是很多情况下,如上图中两个超参数并不知道哪一个更加重要,很有可能发生的一种情况是:超参数1对结果几乎没影响而超参数2对结果影响非常大。这种情况下若采用网格搜索,则使得所取的25个组合点事实上只有5个不同的超参数2值有价值,这点在参数数量变多后就表现的更为明显。
此时让我们再来看看右图,采用随机采样的方式后,同样是选择25个采样点,单单对于超参数2的取值就远远超过了5个,可以探究更多潜在的超参数的关系。
上图则讲了一种由粗到细的随机参数选择方法,先在比较广泛的范围中选择参数对模型进行评估,当发现其中某些点比较好以后,放大那一部分的参数再进行细致的选择。
数值参数取样
对于数值超参数进行选择的时候,往往是通过在一个区间内进行均匀采样的形式,但是有时候这种采样方式并不合理,比如下图是对学习率
α
\alpha
α进行均匀采样,可以看到90%的采样区间都在0.1-1中,而0.0001-0.1中只采样了10%的数据:
这时候,我们可以引入对数分布的采样方式,如下图所示:
#Python代码
r = -4 * np.random.rand() # r∈[-4,0]
alpha = 10 ** r
再举一个例子来说明一下为什么要这样进行采样。
对momentum的加权指数平均值
β
\beta
β进行调整的时候,取值范围往往在0.9-0.999之间。
当
β
\beta
β取值从
0.9
→
0.9005
0.9\rightarrow0.9005
0.9→0.9005时,
1
1
−
β
≈
10
\frac{1}{1-\beta}\approx10
1−β1≈10;
当
β
\beta
β取值从
0.999
→
0.9995
0.999\rightarrow0.9995
0.999→0.9995时,
1
1
−
β
=
1000
→
2000
\frac{1}{1-\beta}=1000\rightarrow2000
1−β1=1000→2000;
可以看出来,
β
\beta
β值同样是变化了0.0005,对于模型的影响差距完全不同,所以若采用均匀采样则效果不好,采用对数的方式取样则能在
β
\beta
β更接近1时进行更加密集的取样。
模型训练方式
最后吴恩达老师介绍了两种模型训练方式,他形象的将其比喻为熊猫模式和鱼子酱模式。
熊猫模式则是一种babysitting一个模型的方式,就如同熊猫一次只生一胎一样,这种训练方式一次只训练一个模型,一直观察这个模型的训练状况,然后随着训练状况对其中的参数进行修改,直到最后模型收敛。这样循环训练两到三个模型最后比较其好坏。
鱼子酱模式则是同时对多个模型同时进行训练,如同鱼一次产大量的卵一样,这种训练方式会对许多不同模型调好初始超参数以后就不再对其进行调整,而是等到模型都收敛后,从中选出最好的模型。