一、Autograd机制:PyTorch的核心魔法
1. 什么是自动求导?
PyTorch的autograd
模块能自动计算张量的梯度,这是训练神经网络的核心功能。
只需设置requires_grad=True
,PyTorch会跟踪所有相关操作,构建计算图,并通过反向传播计算梯度。
import torch
# 创建需要梯度的张量
x = torch.tensor(2.0, requires_grad=True)
w = torch.tensor(3.0, requires_grad=True)
b = torch.tensor(1.0, requires_grad=True)
# 前向计算
y = w * x + b # y = 3*2 + 1 = 7
# 反向传播自动计算梯度
y.backward() # 计算dy/dx, dy/dw, dy/db
print("梯度值:")
print(f"dy/dx = {x.grad}") # 3.0 (即w的值)
print(f"dy/dw = {w.grad}") # 2.0 (即x的值)
print(f"dy/db = {b.grad}") # 1.0
2. 计算图原理
PyTorch会动态构建计算图,图中节点是张量,边是操作(如加法、乘法)。
调用.backward()
时,从当前张量(通常是损失值)反向遍历整个图,计算所有叶节点的梯度。
二、梯度计算实战:手动实现线性回归
1. 生成模拟数据
import matplotlib.pyplot as plt
# 设置随机种子保证可重复性
torch.manual_seed(42)
# 生成数据 y = 2x + 1 + 噪声
x = torch.linspace(0, 5, 100).reshape(-1, 1)
true_w = 2.0
true_b = 1.0
y = true_w * x + true_b + torch.randn(x.shape) * 0.8
# 可视化数据
plt.scatter(x.numpy(), y.numpy(), alpha=0.6)
plt.xlabel("x")
plt.ylabel("y")
plt.title("线性回归数据集")
plt.show()
2. 定义模型和损失函数
# 初始化可训练参数
w = torch.randn(1, requires_grad=True)
b = torch.zeros(1, requires_grad=True)
def linear_model(x):
return w * x + b
def mse_loss(y_pred, y_true):
return torch.mean((y_pred - y_true) ** 2)
3. 手动实现梯度下降
# 超参数设置
learning_rate = 0.01
epochs = 200
# 训练循环
loss_history = []
for epoch in range(epochs):
# 前向传播
y_pred = linear_model(x)
loss = mse_loss(y_pred, y)
# 反向传播
loss.backward()
# 手动更新参数(重要:必须在no_grad()上下文中操作)
with torch.no_grad():
w -= learning_rate * w.grad
b -= learning_rate * b.grad
# 清零梯度!否则梯度会累积
w.grad.zero_()
b.grad.zero_()
# 记录损失
loss_history.append(loss.item())
# 可视化训练结果
plt.plot(loss_history)
plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.title("损失下降曲线")
plt.show()
# 打印最终参数
print(f"真实参数: w={true_w}, b={true_b}")
print(f"训练结果: w={w.item():.2f}, b={b.item():.2f}")
# 绘制拟合直线
with torch.no_grad():
plt.scatter(x, y, alpha=0.6)
plt.plot(x, linear_model(x), c='red', linewidth=3)
plt.show()
三、用优化器自动化参数更新
1. 使用SGD优化器
PyTorch提供torch.optim
模块简化参数更新流程:
# 重新初始化参数
w = torch.randn(1, requires_grad=True)
b = torch.zeros(1, requires_grad=True)
# 定义优化器
optimizer = torch.optim.SGD([w, b], lr=learning_rate)
# 训练循环(仅展示不同部分)
for epoch in range(epochs):
optimizer.zero_grad() # 替代手动清零梯度
y_pred = linear_model(x)
loss = mse_loss(y_pred, y)
loss.backward()
optimizer.step() # 自动更新所有参数
2. 不同优化器对比
尝试更换优化器(如Adam),观察收敛速度变化:
optimizer = torch.optim.Adam([w, b], lr=0.1) # 通常更大的学习率
四、常见问题与调试技巧
Q1:为什么需要调用zero_grad()?
- 梯度默认会累积,而不是覆盖。如果不清零,多次
.backward()
会导致梯度叠加。 - 在RNN等需要梯度累积的场景中,可以故意不清零。
Q2:遇到梯度爆炸/消失怎么办?
- 检查学习率是否过大(将学习率调至0.001试试)
- 使用梯度裁剪:
torch.nn.utils.clip_grad_norm_([w, b], max_norm=1.0)
Q3:如何调试梯度计算?
# 检查梯度是否存在
print(w.grad) # 如果为None,可能是未调用backward或requires_grad未设置
# 打印中间值梯度
print(x.grad)
五、小结与下篇预告
-
关键知识点:
requires_grad=True
开启自动求导loss.backward()
反向传播计算梯度optimizer.step()
和optimizer.zero_grad()
的配合使用
-
下篇预告:
第三篇将深入讲解如何用nn.Module
构建复杂神经网络,并实战FashionMNIST分类任务!