文章目录
- Python 梯度下降法(七):Summary
- 一、核心思想
- 1.1 核心思想
- 1.2 优化方法概述
- 1.3 第三方库的使用
- 二、 BGD
- 2.1 介绍
- 2.2 torch 库算法
- 2.2 代码示例
- 2.3 SGD
- 2.4 SGD代码示例
- 2.5 MBGD
- 2.6 MBGD 代码示例
- 三、 Adagrad
- 3.1 介绍
- 3.2 torch 库算法
- 3.3 代码示例
- 四、 Adadelta
- 4.1 介绍
- 4.2 torch 库算法
- 4.3 代码示例
- 五、RMSProp
- 5.1 介绍
- 5.2 torch 库算法
- 5.3 代码示例
- 六、Adam
- 6.1 介绍
- 6.2 torch 库算法
- 6.3 代码示例
- 七、Nadam
- 7.1 介绍
- 7.2 torch 库算法
- 7.3 代码示例
- 八、 总结
- 九、相关链接
Python 梯度下降法(七):Summary
一、核心思想
1.1 核心思想
梯度下降法的核心思想是通过不断调整参数,使损失函数的值逐渐减小。每次迭代中,参数沿着损失函数梯度的反方向更新,直到达到最小值或收敛。
在Python 梯度下降法系列文章中,我们从基础到进阶,逐步深入探讨了梯度下降法的原理、实现和优化方法。
Python 梯度下降法(一):Gradient Descent我介绍了常用的梯度下降法,其核心公式为:
θ t + 1 : = θ t − η ∇ θ J ( θ ) \theta_{t+1}:=\theta_{t}-\eta \nabla_{\theta}J(\theta) θt+1:=θt−η∇θJ(θ)
这篇文章详细讲解了如何有效使用这一公式,并通过 Python 实现了一个简单的梯度下降算法。
负梯度下降是下降的最快方向,但是由于步长的存在,导致路径并不是最优的,如A->C
是沿负梯度进行,但是其并不是最优解,B->C
的过程并不是最优的选择。
在后续的文章中,我们逐步探讨了如何基于梯度下降法的核心公式进行优化,优化思路:
- 使学习率 η \eta η改变,即改变步长
- 使梯度 ∇ θ J ( θ ) \nabla_{\theta}J(\theta) ∇θJ(θ)改变,即改变方向
- 在 θ \theta θ更新之前进行处理,可使之变小,即权重衰减
1.2 优化方法概述
以降低算法的复杂度、资源占用率,并提升其在实际应用中的性能。这些优化方法包括:
- 批量梯度下降(BGD):使用全部数据计算梯度,稳定性高但计算开销大。
- 随机梯度下降(SGD):每次迭代使用单个样本计算梯度,计算速度快但收敛不稳定。
- 小批量梯度下降(Mini-batch GD):结合 BGD 和 SGD 的优点,使用小批量数据计算梯度。
- 动量法(Momentum):引入动量项,加速收敛并减少震荡。
- 自适应学习率方法:如 AdaGrad、RMSProp、Adadelta、Adam 和 NAdam,动态调整学习率,适合处理稀疏数据和非凸优化问题。
1.3 第三方库的使用
这些梯度下降的方法可以使用,torch.optim 里面的库来实现
torch进行机器学习流程概述:
- 生成示例数据:可以使用
torch
生成随机数据作为训练数据 - 定义线性模型:使用
torch.nn.Linear
定义一个简答的线性模型 - 定义损失函数:使用均方误差损失函数
torch.nn.MESLoss
- 定义优化器:使用
torch.optim
里面的类,设置参数 - 训练模型:进行多次迭代,每次迭代计算损失并且更新模型参数
- 可视化:绘制损失函数图像,当然这是博客需要,你可以绘制原始数据和拟合直线。
二、 BGD
2.1 介绍
Python 梯度下降法(一):Gradient Descent-CSDN博客
GD:
BGD:
SGD — PyTorch 2.6 documentation
2.2 torch 库算法
通过官方给定的算法,我们可以发现 momentum ≠ 0 \text{momentum}\ne 0 momentum=0时,即为Momentum梯度下降法,故就不在后文赘述。
2.2 代码示例
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0)
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt# 生成随机数据
# 生成 100 个在 [0, 2) 之间的随机数作为输入特征
x = 2 * torch.rand(100, 1)
# 生成对应的目标值,添加一些随机噪声
y = 4 + 3 * x + torch.randn(100, 1)loss_ = [] # 存储历史损失值# 定义线性模型
model = nn.Linear(1, 1)# 定义损失函数
criterion = nn.MSELoss()# 定义优化器,使用批量梯度下降(BGD),学习率设置为 0.01
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0)# 训练模型
num_epochs = 1000
threshold = 1e-8
for epoch in range(num_epochs):# 前向传播outputs = model(x)loss = criterion(outputs, y) # 使用MES(均方误差损失)的方式计算损失值loss_.append(loss.item())# 反向传播和优化optimizer.zero_grad() # 在每次迭代中将梯度清零loss.backward() # 反向传播,根据链式法则自动计算损失函数对每个参数的偏导数,并将这些梯度存储在参数的 .grad 属性optimizer.step() # 根据优化算法更新参数# 检查是否收敛if epoch > 1 and abs(loss_[-1] - loss_[-2]) < threshold:print(f"Converged at iteration {epoch}")break# 可视化结果
print("斜率:", model.weight.item(), "偏移量:", model.bias.item())
# 绘制损失函数图像
plt.plot(range(len(loss_)), loss_)
plt.title('Linear Regression using BGD')
plt.xlabel('x')
plt.ylabel('y')
plt.grid(True) # 显示网格线
plt.show()
2.3 SGD
2.4 SGD代码示例
其类似BGD,只是每次拟合的样本数据为1
这里使用DataLoader
模块来进行数据的选择
from torch.utils.data import TensorDataset, DataLoader# 创建数据集对象
dataset = TensorDataset(x, y)# 设置 batch_size
batch_size = 1
# 创建数据加载器,指定 batch_size 和是否打乱数据
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)
总代码:
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import TensorDataset, DataLoader
import matplotlib.pyplot as plt# 生成随机数据
# 生成 100 个在 [0, 2) 之间的随机数作为输入特征
x = 2 * torch.rand(100, 1)
# 生成对应的目标值,添加一些随机噪声
y = 4 + 3 * x + torch.randn(100, 1)loss_ = [] # 存储历史损失值# 定义线性模型
model = nn.Linear(1, 1)# 定义损失函数
criterion = nn.MSELoss()# 定义优化器,使用批量梯度下降(BGD),学习率设置为 0.01
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0)# 创建数据集对象
dataset = TensorDataset(x, y)# 设置 batch_size
batch_size = 1
# 创建数据加载器,指定 batch_size 和是否打乱数据
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)# 训练模型
num_epochs = 10
threshold = 1e-8
for epoch in range(num_epochs):# 前向传播for inputs, labels in dataloader:outputs = model(inputs)loss = criterion(outputs, labels) # 使用MES(均方误差损失)的方式计算损失值loss_.append(loss.item())# 反向传播和优化optimizer.zero_grad() # 在每次迭代中将梯度清零loss.backward() # 反向传播,根据链式法则自动计算损失函数对每个参数的偏导数,并将这些梯度存储在参数的 .grad 属性optimizer.step() # 根据优化算法更新参数# 检查是否收敛if epoch > 1 and abs(loss_[-1] - loss_[-2]) < threshold:print(f"Converged at iteration {epoch}")break# 可视化结果
print("斜率:", model.weight.item(), "偏移量:", model.bias.item())
# 绘制损失函数图像
plt.plot(range(len(loss_)), loss_)
plt.title('Linear Regression using BGD')
plt.xlabel('x')
plt.ylabel('y')
plt.grid(True) # 显示网格线
plt.show()
2.5 MBGD
2.6 MBGD 代码示例
设置 1 < batch_size < all 1<\text{batch\_size}<\text{all} 1<batch_size<all,即可实现
总代码:
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import TensorDataset, DataLoader
import matplotlib.pyplot as plt# 生成随机数据
# 生成 100 个在 [0, 2) 之间的随机数作为输入特征
x = 2 * torch.rand(100, 1)
# 生成对应的目标值,添加一些随机噪声
y = 4 + 3 * x + torch.randn(100, 1)loss_ = [] # 存储历史损失值# 定义线性模型
model = nn.Linear(1, 1)# 定义损失函数
criterion = nn.MSELoss()# 定义优化器,使用批量梯度下降(BGD),学习率设置为 0.01
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0)# 创建数据集对象
dataset = TensorDataset(x, y)# 设置 batch_size
batch_size = 40
# 创建数据加载器,指定 batch_size 和是否打乱数据
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)# 训练模型
num_epochs = 100
threshold = 1e-8
for epoch in range(num_epochs):# 前向传播for inputs, labels in dataloader:outputs = model(inputs)loss = criterion(outputs, labels) # 使用MES(均方误差损失)的方式计算损失值loss_.append(loss.item())# 反向传播和优化optimizer.zero_grad() # 在每次迭代中将梯度清零loss.backward() # 反向传播,根据链式法则自动计算损失函数对每个参数的偏导数,并将这些梯度存储在参数的 .grad 属性optimizer.step() # 根据优化算法更新参数# 检查是否收敛if epoch > 1 and abs(loss_[-1] - loss_[-2]) < threshold:print(f"Converged at iteration {epoch}")break# 可视化结果
print("斜率:", model.weight.item(), "偏移量:", model.bias.item())
# 绘制损失函数图像
plt.plot(range(len(loss_)), loss_)
plt.title('Linear Regression using SGD')
plt.xlabel('x')
plt.ylabel('y')
plt.grid(True) # 显示网格线
plt.show()
即,总结:
三、 Adagrad
3.1 介绍
Python 梯度下降法(三):Adagrad Optimize-CSDN博客
学习率除一个数值,这个数值是历史上所有梯度数据的平方再开方。 g t 2 g_{t}^{2} gt2越大,学习率越小,使得学习率自适应。
假设一个参数累计历史梯度极大,那么经过计算更新这个一步之后,它的学习率反而会变小。相反的,如果一个参数累计历史梯度极小,那么经过计算更新这一步之后,它的学习率反而会变大。
缺点:累加的太快,学习率小的太快,导致最后的学习很慢。
Adagrad — PyTorch 2.6 documentation
3.2 torch 库算法
3.3 代码示例
optimizer = optim.Adagrad(model.parameters(), lr=lr)
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt# 生成随机数据
# 生成 100 个在 [0, 2) 之间的随机数作为输入特征
x = 2 * torch.rand(100, 1)
# 生成对应的目标值,添加一些随机噪声
y = 4 + 3 * x + torch.randn(100, 1)loss_ = [] # 存储历史损失值# 定义线性模型
model = nn.Linear(1, 1)# 定义损失函数
criterion = nn.MSELoss()# 定义优化器,使用Adagrad,学习率设置为 0.01
lr = 0.8
optimizer = optim.Adagrad(model.parameters(), lr=lr)# 训练模型
num_epochs = 1000
threshold = 1e-8
for epoch in range(num_epochs):# 前向传播outputs = model(x)loss = criterion(outputs, y) # 使用MES(均方误差损失)的方式计算损失值loss_.append(loss.item())# 反向传播和优化optimizer.zero_grad() # 在每次迭代中将梯度清零loss.backward() # 反向传播,根据链式法则自动计算损失函数对每个参数的偏导数,并将这些梯度存储在参数的 .grad 属性optimizer.step() # 根据优化算法更新参数# 检查是否收敛if epoch > 1 and abs(loss_[-1] - loss_[-2]) < threshold:print(f"Converged at iteration {epoch}")break# 可视化结果
print("斜率:", model.weight.item(), "偏移量:", model.bias.item())
# 绘制损失函数图像
plt.plot(range(len(loss_)), loss_)
plt.title('Linear Regression using Adagrad')
plt.xlabel('x')
plt.ylabel('y')
plt.grid(True) # 显示网格线
plt.show()
四、 Adadelta
4.1 介绍
Python 梯度下降法(四):Adadelta Optimize-CSDN博客
Adadelta 是正对 Adagrad 学习速率衰减过快问题做出的改进。改进思路和RMSProp很像,但是其背后是基于一次梯度近似代替二次梯度的思想。Adagrad 会累加之前所有的梯度平方,而 Adadelta 只累加固定大小的项,并且也不直接存储这些项,仅仅是近似计算对应的平均值。
Adadelta — PyTorch 2.6 documentation
4.2 torch 库算法
4.3 代码示例
optimizer = optim.Adadelta(model.parameters(), lr=lr)
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt# 生成随机数据
# 生成 100 个在 [0, 2) 之间的随机数作为输入特征
x = 2 * torch.rand(100, 1)
# 生成对应的目标值,添加一些随机噪声
y = 4 + 3 * x + torch.randn(100, 1)loss_ = [] # 存储历史损失值# 定义线性模型
model = nn.Linear(1, 1)# 定义损失函数
criterion = nn.MSELoss()# 定义优化器,Adadelta
lr = 2
optimizer = optim.Adadelta(model.parameters(), lr=lr) # 衰减率默认为0.9# 训练模型
num_epochs = 1000
threshold = 1e-8
for epoch in range(num_epochs):# 前向传播outputs = model(x)loss = criterion(outputs, y) # 使用MES(均方误差损失)的方式计算损失值loss_.append(loss.item())# 反向传播和优化optimizer.zero_grad() # 在每次迭代中将梯度清零loss.backward() # 反向传播,根据链式法则自动计算损失函数对每个参数的偏导数,并将这些梯度存储在参数的 .grad 属性optimizer.step() # 根据优化算法更新参数# 检查是否收敛if epoch > 1 and abs(loss_[-1] - loss_[-2]) < threshold:print(f"Converged at iteration {epoch}")break# 可视化结果
print("斜率:", model.weight.item(), "偏移量:", model.bias.item())
# 绘制损失函数图像
plt.plot(range(len(loss_)), loss_)
plt.title('Linear Regression using Adadelta')
plt.xlabel('x')
plt.ylabel('y')
plt.grid(True) # 显示网格线
plt.show()
五、RMSProp
5.1 介绍
Python 梯度下降法(二):RMSProp Optimize-CSDN博客
Adagrad 根据平方梯度的整个历史来收缩学习率,可能使得学习率在达到局部最小值之前就变得太小而难以继续训练;
RMSProp 使用指数衰减平均以丢弃遥远的历史,使其能够在找到某个“凸”结构后快速收敛;此外,RMSProp 还加入了一个超参数 ρ \rho ρ用于控制衰减率。
RMSProp 主要思想:使用指数加权移动平均的方法计算累积梯度,以丢弃遥远的梯度历史信息(让距离当前越远的梯度的缩减下学习率的权重越小)
衰减率表明的是只是最近的梯度平方有意义,而很久以前的梯度基本上会被遗忘。
RMSprop — PyTorch 2.6 documentation
5.2 torch 库算法
5.3 代码示例
optimizer = optim.RMSprop(model.parameters(), lr=lr)
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt# 生成随机数据
# 生成 100 个在 [0, 2) 之间的随机数作为输入特征
x = 2 * torch.rand(100, 1)
# 生成对应的目标值,添加一些随机噪声
y = 4 + 3 * x + torch.randn(100, 1)loss_ = [] # 存储历史损失值# 定义线性模型
model = nn.Linear(1, 1)# 定义损失函数
criterion = nn.MSELoss()# 定义优化器,RMSprop
lr = 0.01
optimizer = optim.RMSprop(model.parameters(), lr=lr) # 衰减率默认为0.99# 训练模型
num_epochs = 1000
threshold = 1e-8
for epoch in range(num_epochs):# 前向传播outputs = model(x)loss = criterion(outputs, y) # 使用MES(均方误差损失)的方式计算损失值loss_.append(loss.item())# 反向传播和优化optimizer.zero_grad() # 在每次迭代中将梯度清零loss.backward() # 反向传播,根据链式法则自动计算损失函数对每个参数的偏导数,并将这些梯度存储在参数的 .grad 属性optimizer.step() # 根据优化算法更新参数# 检查是否收敛if epoch > 1 and abs(loss_[-1] - loss_[-2]) < threshold:print(f"Converged at iteration {epoch}")break# 可视化结果
print("斜率:", model.weight.item(), "偏移量:", model.bias.item())
# 绘制损失函数图像
plt.plot(range(len(loss_)), loss_)
plt.title('Linear Regression using RMSprop')
plt.xlabel('x')
plt.ylabel('y')
plt.grid(True) # 显示网格线
plt.show()
六、Adam
6.1 介绍
Python 梯度下降法(五):Adam Optimize-CSDN博客
Adam 算法的本质:其实就是 Momentum + RMSProp 的结合,让后再修正其偏差。Adam 对梯度的一阶和二阶都进行了估计与偏差修正,使用梯度的一阶矩估计和二阶矩估计来动态调整每个参数的学习率(参数更新的频率)。
缺点:Adam 优化器在权重衰减的处理上存在问题,Adam 算法弱化了 L2 范数正则化的作用,导致模型过拟合或者训练效果不佳。
Adam — PyTorch 2.6 documentation
6.2 torch 库算法
6.3 代码示例
optimizer = optim.Adam(model.parameters(), lr=lr)
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt# 生成随机数据
# 生成 100 个在 [0, 2) 之间的随机数作为输入特征
x = 2 * torch.rand(100, 1)
# 生成对应的目标值,添加一些随机噪声
y = 4 + 3 * x + torch.randn(100, 1)loss_ = [] # 存储历史损失值# 定义线性模型
model = nn.Linear(1, 1)# 定义损失函数
criterion = nn.MSELoss()# 定义优化器,Adam
lr = 0.03
optimizer = optim.Adam(model.parameters(), lr=lr) # 默认beta: 0.9, 0.99# 训练模型
num_epochs = 1000
threshold = 1e-8
for epoch in range(num_epochs):# 前向传播outputs = model(x)loss = criterion(outputs, y) # 使用MES(均方误差损失)的方式计算损失值loss_.append(loss.item())# 反向传播和优化optimizer.zero_grad() # 在每次迭代中将梯度清零loss.backward() # 反向传播,根据链式法则自动计算损失函数对每个参数的偏导数,并将这些梯度存储在参数的 .grad 属性optimizer.step() # 根据优化算法更新参数# 检查是否收敛if epoch > 1 and abs(loss_[-1] - loss_[-2]) < threshold:print(f"Converged at iteration {epoch}")break# 可视化结果
print("斜率:", model.weight.item(), "偏移量:", model.bias.item())
# 绘制损失函数图像
plt.plot(range(len(loss_)), loss_)
plt.title('Linear Regression using Adam')
plt.xlabel('x')
plt.ylabel('y')
plt.grid(True) # 显示网格线
plt.show()
七、Nadam
7.1 介绍
Python 梯度下降法(六):Nadam Optimize-CSDN博客
Nadam 类似带有 Nesterov 动量项的Adam
其对学习率有了更强的约束,同时对梯度的会更新也有更直观的影响。
NAdam — PyTorch 2.6 documentation
7.2 torch 库算法
7.3 代码示例
optimizer = optim.NAdam(model.parameters(), lr=lr)
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt# 生成随机数据
# 生成 100 个在 [0, 2) 之间的随机数作为输入特征
x = 2 * torch.rand(100, 1)
# 生成对应的目标值,添加一些随机噪声
y = 4 + 3 * x + torch.randn(100, 1)loss_ = [] # 存储历史损失值# 定义线性模型
model = nn.Linear(1, 1)# 定义损失函数
criterion = nn.MSELoss()# 定义优化器,NAdam
lr = 0.03
optimizer = optim.NAdam(model.parameters(), lr=lr) # 默认beta: 0.9, 0.99# 训练模型
num_epochs = 1000
threshold = 1e-8
for epoch in range(num_epochs):# 前向传播outputs = model(x)loss = criterion(outputs, y) # 使用MES(均方误差损失)的方式计算损失值loss_.append(loss.item())# 反向传播和优化optimizer.zero_grad() # 在每次迭代中将梯度清零loss.backward() # 反向传播,根据链式法则自动计算损失函数对每个参数的偏导数,并将这些梯度存储在参数的 .grad 属性optimizer.step() # 根据优化算法更新参数# 检查是否收敛if epoch > 1 and abs(loss_[-1] - loss_[-2]) < threshold:print(f"Converged at iteration {epoch}")break# 可视化结果
print("斜率:", model.weight.item(), "偏移量:", model.bias.item())
# 绘制损失函数图像
plt.plot(range(len(loss_)), loss_)
plt.title('Linear Regression using NAdam')
plt.xlabel('x')
plt.ylabel('y')
plt.grid(True) # 显示网格线
plt.show()
八、 总结
使用torch库,我们可以比较轻松的实现梯度下降法的大部分算法。
九、相关链接
Python 梯度下降法合集:
- Python 梯度下降法(一):Gradient Descent-CSDN博客
- Python 梯度下降法(二):RMSProp Optimize-CSDN博客
- Python 梯度下降法(三):Adagrad Optimize-CSDN博客
- Python 梯度下降法(四):Adadelta Optimize-CSDN博客
- Python 梯度下降法(五):Adam Optimize-CSDN博客
- Python 梯度下降法(六):Nadam Optimize-CSDN博客
- Python 梯度下降法(七):Summary-CSDN博客