Python 梯度下降法(三):Adagrad Optimize
文章目录
- Python 梯度下降法(三):Adagrad Optimize
- 一、数学原理
- 1.1 介绍
- 1.2 符号定义
- 1.3 实现流程
- 二、代码实现
- 2.1 函数代码
- 2.2 总代码
- 三、优缺点
- 3.1 优点
- 3.2 缺点
Python 梯度下降法(三):Adagrad Optimize
Python 梯度下降法(一):Gradient Descent-CSDN博客
Python 梯度下降法(二):RMSProp优化算法-CSDN博客
一、数学原理
1.1 介绍
Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,常用于机器学习中模型参数的优化,能根据每个参数的历史梯度信息自适应地调整学习率。
1.2 符号定义
符号 | 意义 |
---|---|
θ \theta θ | 模型待优化的参数向量( n × 1 n\times 1 n×1) |
J ( θ ) J(\theta) J(θ) | 模型的损失函数 |
g t = ∇ θ J ( θ t ) g_{t}=\nabla_{\theta}J(\theta_{t}) gt=∇θJ(θt) | 模型在第 t t t次迭代时的梯度( n × 1 n\times 1 n×1) |
η \eta η | 全局学习率,控制整体学习的步长 |
ϵ \epsilon ϵ | 无穷小量,防止分母为0,一般为 1 0 − 8 10^{-8} 10−8 |
G t G_{t} Gt | 每个参数对应的累积梯度平方和( n × 1 n\times 1 n×1) |
1.3 实现流程
- 初始化
θ
\theta
θ:全部初始化为0,或者初始化为随机小值
初始化每个参数对应的累计梯度平方和, G 0 , i = 0 , i ∈ ( 1 , 2 , ⋯ , n ) G_{0, i}=0,i\in(1,2,\cdots,n) G0,i=0,i∈(1,2,⋯,n), G t , i G_{t,i} Gt,i用于记录第 i i i个参数从开始到第 t t t次迭代的梯度平方的累计值。 - 迭代更新累计梯度平方和: G t = G t − 1 + g t ⊙ g t G_{t}=G_{t-1}+g_{t}\odot g_{t} Gt=Gt−1+gt⊙gt。 ⊙ \odot ⊙为Hadamard product.
- 更新参数向量 θ \theta θ: θ t = θ t − 1 − η G t + ϵ ⊙ g t \theta_{t}=\theta_{t-1}-\frac{\eta}{\sqrt{G_{t}+\epsilon }}\odot g_{t} θt=θt−1−Gt+ϵη⊙gt。使用numpy的广播机制进行更新。
二、代码实现
2.1 函数代码
# 定义Adagrad函数
def adagrad_optimizer(X, y, eta, num_iter=1000, epsilon=1e-8, threshold=1e-8):
"""
X: 数据 x mxn,可以在传入数据之前进行数据的归一化
y: 数据 y nx1
eta: 学习率
num_iter: 迭代次数
epsilon: 无穷小
threshold: 阈值
"""
# 初始化参数
m, n = X.shape
theta, G_sum, loss_ = np.random.rand(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)
# 更新累计梯度平方和
G_sum = G_sum + np.pow(gradient, 2) # 利用广播机制,进行逐元素相乘
# 更新参数theta
theta = theta - np.multiply(eta / np.sqrt(G_sum + epsilon), gradient)
if (_ > 1) and (abs(loss_[-1] - loss_[-2]) < threshold):
print(f"Converged at iteration {_ + 1}")
break
return theta, loss_
2.2 总代码
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
# 定义Adagrad函数
def adagrad_optimizer(X, y, eta, num_iter=1000, epsilon=1e-8, threshold=1e-8):
"""
X: 数据 x mxn,可以在传入数据之前进行数据的归一化
y: 数据 y nx1
eta: 学习率
num_iter: 迭代次数
epsilon: 无穷小
threshold: 阈值
"""
# 初始化参数
m, n = X.shape
theta, G_sum, loss_ = np.random.rand(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)
# 更新累计梯度平方和
G_sum = G_sum + np.pow(gradient, 2) # 利用广播机制,进行逐元素相乘
# 更新参数theta
theta = theta - np.multiply(eta / np.sqrt(G_sum + epsilon), gradient)
if (_ > 1) and (abs(loss_[-1] - loss_[-2]) < threshold):
print(f"Converged at iteration {_ + 1}")
break
return theta, 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 = 0.91 # 更具损失函数图像进行调整
# Adagrad优化算法
theta, loss_ = adagrad_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 优点
自适应学习率调整Adagrad:能够根据每个参数的历史梯度信息自适应地调整学习率。对于经常更新的参数,其梯度累积平方和会不断增大,导致学习率逐渐减小,避免该参数更新过快;而对于不经常更新的参数,梯度累积平方和相对较小,学习率会相对较大,使得这些参数能够更快地更新。这种特性使得 Adagrad 在处理稀疏数据时表现出色,因为稀疏数据中某些特征的出现频率较低,对应的参数更新不频繁,Adagrad 可以为这些参数分配更大的学习率,从而更有效地学习到数据中的信息。
无需手动频繁调整学习率:在很多情况下,Adagrad 可以使用固定的全局学习率,算法会自动根据参数的梯度历史来调整每个参数的实际学习率。这减少了手动调整学习率的工作量,尤其对于复杂的模型和大规模数据集,手动调参往往是一个耗时且困难的过程。使用 Adagrad 可以在一定程度上简化调参过程,提高开发效率。
理论保证收敛性:在一定的条件下,Adagrad 算法具有理论上的收敛保证。对于凸优化问题,只要满足适当的条件,Adagrad 能够收敛到全局最优解;对于非凸优化问题,也能收敛到局部最优解。这种理论上的保障使得 Adagrad 在实际应用中具有一定的可靠性。
3.2 缺点
学习率单调递减问题:随着迭代次数的增加,累积梯度平方和会不断增大,导致学习率不断减小。在训练后期,学习率可能会变得非常小,使得参数更新变得极其缓慢,甚至可能导致算法提前收敛到局部最优解,无法继续优化。这种学习率单调递减的特性限制了 Adagrad 在某些问题上的性能,尤其是对于需要长时间训练才能达到最优解的复杂模型。随着训练轮数的增加,Adagrad 的学习率会急剧下降,导致模型在后期几乎停止学习,难以进一步提高性能。
对初始学习率敏感:Adagrad 算法对初始学习率的选择比较敏感。如果初始学习率设置过大,在训练初期可能会导致参数更新步长过大,使得算法无法收敛甚至发散;如果初始学习率设置过小,在训练前期参数更新会非常缓慢,增加训练时间。因此,需要仔细调整初始学习率才能使算法达到较好的性能,这在一定程度上增加了使用 Adagrad 的难度。
内存需求较大:Adagrad 需要为每个参数维护一个累积梯度平方和,这意味着在处理高维数据或大规模模型时,需要额外的内存来存储这些累积值。对于一些内存受限的设备或应用场景,这可能会成为一个问题。例如,在处理具有数百万甚至数十亿参数的深度学习模型时,Adagrad 的内存开销可能会变得难以承受。