文章目录
- [Python 梯度下降法(四):Adadelta Optimize](#Python 梯度下降法(四):Adadelta Optimize)
Python 梯度下降法(四):Adadelta Optimize
相关链接:
Python 梯度下降法(一):Gradient Descent-CSDN博客
Python 梯度下降法(二):RMSProp Optimize-CSDN博客
Python 梯度下降法(三):Adagrad Optimize-CSDN博客
一、数学原理
1.1 介绍
Adadelta 是一种自适应学习率的优化算法,它是对 Adagrad 算法的改进,旨在解决 Adagrad 学习率单调递减过快的问题。
Adagrad 算法会累积所有历史梯度的平方和,随着迭代次数的增加,学习率会不断减小,最终可能变得非常小,导致参数更新极其缓慢甚至停滞。
Adadelta 算法通过引入梯度平方的指数加权移动平均,只关注近期的梯度信息,避免了学习率的过度衰减。同时,Adadelta 不需要手动设置全局学习率,它通过使用参数更新量的指数加权移动平均来自适应地调整学习率。
1.2 实现流程
设 θ \theta θ是需要优化的参数向量, g t g_{t} gt是在第t次迭代时损失函数关于参数 θ \theta θ的梯度,即 g t = ∇ θ J ( θ t ) g_{t}=\nabla_{\theta}J(\theta_{t}) gt=∇θJ(θt),算法的步骤以及其对应的公式如下:
- 初始化梯度平方的指数加权移动平均 E [ g 2 ] 0 = 0 ⃗ n × 1 E[g^{2}]{0}=\vec{0}{n\times_{1}} E[g2]0=0 n×1
初始化参数更新量平方的指数加权移动平均 E [ Δ θ 2 ] 0 = 0 ⃗ n × 1 E[\Delta\theta^{2}]{0}=\vec{0}{n\times 1} E[Δθ2]0=0 n×1
设定衰减率 ρ \rho ρ通常为 0.9 0.9 0.9,无穷小量的常数 ϵ \epsilon ϵ通常为 1 0 − 8 10^{-8} 10−8 - 更新梯度平方的指数加权移动平均: E [ g 2 ] t = ρ E [ g 2 ] t − 1 + ( 1 − ρ ) g t 2 E[g^{2}]{t}=\rho E[g^{2}]{t-1}+(1-\rho)g^{2}{t} E[g2]t=ρE[g2]t−1+(1−ρ)gt2(这里的运算都是使用numpy的广播机制来进行运算的)
计算参数更新量: Δ θ t = − E [ Δ θ 2 ] t − 1 + ϵ E [ g 2 ] t + ϵ ⊙ g t \Delta\theta{t}=-\frac{\sqrt{ E[\Delta\theta^{2}]{t-1}+\epsilon }}{\sqrt{ E[g^{2}]{t}+\epsilon}}\odot g_{t} Δθt=−E[g2]t+ϵ E[Δθ2]t−1+ϵ ⊙gt(使用numpy的广播机制更好运算)
这里,分子 E [ Δ θ 2 ] t − 1 + ϵ \sqrt{ E[\Delta\theta^{2}]{t-1}+\epsilon } E[Δθ2]t−1+ϵ 可以看作是参数更新量的估计,分母 E [ g 2 ] t − 1 + ϵ \sqrt{ E[g^{2}]{t-1}+\epsilon} E[g2]t−1+ϵ 对梯度进行了归一化处理,从而实现了自适应学习率的调整 - 更新参数: θ t + 1 = θ t + η Δ θ t \theta_{t+1}=\theta_{t}+\eta\Delta\theta_{t} θt+1=θt+ηΔθt
更新梯度平方的指数加权移动平均: E [ Δ θ 2 ] t = ρ E [ Δ θ 2 ] t − 1 + ( 1 − ρ ) Δ θ t 2 E[\Delta\theta^{2}]{t}=\rho E[\Delta\theta^{2}]{t-1}+(1-\rho)\Delta\theta^{2}_{t} E[Δθ2]t=ρE[Δθ2]t−1+(1−ρ)Δθt2
二、代码实现
2.1 函数代码
python
# 定义Adadelta函数
def adadelta_optimizer(X, y, eta, num_iter=1000, rho=0.01, epsilon=1e-8, threshold=1e-8):
"""
X: 数据 x mxn,可以在传入数据之前进行数据的归一化
y: 数据 y nx1
eta: 学习率
num_iter: 迭代次数
rho: 衰减率
epsilon: 无穷小
threshold: 阈值
"""
# 初始化参数
m, n = X.shape
theta, E_g2, E_theta2, loss_ = np.random.rand(n, 1), np.zeros((n, 1)), np.zeros((n, 1)), [] # n x 1, loss_存储损失率的变化
for _ in range(num_iter):
# 开始迭代
# 使用点积计算预测值
h = X.dot(theta)
# 计算误差
error = h - y
loss_.append(np.mean(error ** 2) / 2)
# 计算梯度
gradient = (1/m) * X.T.dot(error)
E_g2 = rho * E_g2 + (1 - rho) * np.pow(gradient, 2) # 更新梯度平方的指数加权移动平均
# 在运算过程中,可能会由于误差的影响,可能会出现负数的情况,使得我们的模型无法运行,增强模型的稳定性
E_g2 = np.maximum(E_g2, 0)
E_theta2 = np.maximum(E_theta2, 0)
delta_theta = - np.multiply(np.sqrt(E_theta2 + epsilon) / np.sqrt(E_g2 + epsilon), gradient) # 计算参数更新量
theta = theta + eta * delta_theta # 更新参数
E_theta2 = rho * E_theta2 + (1 - rho) * delta_theta ** 2
if (_ > 1) and (abs(loss_[-1] - loss_[-2]) < threshold):
print(f"Converged at iteration {_ + 1}")
break
return theta.flatten(), loss_
2.2 总代码
python
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
# 定义Adadelta函数
def adadelta_optimizer(X, y, eta, num_iter=1000, rho=0.01, epsilon=1e-8, threshold=1e-8):
"""
X: 数据 x mxn,可以在传入数据之前进行数据的归一化
y: 数据 y nx1
eta: 学习率
num_iter: 迭代次数
rho: 衰减率
epsilon: 无穷小
threshold: 阈值
"""
# 初始化参数
m, n = X.shape
theta, E_g2, E_theta2, loss_ = np.random.rand(n, 1), np.zeros((n, 1)), np.zeros((n, 1)), [] # n x 1, loss_存储损失率的变化
for _ in range(num_iter):
# 开始迭代
# 使用点积计算预测值
h = X.dot(theta)
# 计算误差
error = h - y
loss_.append(np.mean(error ** 2) / 2)
# 计算梯度
gradient = (1/m) * X.T.dot(error)
E_g2 = rho * E_g2 + (1 - rho) * np.pow(gradient, 2) # 更新梯度平方的指数加权移动平均
# 在运算过程中,可能会由于误差的影响,可能会出现负数的情况,使得我们的模型无法运行,增强模型的稳定性
E_g2 = np.maximum(E_g2, 0)
E_theta2 = np.maximum(E_theta2, 0)
delta_theta = - np.multiply(np.sqrt(E_theta2 + epsilon) / np.sqrt(E_g2 + epsilon), gradient) # 计算参数更新量
theta = theta + eta * delta_theta # 更新参数
E_theta2 = rho * E_theta2 + (1 - rho) * delta_theta ** 2
if (_ > 1) and (abs(loss_[-1] - loss_[-2]) < threshold):
print(f"Converged at iteration {_ + 1}")
break
return theta.flatten(), loss_
# 生成一些示例数据
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)
# 添加偏置项
X_b = np.c_[np.ones((100, 1)), X]
# 超参数
eta = 2 # 通过调试设置的,使其可以更快的移动,进而使得在较小的迭代次数内损失函数可以收敛
theta, loss_ = adadelta_optimizer(X_b, y, eta)
print("最优参数 theta:")
print(theta)
plt.plot(range(len(loss_)), loss_, label="损失函数图像")
plt.title("损失函数图像")
plt.xlabel("迭代次数")
plt.ylabel("损失值")
plt.show()
三、优缺点
3.1 优点
自适应调整学习率:Adadelta 能够根据参数的梯度变化自适应地调整每个参数的学习率。它通过计算梯度平方的指数加权移动平均和参数更新量平方的指数加权移动平均,动态地为不同参数分配合适的学习率。对于梯度变化较大的参数,学习率会自动减小;对于梯度变化较小的参数,学习率会相对增大。这种自适应机制使得算法在处理不同尺度和变化频率的梯度时表现出色,尤其适用于处理稀疏数据,因为稀疏数据中某些特征的梯度可能很少出现,Adadelta 可以为这些特征的参数提供较大的学习率,促进模型学习。
无需手动设置全局学习率:与一些需要手动精细调整全局学习率的优化算法(如随机梯度下降)不同,Adadelta 不需要用户指定全局学习率。它在算法内部通过自身的机制来确定每个参数的有效学习率,减少了超参数调优的工作量。这对于缺乏经验的使用者或者在大规模实验中快速尝试不同模型时非常方便,降低了调参的难度和时间成本。
缓解学习率衰减问题:Adagrad 算法会累积所有历史梯度的平方和,随着迭代次数的增加,学习率会不断减小,最终可能变得极小,导致参数更新极其缓慢甚至停滞。Adadelta 通过引入指数加权移动平均,只关注近期的梯度信息,避免了学习率的过度衰减。这使得算法在训练后期仍然能够继续更新参数,保持一定的收敛速度,提高了模型的训练效率和最终性能。
收敛相对稳定:Adadelta 在优化过程中,由于自适应学习率的调整,能够在一定程度上避免梯度的剧烈波动,使得参数更新更加平稳。相比一些使用固定学习率的算法,Adadelta 更不容易出现因学习率过大导致的参数震荡发散,或者因学习率过小导致的收敛缓慢问题,能够更稳定地朝着最优解收敛。
3.2 缺点
对衰减率敏感 :Adadelta 算法中有一个重要的超参数 ρ \rho ρ(衰减率),它控制着指数加权移动平均中历史信息和当前信息的权重。 ρ \rho ρ的取值会显著影响算法的性能。如果 ρ \rho ρ设置过大,算法会过于依赖历史梯度信息,对当前梯度的变化反应迟钝,可能导致收敛速度变慢;如果 ρ \rho ρ设置过小,算法会过于关注当前梯度,容易受到噪声的影响,使收敛过程不稳定,甚至可能无法收敛到最优解。确定合适的 ρ \rho ρ值通常需要进行大量的实验和调优。
计算复杂度较高:Adadelta 需要维护两个指数加权移动平均,即梯度平方的指数加权移动平均和参数更新量平方的指数加权移动平均。这意味着在每次迭代中,除了计算梯度和更新参数外,还需要额外计算和更新这两个移动平均,增加了计算量和内存开销。对于大规模的深度学习模型,尤其是具有大量参数的模型,这种额外的计算负担可能会导致训练时间显著增加,对计算资源的要求也更高。