鱼书学习笔记:参数优化
神经网络的学习的目的是找到使用使损失函数的值尽可能小的参数。这是寻找最优参数的问题,解决这个问题的过程称为最优化(optimization)。遗憾的是,神经网络的最优化问题非常难。这是因为参数空间非常复杂,无法轻易找到最优解(无法使用那种通过解数学式一下子就求得最小值的方法)。而且,在深度神经网络中,参数的数量非常庞大,导致最优化问题更加复杂。
在之前,为了找到最优参数,鱼书使用参数的梯度(导数),沿梯度方向更新参数,并重复这个步骤多次,从而逐渐靠近最优参数,这个过程称为随机梯度下降法。
随机梯度下降法(SGD)
class SGD: """随机梯度下降法(Stochastic Gradient Descent)""" def __init__(self, lr=0.01): self.lr = lr def update(self, params, grads): for key in params.keys(): params[key] -= self.lr * grads[key]
lr表示学习率,这个学习率会保存为实例变量(一般事先定义为0.01或0.001)。参数params和grads是字典变量,按params['W1'],grads['W1']的形式,分别保存了权重参数和它们的梯度。
SGD缺点:如果函数的形状非均向(anisotropic),比如呈延伸状,搜索的路径就会非常低效。SGD低效的根本原因是,梯度的方向并没有指向最小值的方向
Momentum
class Momentum: """Momentum SGD""" def __init__(self, lr=0.01, momentum=0.9): self.lr = lr self.momentum = momentum self.v = None def update(self, params, grads): if self.v is None: self.v = {} for key, val in params.items(): self.v[key] = np.zeros_like(val) for key in params.keys(): self.v[key] = self.momentum*self.v[key] - self.lr*grads[key] params[key] += self.v[key]
momentum*v这一项承担使物体逐渐减速的任务,对应物理上的地面摩擦或空气阻力。实例变量v会保存物体的速度。初始化时,v中什么都不保存,但当第一次调用update()时,v会以字典型变量的形式保存与参数结构相同的数据。
和SGD相比,Momentum添加了一个阻力变量,使得梯度收敛于一个固定的方向,从而稳定了参数更新的过程
AdaGrad
在神经网络的学习中,学习率的值很重要。学习率过小,会导致学习花费过多时间;反过来,学习率过大,则会导致学习发散而不能正确进行。
在关于学习率的有效技巧中,有一种被称为学习率衰减(learning rate decay)的方法,即随着学习的进行,使学习率逐渐减小。
逐渐减小学习率的想法,相当于将“全体”参数的学习率值一起降低。而AdaGrade[1]进一步发展了这个想法,针对“一个一个”的参数,赋予其“定制”的值
class AdaGrad: """AdaGrad""" def __init__(self, lr=0.01): self.lr = lr self.h = None def update(self, params, grads): if self.h is None: self.h = {} for key, val in params.items(): self.h[key] = np.zeros_like(val) for key in params.keys(): self.h[key] += grads[key] * grads[key] params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)
这里新出现了变量h,它保存了以前的所有梯度值的平方和。然后,在更新参数时,通过乘以1/sqrt(h),就可以调整学习的尺度。这意味着,参数的元素中变动较大(被大幅更新)的元素的学习率将变小。也就是说,可以按参数的元素进行学习率衰减,使变动大的参数的学习率逐渐减小。
AdaGrad会记录过去所有梯度的平方和。因此,学习越深入,更新的幅度就越小。实际上,如果无止境地学习,更新量就会变为0,完全不再更新。为了改善这个问题,可以使用RMSPro[2]方法。
RMSPro
class RMSprop: """RMSprop""" def __init__(self, lr=0.01, decay_rate = 0.99): self.lr = lr self.decay_rate = decay_rate self.h = None def update(self, params, grads): if self.h is None: self.h = {} for key, val in params.items(): self.h[key] = np.zeros_like(val) for key in params.keys(): self.h[key] *= self.decay_rate self.h[key] += (1 - self.decay_rate) * grads[key] * grads[key] params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)
RMSPro方法并不是将过去所有的梯度一视同仁地相加,而是逐渐地遗忘过去的梯度,在做加法运算时将新梯度的信息更多地反映出来。这种操作从专业上讲,称为“指数移动平均”。
Adam
Momentum参照小球在碗中滚动的物理规则进行移动,AdaGrad为参数的每个元素适当地调整更新步伐。如果将这两个方法融合在一起会怎么样呢?,这就是Adam[3]方法的基本思路。
Adam是2015年提出的新方法。它的理论有些复杂,直观地讲,就是融合了Momentum和AdaGrad的方法。通过组合前面两个方法的优点,有望实现参数空间的高效搜索。此外,进行超参数的“偏置校正”也是Adam的特征。
class Adam: """Adam (http://arxiv.org/abs/1412.6980v8)""" def __init__(self, lr=0.001, beta1=0.9, beta2=0.999): self.lr = lr self.beta1 = beta1 self.beta2 = beta2 self.iter = 0 self.m = None self.v = None def update(self, params, grads): if self.m is None: self.m, self.v = {}, {} for key, val in params.items(): self.m[key] = np.zeros_like(val) self.v[key] = np.zeros_like(val) self.iter += 1 lr_t = self.lr * np.sqrt(1.0 - self.beta2**self.iter) / (1.0 - self.beta1**self.iter) for key in params.keys(): #self.m[key] = self.beta1*self.m[key] + (1-self.beta1)*grads[key] #self.v[key] = self.beta2*self.v[key] + (1-self.beta2)*(grads[key]**2) self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key]) self.v[key] += (1 - self.beta2) * (grads[key]**2 - self.v[key]) params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7) #unbias_m += (1 - self.beta1) * (grads[key] - self.m[key]) # correct bias #unbisa_b += (1 - self.beta2) * (grads[key]*grads[key] - self.v[key]) # correct bias #params[key] += self.lr * unbias_m / (np.sqrt(unbisa_b) + 1e-7)
参考资料:
[1]Adaptive Subgradient Methods forOnline Learning and Stochastic Optimization
https://www.jmlr.org/papers/volume12/duchi11a/duchi11a.pdf
[2]Neural Networks for Machine Learning:divide the gradient by a running average of its recent magnitude
http://www.cs.toronto.edu/~hinton/coursera/lecture6/lec6.pdf
[3]Adam: A Method for Stochastic Optimization
https://arxiv.org/abs/1412.6980