系列文章目录
人工智能—梯度下降的原理和手写实现
文章目录
- 系列文章目录
- 前言
- 一、梯度下降优化器是什么?
- 二、SGD优化方法
- 1.SGD是什么
- 2.SGD的数学原理
- 3.SGD的实现
- 4.SGD的缺陷
- 三、Momentum优化方法
- 1.Momentum是什么
- 2.Momentum的数学原理
- 3.Momentum的实现
- 4.Momentum的优势
- 四、AdaGrad优化方法
- 1.AdaGrad是什么
- 2.AdaGrad的数学原理
- 3.AdaGrad的实现
- 4.AdaGrad的优势
- 五、Adam优化方法
- 1.Adam是什么
- 2.Adam的数学原理
- 3.Adam的实现
- 4.Adam的优势
- 六、汇总对比
- 总结
前言
我们在系列文章 人工智能—梯度下降的原理和手写实现中已经知道了什么是梯度下降,且证明了梯度下降的原理和可行性。但梯度下降中只是指明了梯度是下降的方向,但梯度不是下降的度量。
在 人工智能–机器学习线性回归数学原理及实现中我们采用的是用梯度与引入的步长的概念相结合进行下降指导,其实这就是我们今天要说的优化器的概念了,而步长与梯度的直接结合便是最简单的一个优化——SGD,同时我们今天还会给大家详细介绍Momentum、AdaGrad、Adam等更多的优化器.
一、梯度下降优化器是什么?
对于梯度下降,我们可以理解成站在山谷谷顶,蒙上眼睛寻找谷底的一个过程。将其形象抽象在函数上我们可以理解为,面对复杂的函数曲线/曲面的任意位置,我们在不知道这个函数图像的前提下,摸索出此函数的最小值的一个过程,因为当我们没有画出函数的图像时,在这个函数上摸索的过程也就像我们蒙上眼在山顶找山谷的过程,我们能凭借的只有脚下山坡的一个抖度来猜测是在上山还是在下山。
于是我们能想到的第一个方法就是顺着下坡的方向一小步一小步的挪,而这个一小步的长度其实就是对应我们梯度下降中步长的概念,这个概念在我们之前的章节中已经详细描述过了,所以他不是我们今天的主角,我们今天的任务是怎么能让我们不要太迷茫的摸索,或者说让我们摸索下山的速度变快,路线变平整。这就涉及到梯度下降的优化方法了。
二、SGD优化方法
1.SGD是什么
站在山坡上,我们有360°的方向可以去走,而朝向任意一步我们感受到的坡度都是不一样的,也就是抖度不一样,SGD就是选择这无数个方向中最抖的那个方向,每迈出一步都会重新做一次判断,SGD相信每一次都选择最优的一步,一定能到达那个“最深处”。其实也可以理解为一种贪心的算法。
2.SGD的数学原理
第二个是梯度一个决定试探的长度,一个指导试探的方向。
所以SGD的数学表达式便是:
在梯度和步长的共同作用下,当前的会逐渐的逼近期望值。
3.SGD的实现
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]
4.SGD的缺陷
中进行梯度下降,似乎表现良好,那是因为是均向的,而什么是均向呢?我们来看的梯度图:
所有的梯度不仅指向下降的方向,同时指向最低处,那这种图像我们便称之为均向的。可能有些同学很费解,指向下降的方向不就是指向最低处?那非均向的图像是什么样的呢?我们对进行一个简单的修改可得到:
那他的图像可以相应的画出:
那他的梯度图也可以相应的画出:
这里我们就能很形象的发现,两边的梯度虽然也在下降但他并不是指向最低的位置,但这个函数其实是有最小值的,那就是。那么他的移动轨迹会是什么样呢?因为这个函数是没有其他极值点的,所以用SGD是一定能得走到最低处的,我们可以用python来进行简单的描绘他的运行轨迹。
可以看到他SGD算法在以一种“之”字形的轨迹前进,对于算法自己来说这是不可避免的“最优路径”,但是对于知道函数图像的上帝视角的我们来说,这种走法是非常浪费时间的。
三、Momentum优化方法
1.Momentum是什么
本身的意思是“动量”,其实是一个物理种的概念,描述的是物体运动趋势的变化,其实也很形象的能比喻我们在寻找最低点的过程中来回折返的一个过程。动量是存在于万物之间的,有运动就有动量,他是平滑变化的,但SGD的折返是僵硬的,也可以简单的说是直线的,我们希望能引入自然界的动量来让我们的折返变的更加平滑,从而达到优化的过程。
2.Momentum的数学原理
指的是运动物体的作用效果,表示为物体的质量和速度的乘积,所以我们需要引入新的变量——。于是动量在我们的梯度下降中,用数学的描述方法便为:
3.Momentum的实现
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]
4.Momentum的优势
的意义表示了物体在这个梯度方向上受的力,在这个力的作用下,物体表现出相应的速度,并更新相应的梯度,这个梯度的更新路径就像小球在光滑的碗底滚动一样,和SGD相比,“之”字形的程度大大的降低了。这是因为轴方向的力小,但一直是同一个方向的力,所以会有一定的加速,但方向是在谷底两侧反复变化的,两个方向的力相互抵消,但又不均匀,速度就会变缓,那么“之”字的幅度就会变小,拐角就会变圆。
让我们来看看Momentum的实际效果:
和SGD的对比之下确实达到了我们需要的效果。
四、AdaGrad优化方法
1.AdaGrad是什么
的,那我们还有什么能值得优化的方向吗?想一下我们最基础的模型里还有哪个参数?那就是我们的 学习率。所以AdaGrad就是一种针对的优化方法。
在有关学习率的优化中,其中一种就是学习率衰减。他的思路是一开始训练的时候因为离“山谷”较远,所以迈的步子可以大一点,扩张点说,可以适当的跑一下,而不用担心过头。但随着向山谷的前进后,离山谷越来越近,那步子就要越来越小,谨防越过山谷了。
2.AdaGrad的数学原理
适当的调整学习率,而调整的依据就是我们离山谷还有多远,当然由于我们目前不知道准确的山谷位置,所以我们猜测离山谷的距离肯定不能用
所以我们只能记住以前走过的距离,假设我们是正确的向着谷底走的,那我们走的距离和越大,那就离谷底越近。
这就需要我们又引入一个新的变量来登记我们走过的路程。于是AdaGrad的数学表述为:
中保留了以往全部权重的一个平方和,在更新权重时,步伐大小取决于学习率和。这就意味着在权重的更新中,变动大的权重就会有较大的而较大的就会使变小,也就相当于减小了学习率。
3.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)
4.AdaGrad的优势
让我们来看一下我们的AdaGrad的实际效果:
由图可知,函数的取值高效的向着最小值移动。由于轴方向上的梯度较大,因此第一次的梯度变动较大,但他很快就收到了来自的制约,接下来在上的变动越来越小。
五、Adam优化方法
1.Adam是什么
Adam是相对于AdaGrad提出的,他对于AdaGrad的意义就像Momentum对于SGD的意义一样,为了让僵硬的前者变的更为的平滑。所以你可以简单的理解成Adam是AdaGrad与Momentum的一个结合体。
2.Adam的数学原理
因为我们已经在上面详细的介绍了AdaGrad和Momentum,所以这里就不再赘述直接描述Adam的数学表达形式:
3.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)
4.Adam的优势
让我们来看一下我们的Adam的实际效果:
在AdaGrad中融入了Momentum后,也起到了对AdaGrad摆动幅度和拐角圆滑度的一个极大提高。
六、汇总对比
在今天的教学中,我们较好的实现了4个常用优化器,下面是他们四个的直观比较。
虽然看起来这4个算法一个比一个好,但其实他们各自有自己适应的一个场景以及他们的优势所在,所以不能简单的根据函数寻道的一个轨迹长度来判断优化器的好坏。
当然为了能更好的给大家直观感受几个优化器在不同的场景中的好坏,我这边选取了一个手写数字识别的算法进行代入几个优化器来描述优化器的好坏。
可以看到在这个场景中,AdaGrad的效果是最好的,而不是我们猜测的Adam。
总结
优化器是神经网络训练中非常重要的一个部分,因为网络的训练其实就是一个调整权重的过程,而一个神经网络最多的运行时间都是花在梯度更新上,一个好的优化器和一个较不适应的优化器的选择会直接导致训练精度、时间的极大幅度的变化。
而很可惜的是现在还并不存在一个万能的优化器,也没有选择何种优化器的准确定义,所以对优化器的选择更多的是凭借读者自己的经验和尝试