第 70 课 - PyTorch 基础:Tensor 与自动求导
学习目标
通过本课学习,你将能够:
- 理解 PyTorch 中 Tensor 的基本概念及其在深度学习中的核心地位
- 掌握 Tensor 的创建、操作和基本数学运算
- 理解自动求导机制(autograd)的工作原理
- 能够使用 PyTorch 实现简单的线性回归模型并进行梯度计算
核心概念
1. Tensor:深度学习的数据基础
Tensor(张量)是 PyTorch 中的基本数据结构,类似于 NumPy 的数组,但有两个关键优势:
- 可以在 GPU 上加速计算
- 自动支持微分运算
你可以把 Tensor 想象成:
- 0维:标量(一个数字)
- 1维:向量(一行或一列数字)
- 2维:矩阵(表格数据)
- 更高维:多维数组(如图像数据是3维:高度×宽度×通道)
2. 自动求导:深度学习的魔法
自动求导是 PyTorch 的核心功能,它能自动计算导数。当设置 requires_grad=True 时,PyTorch 会跟踪所有对该 Tensor 的操作,构建计算图,然后通过反向传播计算梯度。
代码示例
1. Tensor 基础操作
import torch
# 创建 Tensor
# 1. 从 Python 列表创建
x = torch.tensor([1, 2, 3, 4, 5])
print(f"1D Tensor: {x}")
# 2. 创建特殊 Tensor
zeros = torch.zeros(3, 4) # 3行4列的全0矩阵
ones = torch.ones(2, 3) # 2行3列的全1矩阵
random = torch.rand(3, 3) # 3x3 随机矩阵
print(f"全0矩阵:\n{zeros}\n")
# 3. Tensor 属性
print(f"形状: {x.shape}") # 形状
print(f"数据类型: {x.dtype}") # 数据类型
print(f"设备: {x.device}") # 计算设备(CPU/GPU)
# 4. 数学运算
a = torch.tensor([1, 2, 3], dtype=torch.float32)
b = torch.tensor([4, 5, 6], dtype=torch.float32)
# 逐元素运算
print(f"加法: {a + b}")
print(f"乘法: {a * b}")
print(f"幂运算: {a ** 2}")
# 矩阵运算
c = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32)
d = torch.tensor([[5, 6], [7, 8]], dtype=torch.float32)
print(f"矩阵乘法:\n{c @ d}") # 或 torch.matmul(c, d)
2. 自动求导实战
import torch
# 创建需要梯度的 Tensor
x = torch.tensor(2.0, requires_grad=True)
print(f"x = {x}, requires_grad = {x.requires_grad}")
# 定义函数 y = x^2 + 2x + 1
y = x**2 + 2*x + 1
print(f"y = {y}")
# 反向传播计算梯度
y.backward()
# 导数 dy/dx = 2x + 2 = 2*2 + 2 = 6
print(f"dy/dx = {x.grad}")
# 注意:梯度默认会累积
x.grad.zero_() # 清零梯度
# 多变量自动求导
x = torch.tensor(3.0, requires_grad=True)
w = torch.tensor(4.0, requires_grad=True)
b = torch.tensor(5.0, requires_grad=True)
# 线性函数: y = w*x + b
y = w * x + b
print(f"y = {y}")
y.backward()
print(f"dy/dx = {x.grad}") # 对 x 的梯度 = w = 4
print(f"dy/dw = {w.grad}") # 对 w 的梯度 = x = 3
print(f"dy/db = {b.grad}") # 对 b 的梯度 = 1
3. 简单线性回归示例
import torch
import matplotlib.pyplot as plt
# 生成模拟数据
torch.manual_seed(42)
X = torch.rand(100, 1) * 10 # 100个样本,特征在0-10之间
y_true = 2 * X + 3 + torch.randn(100, 1) # 真实关系: y = 2x + 3 + 噪声
# 初始化参数
w = torch.randn(1, requires_grad=True)
b = torch.zeros(1, requires_grad=True)
# 训练参数
learning_rate = 0.01
num_epochs = 100
# 训练循环
losses = []
for epoch in range(num_epochs):
# 前向传播
y_pred = X @ w + b # 线性回归预测
# 计算均方误差损失
loss = torch.mean((y_pred - y_true) ** 2)
losses.append(loss.item())
# 反向传播
loss.backward()
# 更新参数(不需要在计算图中跟踪)
with torch.no_grad():
w -= learning_rate * w.grad
b -= learning_rate * b.grad
# 梯度清零
w.grad.zero_()
b.grad.zero_()
print(f"学习到的参数: w = {w.item():.3f}, b = {b.item():.3f}")
print(f"真实参数: w = 2.000, b = 3.000")
# 可视化损失
plt.plot(losses)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training Loss')
plt.show()
实践练习
练习 1:基础 Tensor 操作(易)
创建一个 3×3 的随机矩阵,然后完成以下操作:
- 计算矩阵每一行的平均值
- 找出矩阵中的最大值和最小值
- 将矩阵的所有元素乘以 2,然后加上 5
# 你的代码
预期输出示例:
原始矩阵:
tensor([[0.1234, 0.5678, 0.9012],
[0.3456, 0.7890, 0.2345],
[0.6789, 0.1234, 0.5678]])
行平均值: tensor([0.5308, 0.4564, 0.4567])
最大值: 0.9012, 最小值: 0.1234
变换后的矩阵:
tensor([[ 5.2468, 6.1356, 6.8024],
[ 5.6912, 6.5780, 5.4690],
[ 6.3578, 5.2468, 6.1356]])
练习 2:自动求导应用(中)
给定函数:( f(x, y) = x^2 \cdot y + 3x \cdot y^2 ) 当 ( x = 2 ), ( y = 3 ) 时:
- 计算函数值
- 计算关于 ( x ) 和 ( y ) 的偏导数
- 验证结果是否正确
# 你的代码
预期输出:
函数值 f(2, 3) = 30.0
∂f/∂x = 39.0
∂f/∂y = 40.0
验证: 数值法计算 ∂f/∂x ≈ 39.0001
练习 3:线性回归扩展(难)
在简单线性回归的基础上,扩展实现多特征线性回归:
- 生成包含 3 个特征的数据集(100 个样本)
- 实现多特征线性回归模型
- 训练模型并评估性能
# 你的代码
预期输出:
学习到的权重: w = [1.98, 2.02, 0.99]
学习到的偏置: b = 4.02
真实参数: w = [2.0, 2.0, 1.0], b = 4.0
训练最终损失: 0.98
常见错误
1. 忘记清零梯度
# 错误示例
for i in range(100):
loss = compute_loss()
loss.backward()
optimizer.step()
# 忘记了 optimizer.zero_grad() 或手动清零梯度
# 这会导致梯度累积,训练不稳定
正确做法:
for i in range(100):
optimizer.zero_grad() # 先清零梯度
loss = compute_loss()
loss.backward()
optimizer.step()
2. 不需要梯度的操作时忘记禁用
# 错误:在测试时仍然计算梯度
x = torch.randn(5, requires_grad=True)
# 训练循环中没问题,但测试时...
test_output = model(x) # 如果 x 需要梯度,会创建不必要的计算图
正确做法:
with torch.no_grad(): # 或使用 torch.inference_mode()
test_output = model(x)
3. Tensor 形状不匹配
# 错误示例
a = torch.rand(3, 4)
b = torch.rand(5, 4)
result = a @ b # 矩阵乘法形状不匹配: (3,4) @ (5,4) -> 错误!
正确做法:
# 确保矩阵乘法形状兼容: (m,n) @ (n,p) -> (m,p)
a = torch.rand(3, 4)
b = torch.rand(4, 5)
result = a @ b # (3,4) @ (4,5) -> (3,5) ✓
4. 混淆 Tensor 和 NumPy
# 错误:在需要梯度的 Tensor 上使用 NumPy 操作
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
# x_numpy = x.numpy() # 这会报错!需要先 .detach()
正确做法:
x_numpy = x.detach().numpy() # 分离计算图,然后转为 NumPy
小结
关键要点回顾:
- Tensor 基础:Tensor 是 PyTorch 的核心数据结构,支持 GPU 加速和自动求导
- 自动求导:通过
requires_grad=True启用,backward()计算梯度,grad.zero_()清零 - 计算图:PyTorch 动态构建计算图,记录所有操作以支持反向传播
- 实践应用:从简单的函数求导到线性回归,Tensor 和自动求导是深度学习的基石
核心概念图:
Tensor 操作 → 构建计算图 → 反向传播 → 获取梯度 → 更新参数
↑ ↑ ↑
创建数据 前向传播 loss.backward()
下一课预告:
下一课我们将学习卷积神经网络(CNN)基础,了解如何使用 PyTorch 构建和训练 CNN 模型,这是处理图像数据的关键技术。
练习编辑器
rust
Loading...