目录
- 什么是梯度下降法?
- 运用梯度下降算法所面临的的挑战
- 梯度下降法的变式
- 梯度下降的代码实现
- 不同梯度下降算法的选择
1. 什么是梯度下降?
我会以经典的登山案例来解释梯度下降的含义。
假设你现在在山顶处,必须抵达山脚下(也就是山谷最低处)的湖泊。但让人头疼的是,你的双眼被蒙上了无法辨别前进方向。那么,你会采取什么办法抵达湖泊处呢?
最好的办法就是查看一下周围的地势,观察有下降趋势的地面,这会帮助你迈出第一步。如果沿着下降的路线前进,那么你非常有可能到达湖泊。
以图形的形式呈现该处地势。注意下面的曲线图:
为了学习梯度下降算法,假设我们需要找出最佳的参数(θ1)和(θ2)。与上述做法相似,在测绘“成本空间”时,我们需要找到相似的山脉和山谷。成本空间是指为参数选定了一个特殊值后,算法的运行情况。
所以,在Y轴上,我们让J(θ1)与X轴上的参数(θ1)以及Z轴上的参数(θ2)分别相交。在这里,数值高的红色区域代表山峰,数值低的蓝色区域代表山谷。
梯度下降算法的分类有多种,主要分为两种:
A. 基于数据的获取
1. 全批梯度下降算法
2. 随机梯度下降算法
在全批梯度下降算法中,需要利用全部数据同时计算梯度;然而在随机梯度下降算法中,通常只需选取其中一个样例来计算梯度。
B. 基于微分技术
1. 一阶微分
2. 二阶微分
梯度下降需要通过成本函数微分来计算梯度。我们可以用一阶微分技术或者二阶微分技术来计算。
2. 运用梯度下降算法所面临的的挑战
在很多情况下,梯度下降无法正常工作,甚至不工作。原因有三点:
- 数据挑战
- 梯度挑战
- 执行挑战
2.1 数据挑战
A. 如果数据按照某种方式进行组合形成了一个非凸的优化问题,那么就很难利用梯度下降算法对其进行优化了。梯度下降算法只能解决那些意义非常明确的凸优化问题。
凸函数与凸优化问题相关的介绍,详见该博文 机器学习中的凸优化问题
B. 在优化凸问题时,可能会出现无数的极小点。最低点被称为全局最小值,其它的点被称为局部极小值。我们的目的是要达到全局极小值,而非局部极小值。
事实上,当我们选择不同的起始点时,有可能达到不同的极小值。如下图,选择的起始点不同,最终走向了不同的极小值。
C. 还有一个问题就是鞍点。梯度为零时,它是数据中的一个点,但是不是最优点。目前,我们还没有特定的方法来规避鞍点的出现,是一个新的研究领域。
2.2 梯度挑战
如果执行梯度下降算法时出现了错误,那么可能会导致诸如梯度消失或者梯度崩溃等的问题。当梯度太小或者太大时,就会出现这样的问题。也正因为这些问题,算法无法收敛。
2.3 执行挑战
A. 通常情况下,大多数神经网络的开发者不会留意执行情况,但是观察网络资源利用率是非常重要的。比如,在执行梯度下降算法时,了解需要多少资源是非常重要的。如果应用程序的储存器太小,那么网络就会失败。
B. 跟踪诸如浮点数的注意事项以及软/硬件的先决条件,也非常重要。
3. 梯度下降法的变式
3.1 普通的梯度下降
这是梯度下降技术中最简单的形式。它的主要特性就是,使我们向着成本函数梯度的最小值又迈进了一小步。
我们来看一下它的伪代码。
update = learning_rate * gradient_of_parameters
parameters = parameters - update
在全批梯度下降法中,gradient_of_parameters是所有样本点的梯度之和。而在随机梯度下降法中,gradient_of_parameters是当前样本点的梯度
learning_rate理解为步长,是一个常量
3.2 动量梯度下降法
普通的梯度下降法有一个问题:
从某一点开始的梯度下降过程是及其曲折的。并不是直接走向中心点,而是需要浪费很多时间折来折去,这样的速度就会变慢,怎么样解决这个问题呢?
动量梯度下降法考虑了前一个下降的方向,因此下降的路径不会大幅度地改变,会比较直。从而能够比普通梯度下降更快速地收敛。
伪代码
update = momentum * previous_update + (1 - momentum) * gradient_of_parameters
parameters = parameters - learning_rate * update这里momentum可以理解为权值
3.3 ADAGRAD算法
ADAGRAD算法使用了自适应技术来更新学习率,即步长。在这种算法中,我们会根据前期所有更迭的梯度变化情况,改变学习率。
这是一组伪代码。
grad_component = previous_grad_component + (gradient * gradient)
rate_change = square_root(grad_component) + epsilon
adapted_learning_rate = learning_rate * rate_change
update = adapted_learning_rate * gradient
parameter = parameter – update
在上述代码中,epsilon 是一个用于抑制学习率产生变动率的常量。
3.4 ADAM算法
ADAM 算法是一种以 adagrad 算法为基础并且能进一步减少其缺点的更加自适应的技术。也就是说,你可以认为 ADAM 算法是动量和 ADAGRAD 算法的综合体。
这是一组伪代码。
adapted_gradient = previous_gradient + ((gradient – previous_gradient) * (1 – beta1))
gradient_component = (gradient_change – previous_learning_rate)
adapted_learning_rate = previous_learning_rate + (gradient_component * (1 – beta2))
update = adapted_learning_rate * adapted_gradient
parameter = parameter – update
上述代码中的 beta1 和 beta2 是用来保持梯度和学习率不变的常量。
与此同时,还存在如 l-BFGS 等这样的二阶微分算法。你可以在 scipy 数据库中看到这种算法的执行情况。
4. 梯度下降法的代码实现
这是定义普通梯度下降算法的主代码:
params = [weights_hidden, weights_output, bias_hidden, bias_output]
def sgd(cost, params, lr=0.05):
grads = T.grad(cost=cost, wrt=params)
updates = []for p, g in zip(params, grads):
updates.append([p, p - g * lr])
return updatesupdates = sgd(cost, params)
为了能更好的理解上述代码,接下来我们会分成不同的步骤详细讲解。
我们把 sgd 这个含有参数的函数分别定义为 cost、params 和 lr,分别代表上述例子中的 J(θ),θ是深度学习算法和学习率的参数。我们将默认的学习率设为0.05,但是学习率可以随着我们的喜好轻易地发生改变。
def sgd(cost, params, lr=0.05):
然后,我们定义关于这个成本函数的梯度参数。在这里,我们利用 theano 数据库来寻找梯度,T是我们将导入的 theano 数据:
grads = T.grad(cost=cost, wrt=params)
最后,通过所有参数的迭代找出所有可能需要更新的参数。大家可以看到,在这里我们使用的是普通梯度下降算法。
for p, g in zip(params, grads):
updates.append([p, p - g * lr]
5. 不同梯度下降法的选择
对于上述提到的各种梯度下降算法,各有利弊。接下来,我会介绍一些能够帮助大家找到正确算法的实用方法。
- 如果是为了快速地获得原型,那就选取诸如Adam/Adagrad这样的自适应技术,这会让我们事半功倍,并且无须大量调优超参数。
- 如果是为了得到最好的结果,那就选取普通的梯度下降算法或者动量梯度下降算法。虽然利用梯度下降算法达到预期效果的过程很缓慢,但是大部分的结果比自适应技术要好得多。
- 如果你的数据偏小而且能够适应一次迭代,那么就可以选择诸如 l-BFGS这样的二阶技术。这是因为,二阶技术虽然速度非常快并且非常准确,但是只适用于数据偏小的情况。
- 还有一种是利用学习特性来预测梯度下降学习率的新兴方法(虽然我还没有尝试过这种新兴方法,但是看起来前途无量)。可以仔细地阅读一下这篇文章。
目前,无法学习神经网络算法的原因由很多。但是如果你能检查出算法出现错误的地方,对学习神经网络算法将会非常有帮助。
当选用梯度下降算法时,你可以看看这些能帮助你规避问题的小提示:
- 误码率——特定迭代之后,你应该检查训练误差和测试误差,并且确保训练误差和测试误差有所减少。如果事实并非如此,那么可能会出现问题!
- 隐含层数的梯度风气流——如果网络没有出现梯度消失或梯度爆炸问题,那么请检查一下网络。
- 学习率——选用自适应技术时,你应该检测一下学习率。
Reference
- 简述动量Momentum梯度下降
- 《一文清晰讲解机器学习中梯度下降算法(包括其变式算法)》,来自微信公众号AI科技大本营