超参数调试和Batch正则化
调试处理
神经网络中有很多的超参数,那么如何系统性地组织超参数调试呢?
首先需要明确哪些超参数是更为重要的,一般来说,学习率α是最为重要的,其他的例如mini-batch
,epoch
等。
在深度学习领域,推荐使用的超参数调试方法是:随机选择点。
因为对于要解决的问题而言,很难知道哪个超参数最重要,因此需要进行随机的测试,从而判断哪些参数对最终效果影响最大。
举一个极端的例子是:
存在两个超参数,一个是学习率α,一个是Adam算法中的ε,那么学习率显然很重要,而ε无关紧要。
如果学习率和ε各有五种取值,把所有的取值组合画成网格。
就像这样,那么测试了25组取值后,会发现无论ε取何值,结果基本都是一样的,但进行实验的α只有五个。
另一个指导原则是:采用由粗糙到精细的策略,也就是先大致进行测试,然后在聚焦到取到最优结果的参数值周围,进行更密集的取值进行测试。
为超参数选择合适的范围
在选择超参数的合适范围时,随机取值可以提升搜索效果。
但是随机取值并不是在有效范围内的随机均匀取值。
假设要选择隐藏单元的数量,取值范围是[50, 100]
,那么可以从50开始取,51,52这样,一致取到100。
或者,如果选择神经网络的层数,取值范围是[2, 4]
,那么取2,3,4是合理的。
但是对于例如学习率α,或Momentum算法中的β,这样取值并不适用。
例如对于学习率,取值范围可以是[0.0001, 1]
,那么随着数轴均匀取值,90%的数值都在[0.1, 1]
的范围内,因此并不合理。
这种情况下,可以让学习率以指数的形式取值。
令α=10r,r∈[−4,0],在Python中,可以使用r = -4 * np.random.rand()
。
更为常见的情况是,某个超参数在10a到10b之间取值,那么可以在区间[a,b]
之间随机取值,然后令超参数等于10r。
另外一种情况是,例如给β取值,如果取值范围是[0.9, 0.999]
。那么就可以利用上面的方法,将其转换成β=1−10r。
这样,r的取值方式还是和上面的一样。
归一化网络的激活函数
在逻辑回归中,归一化输入特征可以加快学习过程,计算平均值和方差,从训练集中减去平均值,然后使用方法归一化整个数据集。
把代价函数的等高线图,从扁长的图像变成更圆的图像,从而利于算法优化。
那么对于更深的神经网络呢,不仅有输入特征值x,而且对于每一层来说都有输入a[l]。
那么可以将每一层的输入都进行归一化,从而更有效地训练参数w,b。
这就是Batch归一化。
那么应该归一化a[l]还是z[l],这有一些争论,但是在实践中,经常做的是对z[l]进行归一化。
假设针对神经网络的某一层,有一些隐藏单元值,从z(1)到z(m)(这里省略了层数l,实际应该为z[l](1))。
那么归一化的过程为:
μ=m1i=1∑mz(i)σ2=m1i=1∑m(z(i)−μ)2znorm(i)=σ2+εz(i)−μ
(ε是为了稳定数值,不会出现除以0的情况)
其中znorm(i)就是归一化后的值。
现在已经将z值归一化,平均值为0,方差为1。
但是有时候并不想让隐藏单元的平均值为0,方差为1。
也许有不同的分布会更有意义。
因此可以变成:
z~(i)=γznorm(i)+β
其中γ,β都是要学习的参数,也就是使用梯度下降或其他优化算法时,和权重一样需要更新。
(这里的β和Momentum算法中的超参数没有关系)
这两个参数的作用是:可以随意设置z~(i)的平均值和方差。
事实上,如果γ=σ2+ε,β=μ,那么实际上z~(i)=z(i)。
即,参数β控制平均值,γ控制方差。
将Batch Norm 拟合进神经网络
将Batch归一化拟合到神经网络中。
如上图所示。
在将z输入激活函数,转换成a之前,先讲z归一化,然后使用归一化后的z~[i]输入到激活函数,得到a[1]。
每一层的每一个神经元都有不同的参数γ,β。
需要注意的是:
计算z的方法是z[l]=w[l]a[l−1]+b[l],但是Batch归一化中,z[l]会求平均值,然后再减去这个平均值,因此对于常数b来说,无论值是多少,都会被均值减去所抵消,不影响归一化后的znorm[l],因此可以去掉b[l]这个参数,由β[l]代替控制。
梯度下降的反向传播中,参数γ,β要和权重一同进行更新。
即计算dw[l],db[l](其实b已经去掉了,不需要计算db),dβ[l],dγ[l]。
然后更新参数:
w[l]=w[l]−αdw[l]β[l]=β[l]−αdβ[l]γ[l]=γ[l]−αdγ[l]
如果使用其他优化算法,例如Momentum,RMSprop等,同样把参数γ,β一起更新。
维度说明
加入了Batch归一化后,确认一下各变量的维度。
以神经网络的第l层为例,该层神经元个数为n[l],前一层神经元个数为n[l−1],样本个数为m。
那么各变量维度为:
a[l−1]:(n[l−1],m)
w[l]:(n[l],n[n−1])
z[l],znorm[l]:(n[l],m)
γ[l]:(n[l],1):γznorm[l]中的乘法是对应矩阵位置相乘,不是矩阵乘法,利用Python的广播机制先将γ[l]的维度转换成(n[l],n[l])
β[l]:(n[l],1):与γ类似,先广播,再相加
Batch Norm为什么奏效
没听太懂。
简单来说,Batch归一化减弱了前层参数的作用和后层参数的作用之间的联系,这使得网络每层都可以自己学习,稍微独立于其它层,这有助于加速整个网络的学习。
Batch归一化有轻微的正则化效果。
因为Batch归一化是在每个mini-batch
上进行的,而不是在整个数据集上,因此mini-batch
均值和方差和整个数据集的均值和方差有差距,也就是均值和方差有一些小的噪声。
所以这个dropout
类似,dropout
也是给每个隐藏层增加了噪音,有一定会的概率乘以0。
因此使得后层的神经元不过分依赖任何一个隐藏神经元。
如果想要得到强大的正则化效果,可以将Batch归一化和dropout
一起使用。
Softmax回归
在之前讲过的分类例子中只使用了二分类,即只有两种可能的类别。
如果有多个不同的类别,那么有一种逻辑回归的一般形式,称为Softmax回归。
例如,将输入的图片分为四类:0-其他,1-猫,2-狗,3-鸡。
使用大写的C来表示类别总个数,此时C=4。
在这个例子中,建立一个神经网络,输出层神经元有C个,也就是四个。
那么四个神经元应该计算出某一样本被分类到四个类别的概率,即对于样本输入X,计算概率P(other∣X),P(cat∣X),P(dog∣X),P(chicken∣X)。
并且这四个概率值和为1。
要做到这一点就需要Softmax激活函数,输出层称为Softmax输出层。
对于输出层来说,输出向量为z[l],维度为(4,1)
。
得到z[l]后,应用Softmax激活函数:
t=ez[l]a[l]=∑j=14tiez[l]
其中,t也是一个维度为(4,1)
的向量,对z[l]取指数,相当于对向量z[l]的每一个元素取指数;
然后将向量t的元素求总和,ez[l]除以这个总和,得到a[l]。
相当于对z[l]进行了归一化。
一个例子。
z[l]=⎣⎢⎢⎢⎡52−13⎦⎥⎥⎥⎤t=ez[l]=⎣⎢⎢⎢⎡e5e2e−1e3⎦⎥⎥⎥⎤≈⎣⎢⎢⎢⎡148.47.40.420.1⎦⎥⎥⎥⎤sum=j=1∑4ti=148.4+7.4+0.4+20.1=176.3a[l]=∑j=14tiez[l]=sumez[l]=⎣⎢⎢⎢⎡176.3148.4176.37.4176.30.4176.320.1⎦⎥⎥⎥⎤≈⎣⎢⎢⎢⎡0.8420.0420.0020.114⎦⎥⎥⎥⎤
可以得到,类0的概率为84.2%,类1的概率为4.2%,以此类推。
Softmax激活函数的特殊之处在于:
之前的激活函数例如Sigmoid和Relu都是接受单个数值输入,输入一个实数,输出一个实数。
而Softmax激活函数因为要将所有的可能归一化,所以需要输入一个向量,输出一个向量。
Softmax和Hardmax对应,Hardmax同样接受一个输入向量,然后将元素最大值设为1,其余所有值设为0。
当C=2时,Softmax其实变回了逻辑回归,因为C=2时,Softmax输出两个概率值,和为1,所以其实是冗余的,只需要输出一个值即可,所以其实就是逻辑回归。
训练一个Softmax分类器
以上面的给图片分类为例,C=4。
首先定义单个样本的损失函数。
可以假设一个例子:
某个样本的真实标签是:[0,1,0,0]T,而神经网络的输出是[0.3,0.2,0.1,0.4],所以对这个样本,神经网络表现不佳,实际上是一只猫,但预测为猫的概率是20%。
因此可以给出,单个样本的损失函数:
L(y,y)=−j=1∑Cyjlogyj
以上面给出的例子为例,在这个样本中,y1=y3=y4=0,因此损失函数变成L(y,y)=−y2logy2,而y2=1所以,L(y,y)=−logy2。
这意味着,如果想要减小损失函数值,就要增大y2的值。
概括来讲,损失函数就是找到训练集中的真实类别,然后使该类别所对应的概率值尽可能的高,这样损失函数值就会减小。
整个训练样本的损失J定义为:
J(w[l],b[l],⋯)=m1i=1∑mL(y(i),y(i))
那么如何实现梯度下降法,最主要的是得到dz[l]的表达式,有这个值,就可以连续计算前面所有层的梯度下降。
dz[l]=y−y
有了这个表达式,就可以进行反向传播。