机器学习中,绝大部分模型没有解析解,需要采用梯度下降法求解最有参数,各种各样的梯度下降法都会遇到一个问题,就是如何设置学习率,是一个技术活,更是一个运气活。
超参数调优,经常会遇到两个问题:
1、 模型发散,参数随着迭代数值绝对值越来越大,甚至发散到无穷,从损失函数来看,误差也会越来越大。
2、 震荡,从损失函数来看,误差出现震荡,模型在局部最优解附近徘徊。
假定损失函数为 Q(β)>0 Q ( β ) > 0 ,通常梯度下降法可表示为
几乎所有损失函数都满足这一条件,比如最简单的 Q(θ)=aθ2,a>0,∀θ≠0 Q ( θ ) = a θ 2 , a > 0 , ∀ θ ≠ 0 ,有 c=2a c = 2 a 。
有:
改变损失函数本身性质,必须改变损失函数,即便这样做也很难保证不出现参数爆炸的情况。剩下的就是改变超参数 λ λ .
任何时候,当你设定的学习率导致参数发散的时候,简单的办法就是降低学习率 λ λ ,破坏条件 λc>2 λ c > 2 从而避免参数发散。
不同于参数发散,损失函数值震荡,主要是因为在局部最优解附近学习率过大,导致迭代无法收敛到局部最优值。
假定 β∗ β ∗ 是局部最优值,在局部最优值附近有 β=tα+β∗ β = t α + β ∗ ,简单起见,不防设 ϕα=∇2Q(β∗)α ϕ α = ∇ 2 Q ( β ∗ ) α ,即恰好 α α 是损失函数在局部最优解附近的海塞矩阵的特征方向。
则得到过程可以表示为
复杂模型,还会遇到梯度爆炸和梯度消失的问题,梯度消失则表明模型已经训练到达一个局部最优解。而梯度爆炸的问题是训练过程中更为麻烦的问题,它会迫使搜索脱离当前区域。
样本数量改变经常导致损失函数变化,因此在小样本上训练收敛的参数,在大样本下可能仍然不收敛,这导致工程应用中有麻烦,没法设定固定的学习率。
下面以简单回归模型为例,说明学习率参数对机器学习收敛的影响。一般而言学习率由大到小,先导致发散,缩小学习率后误差震荡,继续缩小则训练过程收敛。 跟混沌理论差不多,导致震荡学习率有一个临界区间。同时学习率并不是越小越好,缩小学习率需要更多的迭代次数才能。
给定模型 y=5x1+6x2+abx3 y = 5 x 1 + 6 x 2 + a b x 3 ,产生随机样本:
Num=1000
set.seed(1000)
x1 = runif(n = Num,min = -10,max = 3)
x2 = runif(n = Num,min = -5,max = 5)
x3 = runif(n = Num,min = -5,max = 10)
X=cbind(x1,x2,x3)
y=5*x1+6*x2+5*6*x3
sd(y)
# 添加随机误差
y=y+rnorm(Num,mean = 0,sd = 1)
模型的损失函数为: Q(a,b)=12∑ni=1(yi−axi1−bxi2−abxi3)2 Q ( a , b ) = 1 2 ∑ i = 1 n ( y i − a x i 1 − b x i 2 − a b x i 3 ) 2 ,
梯度为:
olm<-function(y,x,lambda=0.02,iter=100,beta=runif(n = 2,min = 0.01,max = 0.2)){
stopifnot(length(y)==dim(x)[1])
stopifnot(3==dim(x)[2])
xy=c(t(y)%*%x)
xx=t(x)%*%x
#为了观察内部过程,需要将每一步迭代后的参数保留下来
betas=data.frame(a=rep(0,iter+1),b=rep(0,iter+1))
betas[1,]=beta
for(i in 1:iter){
b0=c(beta,beta[1]*beta[2])
b1=matrix(data=c(1,0,0,1,beta[2:1]),nrow = 2)
#browser()
beta=beta +lambda*b1%*%(xy-xx %*%b0)
betas[i+1,]=beta
}
betas
}
#betas=olm(y,X,lambda = 0.00056,iter = 1000)
计算每一步迭代后个损失函数值:
betas=olm(y,X,lambda =0.0000028,iter = 500)
beta2=betas
beta2[,3]=beta2[,1]*beta2[,2]
err=0.5*colSums((y-X%*%t(beta2))^2)/Num
plot(err,type = "l")
plot(betas,type="b")
从上图可以看出,此参数使得训练在若干步后发散,损失值突然发散到无穷大,训练模型参数由于最后太大,导致之前的模型参数被压缩在一个极小的区域,从而在图上显示为一个点。
重新调整初始化参数,缩小学习率参数,同时加大迭代次数,可以从下图中看到,这一次明显有误差震荡。
betas1=olm(y,X,lambda = 0.000002,iter = 1000)
beta2=betas1
beta2[,3]=beta2[,1]*beta2[,2]
err1=0.5*colSums((y-X%*%t(beta2))^2)/Num
plot(err1,type = "l")
plot(betas1,type="b")
上面的参数,使得参数在最优值附近跳转,在损失函数值上看来,表现为震荡。迭代参数基本在一条直线上,这并不是偶然的,这条直线会逐步逼近损失函数在改局部最小值的Hassan矩阵的特征方向,一般而言是有最大特征值的特征方向。来看最后50次迭代情况。
plot(betas1[950:1000,],type="b")
继续缩小学习率。
betas1=olm(y,X,lambda =0.0000014,iter = 1000)
beta2=betas1
beta2[,3]=beta2[,1]*beta2[,2]
err1=0.5*colSums((y-X%*%t(beta2))^2)/Num
plot(err1[-c(1:2)],type = "l")
plot(betas1,type="b")
plot(betas1[950:1000,],type="o")
这时,训练误差迅速逼近最小值,从模型参数迭代过程来看,参数迅速靠近(5,6)附近从最后一图来看,模型参数值移动范围越来越小。
不管是参数发散还是震荡,归根揭底都是由于给定学习率后,跟梯度模长有关系,实际上在梯度越大的地方,损失函数变化越大,这时我们恰好需要更小的学习率以免模型参数下一步跑过最小值。事实上梯度下降法的关键是梯度方向,而不是梯度的模长,而模长依赖于样本数据,尤其当样本数量变化时,对梯度影响更加明显,更多的样本量意味着需要更小的参数,而机器学习需要的样本越多越好。
随机梯度下降法可以控制每次参与,但每次参与计算样本数量仍然对学习率参数有显著的影响。比较更好的办法是控制梯度模长,因为对于模型训练结果而言,尽需要保证每次迭代的梯度方向大致正确。
比随机梯度更彻底的办法是,是控制梯度模长。保障 V=t∇Q(β)|β=βold;βnew=βold−λV V = t ∇ Q ( β ) | β = β o l d ; β n e w = β o l d − λ V 迭代参数仍然可以做到收敛。若能任何时候保障 |V|<M | V | < M ,有