神经网络学习的目的是找到使损失函数的值尽可能小的参数。这是寻找最优参数的问题,解决这个问题的过程称为最优化。而在深度神经网络中,参数的数量非常庞大,最优化问题也就十分复杂。
之前我们学过随机梯度下降法(SGD)来寻找最优参数,大致思路是:将参数的梯度作为线索,沿梯度方向更新参数,并重复步骤多次,从而逐渐靠近最优参数。
在随机梯度下降法中有两种求梯度的方式:
- 利用数值微分求梯度(简单、速度慢)
- 利用误差反向传播法求梯度(复杂、速度快)
将SGD实现为一个Python类:
class SGD:
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]
虽然SGD简单,但在解决某些问题时,也会显得十分低效。低效的根本原因是:梯度的方向并没有指向最小值的方法。
为了避免SGD的缺点,还有Momentum、AdaGrad、Adam这三种方法可以取代SGD。。
Momentum
Momentum是“动量”的意思,和物理有关。用Python表示如下:
class Momentum:
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]
AdaGrad
在神经网络的学习中,学习率的值lr
很重要,学习率过小,会导致学习花费过多时间;反过来,学习率过大会导致学习发散而不能正确进行。
在关于学习率的有效技巧中,有一种被称为学习率衰减的方法,即随着学习的进行,学习率不断减小。AdaGrad正是基于这种思想,AdaGrad会为参数的每一个元素适当的调整学习率。
class 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) # 加上微小值1e-7,防止除数为零
Adam
Adam融合了Momentum和AdaGrad两个方法的优点,有望实现参数空间的高效搜索。此外,进行超参数的“偏置校正”也是Adam的特征。
class Adam:
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] += (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)
对于SGD、Momentum、AdaGrad和Adam这四种更新参数的方式。它们各自的最优化的更新路径如下图:
其中SGD最简单,使用也最广泛。Adam的综合性能最好,最受欢迎。
我们以MNIST手写数字集为例,比较SGD、Momentum、AdaGrad和Adam这四种方法在学习进展上有多大程度的差异:如上图所示,SGD的学习进度最慢,损失函数的值最大;Momentum、AdaGrad和Adam这三种方法的学习进度较快,其中AdaGrad的学习效果最好。
每天学习一点点,每天进步一点点。
网友评论