梯度下降法
即 Gradient Descent 。我们不是来讲最优化方法的,所以这里将不会提供收敛性和收敛速度的严格分析。
问题背景
- 我们希望求解如下问题:
其中 在定义域内可微。
算法描述
假设我们已经得到了一个初始解
, 那么一个简单的思路是,我们每次向 处的负梯度方向移动。我们期待通过有限次的移动就可以抵达一个足够好的解,这就是最朴素的梯度下降算法。 1
2
3
4
5# Vanilla gradient descent
w = initialize_weights()
for t in range(num_steps):
dw = compute_gradient(loss_func, data, w)
w -= learning_rate * dw
SGD
即 Stochastic Gradient Descent 。在运行梯度下降法时,实践中的一个常见的问题是,我们的数据集往往很大,那么每一次计算损失函数的时间开销是不能接受的。因此我们往往会考虑使用蒙特卡洛方法,即选定一个 batch size(e.g. 32/64/128),每一次计算梯度时,我们都从数据集中随机取样出大小为 batch size 的样本集,然后再对样本集计算梯度。我们期待在梯度下降的过程中,样本集可以较好的反映出数据集的性质。这样的算法就被称作 SGD 。
1
2
3
4
5
6# Stochastic gradient descent
w = initialize_weights()
for t in range(num_steps):
minibatch = sample_data(data, batch_size)
dw = compute_gradient(loss_func, minibatch, w)
w -= learning_rate * dw
SGD with Momentum
为了避免梯度的剧烈震荡/解停滞在鞍点/较大的噪声,我们可以考虑引入“速度”的概念来替代目前的“加速度”(即负梯度)。
1
2
3
4
5
6
7
8# Stochastic gradient descent with momentum
w = initialize_weights()
v = 0
for t in range(num_steps):
minibatch = sample_data(data, batch_size)
dw = compute_gradient(loss_func, minibatch, w)
v = rho * v + dw
w -= learning_rate * v其中的超参数
rho
是衰减系数。SGD with Momentum 也有另一种实现,称作 Nesterov Momentum ,这里就不详谈了。
AdaGrad
即自适应梯度下降。类似的,我们希望避免梯度剧烈震荡的问题,因此考虑保存每一维上梯度的平方历史和,并在计算下一个解时为每一维除以对应的开根平方历史和。这样做的好处是,变化大的维度被限制,而变化小的维度则相对不被限制。
1
2
3
4
5
6
7
8# Adaptive stochastic gradient descent
w = initialize_weights()
grad_squared = 0
for t in range(num_steps):
minibatch = sample_data(data, batch_size)
dw = compute_gradient(loss_func, minibatch, w)
grad_squared += dw**2
w -= learning_rate * dw / (grad_squared.sqrt() + 1e-7)
RMSProp
AdaGrad 存在一个明显的问题,就是随着
grad_squared
的积累,到最后解就很难移动了。一个好的改进是 RMSProp(Root Mean Square Propagation),可以将其视作带有衰减系数的 AdaGrad 。1
2
3
4
5
6
7
8# RMSProp
w = initialize_weights()
grad_squared = 0
for t in range(num_steps):
minibatch = sample_data(data, batch_size)
dw = compute_gradient(loss_func, minibatch, w)
grad_squared = decay_rate * grad_squared + (1 - decay_rate) * dw**2
w -= learning_rate * dw / (grad_squared.sqrt() + 1e-7)
Adam
现在我们试着结合 Momentum 和 RMSProp 的优点,这样就得到了 Adam :
1
2
3
4
5
6
7
8
9
10
11# Adaptive Moment Estimation
moment1 = 0
moment2 = 0
for t in range(num_steps):
minibatch = sample_data(data, batch_size)
dw = compute_gradient(loss_func, minibatch, w)
moment1 = beta1 * moment1 + (1 - beta1) * dw
moment2 = beta2 * moment2 + (1 - beta2) * dw**2
moment1_unbias = moment1 / (1 - beta1**t)
moment2_unbias = moment2 / (1 - beta2**t)
w -= learning_rate * moment1_unbias / (moment2_unbias.sqrt() + 1e-7)我们在上述的代码加入了计算
moment1_unbias
和moment2_unbias
,这是为了避免在算法最开始更新w
时除以一个极小的数,导致解剧烈变化。1特别的,Adam 在很多时候都是一个好的方法,建议的初始参数
beta1 = 0.9
,beta2 = 0.999
,learning_rate = 1e-3, 5e-4, 1e-4
。
基于二阶微分的优化
- 基于二阶微分的优化也是有研究的,但是并不常用,因为计算 Hessian 矩阵以及矩阵求逆的复杂度开销太高了。这里就不详谈了。
这样的写法应该有更严格的推导。↩︎
- 标题: 梯度下降法
- 作者: RPChe_
- 创建于 : 2025-03-06 00:00:00
- 更新于 : 2025-03-06 23:02:12
- 链接: https://rpche-6626.github.io/2025/03/06/DL/GD/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。