深度学习26:PyTorch:参数更新方法(Momentum)

目录标题

一、Momentum

Momentum(动量法)会保存历史梯度并给予一定的权重,使其也参与到参数更新中:

V ← α V − η ∇ V \leftarrow \alpha V - \eta \nabla V←αV−η∇

W ← W + V W \leftarrow W + V W←W+V

V V V:历史(负)梯度的加权和
α \alpha α:历史梯度的权重
∇ \nabla ∇:当前梯度,即 ∂ L ∂ W \frac{\partial L}{\partial W} ∂W∂L
η \eta η:学习率

可以通过 torch.optim.SGD() 并设置 momentum 历史梯度权重参数来使用动量法。

以寻找 f ( x 1 , x 2 ) = 0.05 x 1 2 + x 2 2 f(x_1, x_2) = 0.05x_1^2 + x_2^2 f(x1,x2)=0.05x12+x22 的最小值为例:

python 复制代码
import torch
import numpy as np
import matplotlib.pyplot as plt

#这是在函数上找一个最小点,   二不是找到一组权重 w,使得在未知数据上的预测最准

# 定义函数
def f(X):
    return 0.05 * X[0] ** 2 + X[1] ** 2


# 定义函数,实现梯度下降法
def gradient_descent(X, optimizer, num_iters):
    # 拷贝当前X的值,放入列表中
    X_arr = X.detach().numpy().copy()
    for i in range(num_iters):
        # 1. 前向传播,得到"损失值"
        y = f(X)
        # 2. 反向传播
        y.backward()
        # 3. 更新参数
        optimizer.step()
        # 4. 梯度清零
        optimizer.zero_grad()

        # 将更新之后的X,保存到列表中
        X_arr = np.vstack((X_arr, X.detach().numpy()))
    return X_arr


# 主流程
# 1. 参数X初始化
X = torch.tensor(data=[-7.0, 2.0], requires_grad=True)

# 2. 定义超参数
lr = 0.01
num_iters = 500

# 3. 优化器对比
# 3.1 SGD
X_clone = X.clone().detach().requires_grad_(True)
optimizer = torch.optim.SGD(params=[X_clone], lr=lr)

# 梯度下降
X_arr1 = gradient_descent(X_clone, optimizer, num_iters)
# 画出点轨迹
plt.plot(X_arr1[:, 0], X_arr1[:, 1], 'r')

# 3.2 动量法
X_clone = X.clone().detach().requires_grad_(True)
optimizer = torch.optim.SGD(params=[X_clone], lr=lr, momentum=0.9)

# 梯度下降
X_arr2 = gradient_descent(X_clone, optimizer, num_iters)
# 画出点轨迹
plt.plot(X_arr2[:, 0], X_arr2[:, 1], 'b')

# 画出等高线
x1_grid, x2_grid = np.meshgrid(np.linspace(-7, 7, 100), np.linspace(-2, 2, 100))
y_grid = 0.05 * x1_grid ** 2 + x2_grid ** 2
plt.contour(x1_grid, x2_grid, y_grid, levels=30, colors='gray')
plt.legend(["SGD", "Momentum"])

# plt.show()
plt.savefig('momentum_result.png')

效果图:

二、理解上面代码

参考blog 机器学习:线性回归3(正规方程法和梯度下降法) 里面的 三、梯度下降中的一元线性回归-梯度下降法 (当初我也用这个和一元线性回归的梯度下降法和真实多元梯度下降对比)

在真正的机器学习(深度学习)中,损失函数确实是计算"预测值"与"真实标签(Ground Truth)"之间的差距(比如均方误差 MSE、交叉熵等)

但在这段代码中,并没有真实的训练数据,也没有神经网络模型。它其实是在演示一个数学优化问题(寻找函数极小值)

原来blog起始点是 (1,1) 导数(梯度)是2Θ

这个上面代码起始点是 和导数(梯度) 都是 X = torch.tensor(data=-7.0, 2.0, requires_grad=True)

上面代码就是参考 blog 里的逻辑,但是这里我还要分析一遍

下面为你详细解释为什么这里没有数据,以及它和真实机器学习的区别:

1. 为什么这里可以直接用函数上的一个点?

这段代码演示的是最基础的梯度下降(Gradient Descent)物理过程。

  • 你可以把函数 f ( x ) = 0.05 ∗ X 0 2 + X 1 2 f(x) = 0.05 * X0^2 + X1^2 f(x)=0.05∗X02+X12 想象成一个真实的碗(或者山谷)。
  • 参数 X(初始在 [-7.0, 2.0])就像是一个放在碗壁上的玻璃珠。
  • 梯度(Gradient):就是这个位置的坡度(倾斜方向)。
  • 梯度下降:就是让玻璃珠顺着坡度最陡的地方往下滚。

在这个"滚下山"的过程中,你不需要任何数据集。你唯一需要的就是:当前位置(X) ,以及当前位置的坡度(通过 y.backward() 算出来)。所以它确实只是在用函数上的点不断迭代。

2. 如果换成真实的机器学习,它会是什么样?

假设我们要训练一个神经网络来预测房价。代码的逻辑就会变成这样:

python 复制代码
# 1. 有真实数据了!
# X_train 是房子面积,y_true 是真实房价
X_train = torch.tensor([50.0, 80.0, 100.0])
y_true = torch.tensor([100.0, 150.0, 200.0])

# 2. 有模型了!
# w 是权重参数(相当于上面代码里的 X)
w = torch.tensor([1.0], requires_grad=True) 

# 3. 前向传播:计算预测值
y_pred = w * X_train  # 预测房价 = 权重 * 面积

# 4. 计算损失(这里才体现出"预测值减真实值")
# 比如用均方误差 (MSE)
loss = torch.mean((y_pred - y_true) ** 2) 

# 5. 反向传播
loss.backward()

# 6. 更新参数(优化器更新的是权重 w,而不是数据)
optimizer.step()
optimizer.zero_grad()

3. 对比一下两者的逻辑:

对比项 提供的代码(函数优化演示) 真实的机器学习训练
参数(被更新的对象) 坐标 X (X[0], X[1]) 模型权重 w / b (神经网络的神经元参数)
损失函数计算什么? 当前坐标 X 在空间里的高度/函数值 模型预测出的 y_pred 与真实标签 y_true 的差距
前向传播 把 X 代入公式 f(X) 把真实数据 X_train 喂给模型算 y_pred
优化目标 找到坐标系里函数值最小的点 (0,0) 找到一组权重 w,使得在未知数据上的预测最准
应用场景 纯粹的数学/可视化教学演示 图像识别、自然语言处理等实际工程

三、X_clone = X.clone().detach().requires_grad_(True)

这行代码是 PyTorch 中非常经典且严谨的写法,通常出现在需要对一个张量重新开启梯度追踪,但又不想影响原张量的场景中(比如写自定义优化器、或者手动实现梯度下降

1. x.clone():克隆(复制一份数据)

  • 作用: 在内存中复制一份和 x 一模一样的新数据。
  • 为什么需要: 如果你直接用 x,后续的修改会影响到原来的变量。clone() 保证了新变量和原变量是完全独立的,修改 x_clone 不会影响 x。

2. .detach():脱离计算图(断开追踪)

  • 作用: 把克隆出来的张量从当前的计算图中剥离出来。
  • 为什么需要: 如果你直接用 x.clone(),这个新张量依然会记录着它是怎么从 x 计算出来的(带有 grad_fn)。如果你之后想把它当作一个全新的、独立的变量来用,就必须用 detach() 切断它和过去的联系,把它变成一个普通的叶子节点(Leaf Tensor)。

3. .requires_grad_(True):重新开启梯度追踪

  • 作用: 强制让这个新张量可以计算梯度。
  • 为什么需要: detach() 之后,这个张量默认是不需要求导的(requires_grad=False)。但这行代码的目的是为了重新训练或计算,所以必须加个下划线 _(这是原地修改方法),强制把它设为 True。
相关推荐
Raas1001 小时前
MAI Gateway实战部署:AI网关怎么部署?从0到1上手指南
人工智能·gateway·ai网关·企业级ai网关·企业级产品·ai技术交流
搭贝1 小时前
【无标题】
人工智能·低代码·ai·1024程序员节·搭贝
Zack_Liu1 小时前
code样例
pytorch·python·深度学习
lucas_AI1 小时前
万亿参数「大胖猫」上秤了:Mistral 如期放出 Large 4 权重,但想拿它做生意先看许可证
人工智能
aneasystone本尊1 小时前
学习大模型训练的分布式并行策略
人工智能
mudtools1 小时前
在飞书 SDK 上用 Microsoft Agent Framework 搭一个能干活的工作搭子
人工智能·ai·.net·飞书·agent
数治研习社1 小时前
数据治理第十九篇|DAMA 十一个核心数据管理领域:从信息化到智能化,用三层主线读懂
大数据·人工智能
海盗12341 小时前
微软技术日报 2026-10-29:Copilot harness 进 Foundry,云重建能拉镜像
人工智能·microsoft·机器人·aigc·copilot
论文复现现场1 小时前
BEVFormer训练需要几张GPU?2张A100 80GB够不够,显存与云GPU成本分析
pytorch·自动驾驶·a100·bevformer