70·深度学习入门进阶

PyTorch 基础:Tensor 与自动求导

pytorchframework

第 70 课 - PyTorch 基础:Tensor 与自动求导

学习目标

通过本课学习,你将能够:

  1. 理解 PyTorch 中 Tensor 的基本概念及其在深度学习中的核心地位
  2. 掌握 Tensor 的创建、操作和基本数学运算
  3. 理解自动求导机制(autograd)的工作原理
  4. 能够使用 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 的随机矩阵,然后完成以下操作:

  1. 计算矩阵每一行的平均值
  2. 找出矩阵中的最大值和最小值
  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 ) 时:

  1. 计算函数值
  2. 计算关于 ( x ) 和 ( y ) 的偏导数
  3. 验证结果是否正确
# 你的代码

预期输出:

函数值 f(2, 3) = 30.0
∂f/∂x = 39.0
∂f/∂y = 40.0
验证: 数值法计算 ∂f/∂x ≈ 39.0001

练习 3:线性回归扩展(难)

在简单线性回归的基础上,扩展实现多特征线性回归:

  1. 生成包含 3 个特征的数据集(100 个样本)
  2. 实现多特征线性回归模型
  3. 训练模型并评估性能
# 你的代码

预期输出:

学习到的权重: 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

小结

关键要点回顾:

  1. Tensor 基础:Tensor 是 PyTorch 的核心数据结构,支持 GPU 加速和自动求导
  2. 自动求导:通过 requires_grad=True 启用,backward() 计算梯度,grad.zero_() 清零
  3. 计算图:PyTorch 动态构建计算图,记录所有操作以支持反向传播
  4. 实践应用:从简单的函数求导到线性回归,Tensor 和自动求导是深度学习的基石

核心概念图:

Tensor 操作 → 构建计算图 → 反向传播 → 获取梯度 → 更新参数
     ↑              ↑              ↑
   创建数据       前向传播       loss.backward()

下一课预告:

下一课我们将学习卷积神经网络(CNN)基础,了解如何使用 PyTorch 构建和训练 CNN 模型,这是处理图像数据的关键技术。

练习编辑器

rust
Loading...

继续学习

完成本课后,建议继续学习下一课「卷积神经网络(CNN)基础」 以巩固所学知识。