训练流程中的功能组件 - 优化器
·
训练流程中的功能组件 - 优化器
flyfish
真实规律是 y = 2x + 3,我们给数据加一点噪声。模型只有 2 个可学习参数:权重 w 和偏置 b。训练的目标就是让模型自动学到 w,b
优化器主要是接收反向传播算出的梯度,按照自身算法规则更新模型的可学习参数(权重、偏置),驱动损失函数持续下降。
import torch
# --------------------------
# 1. 准备训练数据
# --------------------------
# 生成100个x样本,从0到10均匀分布
x = torch.linspace(0, 10, 100).reshape(-1, 1)
# 真实值 y = 2x + 3,加一点随机噪声模拟真实数据
y_true = 2 * x + 3 + torch.randn_like(x) * 0.5
# --------------------------
# 2. 初始化可学习参数 + 超参数
# --------------------------
# 随机初始化w和b,requires_grad=True 表示需要对它们求梯度
w = torch.randn(1, requires_grad=True)
b = torch.randn(1, requires_grad=True)
learning_rate = 0.01 # 学习率
epochs = 200 # 训练总轮数
# --------------------------
# 3. 训练循环(手动实现优化器)
# --------------------------
for epoch in range(epochs):
# 1前向传播:输入x,算出预测值
y_pred = w * x + b
#2 计算损失:均方误差(预测值和真实值的差的平方,取平均)
loss = torch.mean((y_pred - y_true) ** 2)
# 3 反向传播:自动算出w和b的梯度,存在 w.grad 和 b.grad 里
loss.backward()
# ==================================================
# 这里就是优化器的逻辑
# ==================================================
# 关闭梯度追踪,只做纯数值更新,避免生成计算图浪费资源
with torch.no_grad():
# 核心更新公式:新参数 = 旧参数 - 学习率 × 梯度
w -= learning_rate * w.grad
b -= learning_rate * b.grad
# 梯度清零:把w.grad和b.grad重置为0,避免下一轮累加旧梯度
w.grad.zero_()
b.grad.zero_()
# ==================================================
# 每20轮打印一次结果
if (epoch + 1) % 40 == 0:
print(f"轮次 {epoch+1:3d} | 损失: {loss.item():.4f} | w={w.item():.4f} | b={b.item():.4f}")
# 训练结束看最终结果
print(f"\n训练完成:最终w={w.item():.4f},最终b={b.item():.4f}")
print("真实值:w=2,b=3")
代码中,with torch.no_grad() 里的两行 + 梯度清零,是最基础的随机梯度下降(SGD)优化器的功能。
它做两件事:
-
参数更新(对应PyTorch的
optimizer.step())
按照固定公式新参数 = 旧参数 - 学习率 × 梯度,逐个更新所有可学习参数。
梯度告诉我们参数往哪个方向调能让损失变小,学习率控制调多大幅度。 -
梯度清零(对应PyTorch的
optimizer.zero_grad())
PyTorch 默认会累加梯度,每轮更新完必须手动清零,否则下一轮的梯度会叠加上旧值,导致更新完全错误。
优化器就是参数更新规则的封装。
使用PyTorch自带的torch.optim实现
import torch
# 数据和参数初始化和上面完全一样
x = torch.linspace(0, 10, 100).reshape(-1, 1)
y_true = 2 * x + 3 + torch.randn_like(x) * 0.5
w = torch.randn(1, requires_grad=True)
b = torch.randn(1, requires_grad=True)
learning_rate = 0.01
epochs = 200
# SGD优化器:把所有参数交给它管理
optimizer = torch.optim.SGD([w, b], lr=learning_rate)
for epoch in range(epochs):
y_pred = w * x + b
loss = torch.mean((y_pred - y_true) ** 2)
# 对应手动的 w.grad.zero_(); b.grad.zero_()
optimizer.zero_grad()
# 反向传播算梯度
loss.backward()
# 对应手动的 w -= lr*w.grad; b -= lr*b.grad
optimizer.step()
if (epoch + 1) % 40 == 0:
print(f"轮次 {epoch+1:3d} | 损失: {loss.item():.4f} | w={w.item():.4f} | b={b.item():.4f}")
省去了逐个参数手写更新、逐个清零的麻烦
可以把epoch加大看效果
轮次 40 | 损失: 0.8172 | w=2.2211 | b=1.5349
轮次 80 | 损失: 0.6443 | w=2.1822 | b=1.7936
轮次 120 | 损失: 0.5282 | w=2.1504 | b=2.0055
轮次 160 | 损失: 0.4503 | w=2.1243 | b=2.1792
轮次 200 | 损失: 0.3979 | w=2.1029 | b=2.3215
在单轮训练迭代中,优化器的执行逻辑:
- 接收梯度输入:获取由反向传播算法计算得到的损失函数对各参数的一阶导数(梯度)∇L(θ)\nabla L(\boldsymbol{\theta})∇L(θ),该信息指明了参数空间中损失上升最快的方向与速率。
- 计算参数更新量:基于当前梯度、历史梯度状态与预设超参数,计算每个参数的更新方向与步长,生成参数更新向量Δθ\Delta\boldsymbol{\theta}Δθ。这是优化器的核心功能,不同优化算法的差异均体现于此:
基础随机梯度下降(SGD)仅以全局学习率对梯度做线性缩放,即Δθ=−η⋅∇L(θ)\Delta\boldsymbol{\theta} = -\eta \cdot \nabla L(\boldsymbol{\theta})Δθ=−η⋅∇L(θ),所有参数共享统一的步长缩放系数;
自适应优化器(如Adam)会额外维护每个参数的梯度一阶矩(动量)与二阶矩(波动幅度)统计量,为每个参数动态适配更新步长,在梯度波动剧烈的维度收窄步长以抑制震荡,在梯度平缓的维度放大步长以加速收敛。 - 执行参数更新与状态维护:按照θ←θ+Δθ\boldsymbol{\theta} \leftarrow \boldsymbol{\theta} + \Delta\boldsymbol{\theta}θ←θ+Δθ更新所有可学习参数的数值;同时清零参数的梯度缓存,避免迭代间梯度累加导致更新偏差,并更新优化算法所需的历史状态变量,供后续迭代使用。
更多推荐

所有评论(0)