梯度下降(Gradient Descent)是人工智能,机器学习和深度学习的核心优化算法,几乎是所有的参数学习模型(线性回归、神经网络、CNN等)依赖最小化损失函数找到最优参数。
本质原理:沿着损失函数的下坡方向逐步迭代,最终找到最低点(最优解),全局极小值。
本质:梯度就是函数对它的各个自变量求偏导后,由偏导数组成的一个向量。
我们可以通过向量中每个自变量的正负值来表示方向,如果导数的值是正数,那么就代表X轴的“正方向”。如果导数的值是负数,那么就代表是X轴的“负方向”。(所谓的正负方向就是在极小值点的右边/左边)。
梯度下降原理:就是如果我们在极小值的正方向即导数大于0,则我们需要“下楼梯”下一次运动方向需要朝导数方向的反方向运动,如果导数小于0,我们就需要接着“下楼梯”顺着导数的方向运动,方向确定了,每次走的步幅多大呢,我们引入了学习率eta,通过(eta*偏导数)的值算出下一步我们走的步幅,再通过原本的所在位置坐标x1对步幅进行加减操作,来确定下一次我们到达的位置x2,加减操作取决于偏导数的正负。
梯度下降的公式:

案例:
将上述梯度下降算法运用到python中的代码如下
import matplotlib.pyplot as plt import numpy as np # 初始化参数 x = 6 learning_rate = 0.05 iterations = 50 # 存储迭代过程中的坐标 x_history = [x] y_history = [(x - 1)**2 + 1] # 梯度下降迭代 for _ in range(iterations): gradient = 2 * x - 2 x = x - learning_rate * gradient y = (x - 1)**2 + 1 x_history.append(x) y_history.append(y) # 绘制函数曲线 func_x = np.linspace(-5, 7, 100) func_y = (func_x - 1)**2 + 1 plt.plot(func_x, func_y) # 绘制迭代轨迹 plt.scatter(np.array(x_history), np.array(y_history), color='red') plt.axis([-7, 9, 0, 50]) plt.show()其产生的最终图像如下:
上述是2维情况下的例子仅有一个自变量,我们继续推广为3D情况,请看下图在曲面上的任意一点(灰点)要想去该曲面的极小值点(红点)则需要采用梯度下降的方法,在x轴方向,y轴方向,z轴方向都要下降相应的梯度,其n个方向受力的合力(红色箭头)代表该点最终的前进方向。

我们根据一维的平面计算其前进轨迹可以发散一下思维,在这个三位平面上我们有三个自变量[x,y,z],那么在每一个轴上的前进距离我们用[x1,x2,x3]代替,其每个轴上的计算方法可以为(此处仅列举两个轴上的运动距离)

也可以写成向量形式

我们又将其前两个自变量的偏导数组成一个【向量】:
我们可以很容易的从该点的向量值(正负)判断当前位置在不同维度时,朝向哪个方向变化可以使得函数值 y 减小。
我们再发散一下思维,如果将在n维形状上运动寻找极值点,那么我们就可以将物体最终运动的合力拆解为n个分力,每个分力运动方向可以为一个轴的梯度下降,这样我们就可以计算n个x偏导组成的向量也进一步计算n个轴上的运动距离,如下

用于求解机器学习模型参数(如线性回归的权重w,逻辑回归的系数等)优化算法,核心目标是最小化模型的【损失函数】(MSE,交叉熵)
与传统 “批量梯度下降(BGD)” 的核心区别:
- BGD:每次迭代用「全部训练数据」计算损失的梯度,更新一次参数。问题:数据量大时(如百万样本),计算量爆炸,训练速度极慢。
- SGD:每次迭代只用「1 个随机样本」计算梯度,更新一次参数。核心思想:用单个样本的 “局部梯度” 近似全局梯度,以 “牺牲一点点精度” 换 “极致的速度和内存效率”。
假设模型参数为 θ,损失函数为 L (θ),学习率为 η:
- BGD 的参数更新:θ = θ - η × ∇L (θ)(∇是全局梯度,用所有样本计算)
- SGD 的参数更新:θ = θ - η × ∇L (θ; x_i, y_i)(∇是单个样本 (x_i,y_i) 的局部梯度)
1.大模型数据集:
样本数>10W,甚至百万千万等,SGD 每次只处理 1 个样本,内存占用恒定(与数据量无关),迭代速度快,能快速看到模型收敛趋势。
2.在线学习(实时更新模型):
实时推荐,例如常见的股票预测,抖音推荐等。
3.模型简单,数据噪声不敏感:
模型是线性模型(线性回归、逻辑回归、SVM)或简单神经网络。
4.内存资源有限的场景:
嵌入式设备上做简单的温度预测(用线性回归 + SGD,实时处理传感器数据)
优点:
缺点:
1.数据预处理
SGD 对数据尺度敏感(因为梯度更新与特征值大小相关),必须先做 标准化 / 归一化:
sklearn.preprocessing.StandardScaler);sklearn.preprocessing.MinMaxScaler)。2. 学习率调度
torch.optim.lr_scheduler.ReduceLROnPlateau,当验证集损失不再下降时衰减)。3. 避免异常值影响
torch.nn.utils.clip_grad_norm_),避免异常样本导致梯度爆炸。用sgd做简单的线性回归
'''
1. 生成模拟数据
2. 数据预处理(标准化)
3. 划分训练/测试集
4. 初始化SGD模型
5. 训练模型
6. 评估模型
'''
#导入代码库
import numpy as np
#sklearn 库中封装好的 “SGD 回归器”—— 核心工具
from sklearn.linear_model import SGDRegressor
#数据 “标准化” 工具 ——SGD 算法的关键预处理步骤
from sklearn.preprocessing import StandardScaler
#拆分数据集的工具 —— 把数据分成 “训练集”(教模型学习)和 “测试集”
from sklearn.model_selection import train_test_split
#生成一个模拟数据
# 10万样本,5个特征(特征矩阵X:形状是 (100000, 5))
X = np.random.randn(100000, 5)
# 真实模型:y = 3*X1 + 2*X2 -5*X3 + 噪声(标签y:形状是 (100000,))
y = 3*X[:,0] + 2*X[:,1] - 5*X[:,2] + np.random.randn(100000)
#数据预处理
scaler = StandardScaler() # 初始化标准化工具
X_scaled = scaler.fit_transform(X) # 对X做标准化
#划分训练集测试集
X_train, X_test, y_train, y_test = train_test_split(
X_scaled, y, test_size=0.2, random_state=42
)
#初始化SGD回归模型,制定学习规则
sgd_reg = SGDRegressor(
loss='squared_error', # 损失函数:均方误差(MSE)
learning_rate='adaptive', # 学习率策略:自适应
eta0=0.01, # 初始学习率:0.01
max_iter=1000, # 最大迭代次数:1000次
random_state=42 # 固定随机种子,复现结果
)
#训练模型
sgd_reg.fit(X_train, y_train)
#评估模型
print("模型参数(权重w):", sgd_reg.coef_) # 接近真实值 [3,2,-5,0,0]
print("模型截距(b):", sgd_reg.intercept_)
print("测试集R²分数:", sgd_reg.score(X_test, y_test)) # 通常>0.8
在SGD算法的基础上添加了“动量”,积累了历史梯度的“惯性”,抵消当前梯度的噪音,让参数更新更平稳、更快。
SGD的基础算法:

SGD 的 3 个关键问题:
基于上述SGD的短板,Momentum的核心思想就是给SGD加“惯性”
想象参数更新是 “小球下山”:
Momentum相比于SGD其就是在SGD的基础上添加了【动量项】v,更新逻辑
1.计算动量项v(积累历史梯度)

2.动量项更新参数(代替 SGD 的直接梯度更新)
![]()
SGD和Momentum对比
案例:我们为了更清楚的对比两种梯度下降算法的差异我用AI生成了一个香蕉函数的3D图供大家,更好的查看两种算法的差异性

NAG是Momentum算法的改进版,核心是“先根据历史动量走一步,再计算梯度”,以此减少梯度估计的滞后性,让收敛更加精确。

直观理解:NAG 是 “有预判的 Momentum”
NAG针对Momentum的“梯度滞后性”做了一些优化
NAG 是 Momentum 的改进版,适用场景与 Momentum 高度重叠,但在梯度波动大、收敛路径崎岖的场景下表现更优:
优点
缺点

Adagrad 是自适应学习率优化器的开山之作,核心思想是 “对不同特征(或参数)使用不同的学习率”—— 在训练过程中,对出现频率低的特征采用大学习率,对出现频率高的特征采用小学习率。
其本质就是通过每个特征的频率更新其累计梯度平方和,从而进一步调节学习率,实现自适应学习率的作用。
1. 稀疏数据场景(如文本、推荐系统)
2. 特征尺度差异大的场景
3. 不需要手动调学习率的场景
优点
缺点

RMSProp原理:在Adagrad的基础上把「累积梯度平方」改成「梯度平方的指数移动平均」(相当于 “只关注近期梯度,遗忘远期梯度”)

RmsProp算法是Adagrad算法的改进版本,其核心目标是修复Adagrad【学习率单调递减,后期收敛停滞不前】的问题——通过【指数移动平均】替代Adagrad的【梯度平方累积】,让学习率可升可降。
Adagrad 用
累积梯度平方,导致 Gt只会越来越大,学习率
持续衰减,后期几乎无法更新参数。
优点
缺点
适用场景

Adam 是当前机器学习最常用的优化器,核心是 “RMSProp 的自适应学习率 + Momentum 的动量加速”—— 可以理解为 “集大成者”,直接复用 RMSProp 和 Momentum 的代码逻辑。
Adam 核心定位:RMSProp + Momentum 的结合体


Adadelta 是 Adagrad 的另一改进版,核心是 “解决 Adagrad 学习率停滞 + 无需手动设置初始学习率”—— 和 Adagrad 关联性强,但和你最近学的 RMSProp/Adam 关联性较弱,所以放在后面。
1. Adadelta 核心定位:Adagrad 的 “无学习率” 版本



import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import os
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统默认中文字体(黑体)
plt.rcParams['axes.unicode_minus'] = False
# --------------------- 1. 生成2特征模拟数据(适配3D可视化) ---------------------
torch.manual_seed(42)
n_samples = 1000
X = torch.randn(n_samples, 2) # 仅2个特征(x1, x2),方便3D可视化
w_true = torch.tensor([3.0, -5.0]) # 真实权重(w1=3, w2=-5)
y = X @ w_true + torch.randn(n_samples) * 0.5 # 线性关系+噪声
# 划分训练集(无需测试集,重点看轨迹)
X_train, y_train = X, y
# --------------------- 2. 定义简单线性模型(仅2个权重参数) ---------------------
class LinearModel2D(nn.Module):
def __init__(self):
super().__init__()
# 仅2个权重(w1, w2)+ 1个偏置(b),偏置不参与3D轨迹(重点看w1, w2)
self.linear = nn.Linear(2, 1)
def forward(self, x):
return self.linear(x)
# --------------------- 3. 初始化7个模型(参数完全一致,保证公平对比) ---------------------
model_sgd = LinearModel2D()
model_momentum = LinearModel2D()
model_nag = LinearModel2D()
model_adagrad = LinearModel2D()
model_rmsprop = LinearModel2D()
model_adadelta = LinearModel2D()
model_adam = LinearModel2D()
# 统一初始化参数(所有模型初始权重相同)
torch.manual_seed(42)
init_w = torch.randn_like(model_sgd.linear.weight.data) # 初始权重(w1, w2)
init_b = torch.randn_like(model_sgd.linear.bias.data) # 初始偏置
models = [model_sgd, model_momentum, model_nag, model_adagrad, model_rmsprop, model_adadelta, model_adam]
for model in models:
model.linear.weight.data = init_w.clone()
model.linear.bias.data = init_b.clone()
# --------------------- 4. 定义损失函数和7种优化器 ---------------------
criterion = nn.MSELoss()
# 优化器配置(沿用之前的最优超参数)
optimizers = [
torch.optim.SGD(model_sgd.parameters(), lr=0.01), # 不变
torch.optim.SGD(model_momentum.parameters(), lr=0.01, momentum=0.9), # 不变
torch.optim.SGD(model_nag.parameters(), lr=0.01, momentum=0.9, nesterov=True), # 不变
torch.optim.Adagrad(model_adagrad.parameters(), lr=0.01), # 不变(简单模型后期停滞不明显)
torch.optim.RMSprop(model_rmsprop.parameters(), lr=0.01, alpha=0.9), # lr从0.001→0.01
torch.optim.Adadelta(model_adadelta.parameters(), rho=0.95, lr=1.0), # 给Adadelta加默认lr=1.0(官方默认)
torch.optim.Adam(model_adam.parameters(), lr=0.01, betas=(0.9, 0.999)) # lr从0.001→0.01
]
optimizer_names = ["SGD", "Momentum", "NAG", "Adagrad", "RMSProp", "Adadelta", "Adam"]
# --------------------- 5. 训练并记录轨迹(w1, w2, loss) ---------------------
epochs = 150 # 迭代次数适中,保证轨迹完整且不冗余
trajectories = [] # 存储7种优化器的轨迹:每个元素是 (w1_list, w2_list, loss_list)
for idx, (model, optimizer) in enumerate(zip(models, optimizers)):
w1_list = [] # 记录每次迭代的w1
w2_list = [] # 记录每次迭代的w2
loss_list = [] # 记录每次迭代的损失值
for epoch in range(epochs):
model.train()
optimizer.zero_grad()
y_pred = model(X_train)
loss = criterion(y_pred, y_train.unsqueeze(1))
loss.backward()
optimizer.step()
# 记录当前参数和损失(detach()避免计算图占用内存)
w1 = model.linear.weight.data[0][0].detach().item()
w2 = model.linear.weight.data[0][1].detach().item()
loss_val = loss.item()
w1_list.append(w1)
w2_list.append(w2)
loss_list.append(loss_val)
trajectories.append((w1_list, w2_list, loss_list))
print(f"{optimizer_names[idx]} 训练完成,最终损失:{loss_val:.6f}")
# --------------------- 6. 绘制3D收敛轨迹图 ---------------------
fig = plt.figure(figsize=(12, 10))
ax = fig.add_subplot(111, projection='3d')
# 定义7种颜色(区分不同优化器)
colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'purple']
markers = ['o', 's', '^', 'D', 'v', '<', '>'] # 轨迹点标记(可选,增强区分度)
# 绘制每个优化器的3D轨迹
for idx, (traj, name, color, marker) in enumerate(zip(trajectories, optimizer_names, colors, markers)):
w1, w2, loss = traj
# 绘制轨迹线(alpha=0.8:透明度,linewidth=2:线宽)
ax.plot(w1, w2, loss, color=color, label=name, linewidth=2, alpha=0.8)
# 绘制轨迹起点(红色标记,突出初始位置)
ax.scatter(w1[0], w2[0], loss[0], color='red', s=50, marker='*', zorder=10)
# 绘制轨迹终点(绿色标记,突出收敛位置)
ax.scatter(w1[-1], w2[-1], loss[-1], color='green', s=50, marker='*', zorder=10)
# 标记真实权重对应的“最优解点”(w1=3, w2=-5,loss≈0.25:噪声方差的平方)
ax.scatter(3.0, -5.0, 0.25, color='black', s=100, marker='x', label='True Optimum', zorder=15)
# 设置3D图标签和标题
ax.set_xlabel('Weight w1', fontsize=12)
ax.set_ylabel('Weight w2', fontsize=12)
ax.set_zlabel('MSE Loss', fontsize=12)
ax.set_title('7种优化器的3D收敛轨迹对比(w1, w2, Loss)', fontsize=14, pad=20)
# 添加图例(放在图外侧,避免遮挡轨迹)
ax.legend(loc='upper left', bbox_to_anchor=(1.05, 1), fontsize=10)
# 调整视角(让轨迹更清晰)
ax.view_init(elev=20, azim=45) # elev:仰角,azim:方位角
plt.tight_layout()
plt.show()
# --------------------- 7. 补充:2D俯视图(w1-w2平面,更易看参数收敛方向) ---------------------
plt.figure(figsize=(10, 8))
for idx, (traj, name, color) in enumerate(zip(trajectories, optimizer_names, colors)):
w1, w2, _ = traj
plt.plot(w1, w2, color=color, label=name, linewidth=2, alpha=0.8)
plt.scatter(w1[0], w2[0], color='red', s=30, marker='*') # 起点
plt.scatter(w1[-1], w2[-1], color='green', s=30, marker='*') # 终点
# 标记真实最优权重
plt.scatter(3.0, -5.0, color='black', s=80, marker='x', label='True Optimum')
plt.xlabel('Weight w1', fontsize=12)
plt.ylabel('Weight w2', fontsize=12)
plt.title('7种优化器的参数收敛轨迹(2D俯视图:w1 vs w2)', fontsize=14)
plt.legend()
plt.grid(True, linestyle='--', alpha=0.7)
plt.axis('equal') # 保证w1和w2轴刻度一致,轨迹无畸变
plt.show()
按模型复杂度
选取判断方式:
- 数据是否稀疏?→ 是→Adagrad,否→Adam/RMSProp;
- 模型是否复杂?→ 是→Adam/NAG,否→SGD/Adadelta;
- 能否接受调参?→ 否→Adadelta/Adam(默认),是→NAG/SGD(精细调参)。
本文是笔者在学习梯度下降算法的时候,所整理这7种算法之间的联系和相应的区别,供自己学习和总结,希望能给大家带来帮助,更好的理解这些常见的梯度下降算法,后续有新的总结会持续更新,如果大家发现有问题或错误,欢迎大家指出!感谢!