当前位置: 首页 > news >正文

PyTorch 核心三件套:Tensor、Module、Autograd

欢迎来到啾啾的博客🐱。
记录学习点滴。分享工作思考和实用技巧,偶尔也分享一些杂谈💬。
有很多很多不足的地方,欢迎评论交流,感谢您的阅读和评论😄。

目录

  • 引言
  • 1 Tensor
    • 1.1 🛠️Tensor 的核心用途(5大场景)
    • 1.2 表示现实世界数据|输入数据
    • 1.3 表示标签(Labels / Targets)
    • 1.4 存储模型参数(Weights & Biases)
    • 1.5 中间计算结果(Forward Pass)
    • 1.6 梯度计算(Backward Pass)
    • 1.7 📚 常用 API 速查表
    • 1.8 💡 Tensor 演示
  • 2 Module
    • 2.1 简介
    • 2.2 创建Module
    • 2.3 Module 的核心操作
  • 3 Autograd
    • 3.1 简介
    • 3.2 梯度追踪
    • 3.3 反向传播
    • 3.4 禁用梯度计算
  • 4 📌 三件套关系图解
  • 5 🌟 三件套协同工作流程(完整训练示例)
    • 5.1 Demo:三件套简单串联
    • 5.2 构建一个简单线性回归模型 y=wx+b
    • 5.3 Demo:搭建一个简单的多层感知机
  • 刻意练习

引言

本篇将讲述PyTorch的核心“三件套”:Tensor, Module, Autograd。
尝试以后端岗位工程应用角度来理解、串联起这个三个概念。

AI使用声明:本文内容由作者基于对深度学习和PyTorch框架的学习与理解撰写。在内容整理、结构优化和语言表达的过程中,我使用了人工智能(AI)工具作为辅助。

资料:
《深入浅出PyTorch 第二章节》
《动手学深度学习第四章》

1 Tensor

让我们思考一个问题,我们要怎么把现实世界的数据变成计算机能处理的数字形式呢?

通过Tensor,张量,它是现代机器学习的基础。

n维数组,也称为_张量_(tensor)。

几何代数中定义的张量是基于向量和矩阵的推广,比如我们可以将标量视为零阶张量,矢量可以视为一阶张量,矩阵就是二阶张量。

张量维度代表含义示例
0D标量(Scalar)torch.tensor(3.14)
1D向量(Vector)[1, 2, 3]
2D矩阵(Matrix)图像展平后的特征
3D序列/单图(C, H, W) 彩色图像
4D批量图像(B, C, H, W)

在PyTorch中, torch.Tensor 是存储和变换数据的主要工具,比起NumPy更适合深度学习。

1.1 🛠️Tensor 的核心用途(5大场景)

1.2 表示现实世界数据|输入数据

现实世界常见数据的Tensor示例如下:

数据类型Tensor形状示例说明
图像(3, 224, 224)RGB 三通道图像
批量图像(64, 3, 224, 224)一次处理64张图
文本(1, 512)512个词的编码序列
音频(1, 16000)1秒音频(16kHz采样)
# 图像转 Tensor(使用 torchvision)
from torchvision import transforms
transform = transforms.ToTensor()
image_tensor = transform(pil_image)  # PIL图像 → Tensor

1.3 表示标签(Labels / Targets)

可以告诉模型“正确答案”。

# 分类任务:类别标签
labels = torch.tensor([3, 1, 7, 0])  # 4个样本的真实类别# 回归任务:连续值
targets = torch.tensor([1.5, 2.3, 0.8])# 语义分割:每个像素的类别
mask = torch.randint(0, 20, (1, 256, 256))  # (C,H,W)

1.4 存储模型参数(Weights & Biases)

神经网络的“记忆”就存在 Tensor 里。

linear = torch.nn.Linear(10, 5)
print(linear.weight.shape)  # torch.Size([5, 10]) ← 这是个 Tensor!
print(linear.bias.shape)    # torch.Size([5])     ← 这也是 Tensor!

这些参数 Tensor 会:

  • 在训练中不断更新(梯度下降)
  • 决定模型的预测能力
  • 可以保存和加载(.pth 文件)

1.5 中间计算结果(Forward Pass)

网络每一层的输出都是 Tensor 。

x = torch.randn(1, 784)          # 输入
w = torch.randn(784, 256)        # 权重
b = torch.zeros(256)             # 偏置# 每一步都是 Tensor 运算
z = x @ w + b                    # 线性变换
a = torch.relu(z)                # 激活函数
# a 仍然是 Tensor,传给下一层

1.6 梯度计算(Backward Pass)

Autograd 用 Tensor 记录梯度 。

x = torch.tensor(2.0, requires_grad=True)
y = x ** 2
y.backward()  # 计算 dy/dx
print(x.grad)  # tensor(4.) ← 梯度也存在 Tensor 中!

1.7 📚 常用 API 速查表

类别API说明示例
创建torch.tensor()通用创建x = torch.tensor([1,2,3])
torch.randn()随机正态分布x = torch.randn(2,3)
torch.zeros()全零张量x = torch.zeros(4,4)
转换.numpy()→ NumPy 数组arr = x.numpy()
torch.from_numpy()← NumPy 数组x = torch.from_numpy(arr)
.to('cuda')→ GPUx_gpu = x.to('cuda')
运算+,-,*,/基础运算z = x + y
torch.matmul()矩阵乘法z = torch.matmul(x,y)
x.view()形状变换x_2d = x.view(2,3)
自动求导.requires_grad_()开启梯度追踪x.requires_grad_()
.detach()剥离梯度计算x_no_grad = x.detach()

1.8 💡 Tensor 演示

import torch  # 1. 创建张量(开启梯度追踪)  
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)  
print(f"初始张量: {x} | 是否追踪梯度: {x.requires_grad}")  # 2. 张量运算(自动构建计算图)  
y = x * 2 + 3  
z = y.mean()  
print(f"中间结果 y: {y} | 最终结果 z: {z}")  # 3. 自动求导(反向传播)  
z.backward()  # 计算 dz/dxprint(f"梯度 dz/dx: {x.grad}")  # 输出: [0.6667, 0.6667, 0.6667]  # 4. GPU 加速演示  
if torch.cuda.is_available():  x_gpu = x.to('cuda')  # 一键迁移到GPU  print(f"GPU张量: {x_gpu} | 位于设备: {x_gpu.device}")

2 Module

让我们思考一个问题,我们要怎么定义一个神经网络的“结构”和“行为”呢?

通过 nn.Module,它是PyTorch中所有神经网络模块的基类。你可以把它想象成一个可以“思考”的容器,它不仅存储网络的参数(如权重和偏置),还定义了数据应该如何流动(前向传播)。

本质:所有神经网络层的基类,管理参数 + 定义计算流程
核心forward() 方法定义数据流动逻辑

2.1 简介

在PyTorch中,torch.nn.Module 是构建神经网络的核心抽象。无论是简单的线性层 nn.Linear,还是复杂的ResNet模型,它们都是 nn.Module 的子类。通过继承 nn.Module,我们可以轻松地定义自己的神经网络。

经过本节的学习,你将收获:

  • nn.Module 的简介
  • 如何使用 nn.Sequential 快速构建模型
  • 如何自定义 nn.Module 子类来创建复杂模型
  • nn.Module 的核心操作(参数管理、设备迁移)

2.2 创建Module

在接下来的内容中,我们将介绍几种常见的创建 Module 的方法。

  • 使用 nn.Sequential 构建模型
    nn.Sequential 是一个有序的容器,它将传入的模块按顺序组合起来。对于不需要复杂逻辑的“直筒型”网络,这是最简单的方法。

    import torch.nn as nn# 定义一个简单的多层感知机 (MLP)
    model = nn.Sequential(nn.Flatten(),                    # 将图像展平成一维向量nn.Linear(28*28, 128),           # 全连接层 (输入784 -> 输出128)nn.ReLU(),                       # 激活函数nn.Linear(128, 10),              # 输出层 (输入128 -> 输出10类)nn.Softmax(dim=1)                # 输出概率分布
    )
    print(model)
    
    Sequential((0): Flatten(start_dim=1, end_dim=-1)(1): Linear(in_features=784, out_features=128, bias=True)(2): ReLU()(3): Linear(in_features=128, out_features=10, bias=True)(4): Softmax(dim=1)
    )
    
  • 自定义 nn.Module 子类
    对于更复杂的网络结构(如包含分支、残差连接等),我们需要创建自己的类,继承 nn.Module

    import torch.nn as nnclass SimpleMLP(nn.Module):def __init__(self):super().__init__()# 在 __init__ 中定义网络层self.flatten = nn.Flatten()self.fc1 = nn.Linear(28*28, 128)self.relu = nn.ReLU()self.fc2 = nn.Linear(128, 10)def forward(self, x):# 在 forward 中定义数据流动的逻辑x = self.flatten(x)x = self.fc1(x)x = self.relu(x)x = self.fc2(x)return x# 实例化模型
    model = SimpleMLP()
    print(model)
    
    SimpleMLP((flatten): Flatten(start_dim=1, end_dim=-1)(fc1): Linear(in_features=784, out_features=128, bias=True)(relu): ReLU()(fc2): Linear(in_features=128, out_features=10, bias=True)
    )
    

一个标准的 nn.Module 子类有两个必须理解的方法。

  1. __init__ 方法:
    初始化网络层,并将它们作为类的属性,在这里定义参数。
  2. forward 方法:
    接收输入 x(一个Tensor),然后执行一系列计算并返回输出(也是一个Tensor)。
    不需要手动调用 forward,执行model实例是,PyTorch 会自动调用它的forward方法。

2.3 Module 的核心操作

在接下来的内容中,我们将介绍 Module 的几个关键操作。

  • 参数管理
    nn.Module 会自动将 nn.Parameter 或任何 nn.Module 子类的实例注册为模型的参数。我们可以使用以下方法访问它们。

    # 获取模型的所有参数
    for param in model.parameters():print(param.shape)# 获取模型的命名参数
    for name, param in model.named_parameters():print(f"{name}: {param.shape}")
    
    flatten._parameters: {} # Flatten层无参数
    fc1.weight: torch.Size([128, 784])
    fc1.bias: torch.Size([128])
    fc2.weight: torch.Size([10, 128])
    fc2.bias: torch.Size([10])
    
  • 设备迁移
    为了利用GPU加速,我们可以使用 .to(device) 方法将整个模型(包括其所有参数和子模块)迁移到指定设备。

    device = 'cuda' if torch.cuda.is_available() else 'cpu'
    model = model.to(device)  # 一键迁移到GPU
    print(f"模型设备: {next(model.parameters()).device}")
    
  • 状态保存与加载
    我们可以使用 state_dict() 来获取模型参数的字典,这非常适合保存和加载训练好的模型。

    # 保存模型
    torch.save(model.state_dict(), 'simple_mlp.pth')# 加载模型 (需要先创建相同结构的模型)
    new_model = SimpleMLP()
    new_model.load_state_dict(torch.load('simple_mlp.pth'))
    new_model.eval() # 切换到评估模式
    
  • 训练/评估模式切换
    一些层(如 Dropout, BatchNorm)在训练和评估时的行为不同。我们可以使用 model.train()model.eval() 来切换模式。

    model.train()  # 启用Dropout等训练专用操作
    # ... 训练代码 ...model.eval()   # 禁用Dropout,使用BatchNorm的统计值
    # ... 推理代码 ...
    with torch.no_grad():  # 通常与 no_grad() 一起使用output = model(input_tensor)
    

3 Autograd

让我们思考一个问题,我们要怎么让模型“学会”调整自己的参数呢?

通过 autograd,它是PyTorch的自动微分引擎。它会默默地记录我们对张量进行的所有操作,构建一个“计算图”,然后在需要时自动计算梯度,使得反向传播变得极其简单。

3.1 简介

在深度学习中,我们通过反向传播算法来更新模型参数。手动计算梯度不仅繁琐而且容易出错。PyTorch的 autograd 包可以自动完成这一过程。

autograd 的核心是 torch.Tensortorch.Function
Tensorrequires_grad 属性为 True 时,autograd 会追踪所有作用于该张量的操作
。一旦计算完成,调用 .backward() 方法,autograd 就会自动计算所有梯度,并将它们累积到 .grad 属性中。

经过本节的学习,你将收获:

  • autograd 的工作原理
  • 如何使用 requires_grad 控制梯度追踪
  • 如何执行反向传播并查看梯度
  • 如何使用 torch.no_grad() 上下文管理器

3.2 梯度追踪

  • 开启梯度追踪
    通过设置 requires_grad=True,我们可以告诉 autograd 需要追踪对这个张量的所有操作。

    import torch# 方法1: 创建时指定
    x = torch.tensor([1., 2., 3.], requires_grad=True)
    print(f"x.requires_grad: {x.requires_grad}")# 方法2: 对已有张量启用
    y = torch.tensor([4., 5., 6.])
    y.requires_grad_(True) # 注意是方法,带下划线
    print(f"y.requires_grad: {y.requires_grad}")
    
  • 构建计算图
    一旦开启了梯度追踪,后续的运算都会被记录下来。

    z = x * y + 2
    print(f"z.grad_fn: {z.grad_fn}")  # <AddBackward0 object>
    print(f"z's creator: {z.grad_fn}") 
    

3.3 反向传播

  • 执行反向传播
    调用 .backward() 方法来触发反向传播。如果 loss 是一个标量(0维张量),可以直接调用 loss.backward()

    loss = z.sum() # loss 是一个标量
    print(f"loss: {loss}") # tensor(27., grad_fn=<SumBackward0>)loss.backward() # 计算梯度
    print(f"dx/dloss: {x.grad}") # tensor([4., 5., 6.])
    print(f"dy/dloss: {y.grad}") # tensor([1., 2., 3.])
    

    注意.backward()累积梯度。在下一次迭代前,务必使用 optimizer.zero_grad() 或手动清零 x.grad.zero_()

  • 非标量输出
    如果要对非标量张量调用 .backward(),需要传入一个 gradient 参数(形状与该张量相同),作为外部梯度。

    v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
    z.backward(gradient=v) # 这里会累加梯度
    # x.grad 会增加 v * y 的值
    

3.4 禁用梯度计算

在模型推理(inference)或某些不需要梯度的计算中,我们可以使用 torch.no_grad() 上下文管理器来临时禁用梯度计算,这可以节省内存并加快计算速度。

print(f"Before no_grad, x.requires_grad: {x.requires_grad}")with torch.no_grad():a = x * 2print(f"Inside no_grad, a.requires_grad: {a.requires_grad}") # Falseprint(f"After no_grad, x.requires_grad: {x.requires_grad}") # 仍然是 True# 另一种方式:使用 .detach()
b = x.detach() * 3
print(f"b.requires_grad: {b.requires_grad}") # False

4 📌 三件套关系图解

  ┌─────────────┐      ┌───────────┐      ┌─────────────┐│             │      │           │      │             ││   Tensor    │─────▶│  Module   │─────▶│   Autograd  ││ (数据载体)  │◀─────│ (计算逻辑)│◀─────│ (梯度引擎)  │└─────────────┘      └───────────┘      └─────────────┘▲                      │                   ││                      ▼                   ▼┌─────┴──────┐        ┌─────────────┐     ┌─────────────┐│ 数据加载   │        │ 模型定义    │     │ 反向传播    ││ DataLoader │        │ forward()   │     │ loss.backward() └────────────┘        └─────────────┘     └─────────────┘

关键口诀
“Tensor 存数据,Module 定流程,Autograd 算梯度,三件套合训练成”

5 🌟 三件套协同工作流程(完整训练示例)

资料:https://zh-v2.d2l.ai/chapter_multilayer-perceptrons/mlp-scratch.html

5.1 Demo:三件套简单串联

import torch
import torch.nn as nn
import torch.optim as optim# 1. Tensor:准备数据
x = torch.randn(64, 1, 28, 28)  # 64张28x28灰度图
y_true = torch.randint(0, 10, (64,))  # 10分类标签# 2. Module:创建模型
model = nn.Sequential(nn.Flatten(),nn.Linear(28*28, 128),nn.ReLU(),nn.Linear(128, 10)
)# 3. Autograd:训练循环
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)# 前向传播 → 计算损失 → 反向传播 → 更新参数
optimizer.zero_grad()         # 清零梯度(Autograd)
logits = model(x)             # 前向传播(Module)
loss = criterion(logits, y_true)  # 计算损失(Tensor)
loss.backward()               # 反向传播(Autograd)
optimizer.step()              # 更新参数(Tensor)print(f"训练完成!损失值: {loss.item():.4f}")

5.2 构建一个简单线性回归模型 y=wx+b

我们将构建一个简单的线性回归模型 y = wx + b,并模拟一个真实的训练过程。

  1. 创建一个简单的线性模型(nn.Module)。
  2. 使用真实数据和模型预测来手动计算损失(均方误差)。
  3. 调用 loss.backward() 触发自动微分。
  4. 观察模型参数(权重 w 和偏置 b)的 .grad 属性,看到梯度的产生。
import torch
import torch.nn as nn# ----------------------------------------
# 第一步:准备虚拟数据 (y = 3x + 2)
# ----------------------------------------
# 我们假设真实的世界关系是 y = 3x + 2
# 我们生成一些带点“噪声”的数据来模拟真实情况# 创建输入 x (形状: [5, 1])
x = torch.tensor([[1.0], [2.0], [3.0], [4.0], [5.0]])  # 5个样本
print("输入 x:")
print(x)# 创建真实标签 y_true (形状: [5, 1])
true_w = 3.0
true_b = 2.0
y_true = true_w * x + true_b + torch.randn_like(x) * 0.1  # 加点小噪声
print("\n真实标签 y_true (y ≈ 3x + 2):")
print(y_true)# ----------------------------------------
# 第二步:定义模型 (nn.Module)
# ----------------------------------------
# 我们创建一个非常简单的模型,它就是一个线性层class SimpleLinear(nn.Module):def __init__(self):super().__init__()# nn.Linear(in_features, out_features) -> y = Wx + b# 因为我们只有一个输入和一个输出,所以是 Linear(1, 1)self.linear = nn.Linear(1, 1)def forward(self, x):return self.linear(x)# 实例化模型
model = SimpleLinear()
print("\n模型结构:")
print(model)# 打印初始参数 (模型刚开始是“瞎猜”的)
print("\n训练前的模型参数:")
print(f"权重 w: {model.linear.weight.item():.3f}")  # .item() 取出单个数值
print(f"偏置 b: {model.linear.bias.item():.3f}")
# 你会发现初始的 w 和 b 是随机的,和 3.0, 2.0 差很远# ----------------------------------------
# 第三步:前向传播 (Forward Pass)
# ----------------------------------------
# 让模型对输入 x 进行预测model.train()  # 确保模型在训练模式
y_pred = model(x)  # 调用 forward 方法print("\n模型预测 y_pred (训练前):")
print(y_pred)# ----------------------------------------
# 第四步:手动计算损失 (Loss)
# ----------------------------------------
# 我们使用最简单的均方误差 (MSE) 损失
# loss = (1/N) * Σ (y_true - y_pred)²# 手动计算损失
loss = torch.mean((y_true - y_pred) ** 2)print(f"\n训练前的损失 (MSE): {loss.item():.4f}")# ----------------------------------------
# 第五步:反向传播 (Backward Pass) 和 观察梯度
# ----------------------------------------
# 这是最关键的一步!
# 在反向传播之前,必须先将梯度清零,否则梯度会累积
model.zero_grad()  # 等价于 optimizer.zero_grad()# 执行反向传播
loss.backward()# 🔥 现在,我们来“亲眼”看看梯度产生了!
print("\n" + "="*50)
print("调用 loss.backward() 后,模型参数的梯度:")
print("="*50)# 查看权重 w 的梯度
w_grad = model.linear.weight.grad
print(f"权重 w 的梯度 (.grad): {w_grad.item():.4f}")
# 这个梯度告诉我们:为了减小损失,w 应该增加还是减少?# 查看偏置 b 的梯度
b_grad = model.linear.bias.grad
print(f"偏置 b 的梯度 (.grad): {b_grad.item():.4f}")
# 同理,这个梯度指导 b 如何更新# ----------------------------------------
# 第六步:(可选)更新参数
# ----------------------------------------
# 虽然练习目标不包括更新,但我们可以手动模拟一下
# 这就是优化器(如SGD)做的事情:参数 = 参数 - 学习率 * 梯度learning_rate = 0.01# 手动更新权重
with torch.no_grad():  # 更新参数时不需要记录梯度model.linear.weight -= learning_rate * w_gradmodel.linear.bias -= learning_rate * b_gradprint("\n" + "="*50)
print("一次梯度下降更新后的模型参数:")
print("="*50)
print(f"更新后的权重 w: {model.linear.weight.item():.3f}")
print(f"更新后的偏置 b: {model.linear.bias.item():.3f}")
print("现在参数更接近真实的 3.0 和 2.0 了吗?")

在这个Demo中,三大件协作如下:

          (数据)↓┌─────────────┐│    Tensor   │ ← 存放 x, y, w, b 这些数字└─────────────┘↓ (输入)┌─────────────┐│   Module    │ ← 定义 y = w*x + b 这个计算公式└─────────────┘↓ (输出预测 y_pred)┌─────────────┐│  计算损失    │ ← 比较 y_pred 和 真实 y 的差距└─────────────┘↓ (标量 loss)┌─────────────┐│  Autograd   │ ← 调用 loss.backward(),自动算出 w 和 b 的梯度└─────────────┘↓ (梯度 dw, db)┌─────────────┐│  优化器     │ ← 用梯度更新 w 和 b,让它们更接近真实值└─────────────┘

5.3 Demo:搭建一个简单的多层感知机

Transformer模型中的“前馈网络(Feed-Forward Network)”部分,其本质就是一个多层感知机(MLP)。
让我们仿照教程,搭建搭建一个简单的多层感知机:手写数字分类MLP。

import torch  
import torch.nn.functional as F  
from torchvision import datasets, transforms  
from torch.utils.data import DataLoader  
import numpy as np  # =============================================================================  
# 1. Tensor:数据的容器  
# =============================================================================  
"""  
Tensor 是 PyTorch 中存储和操作数据的核心结构。  
它不仅是多维数组,还能追踪梯度,是深度学习的“通用货币”。  
"""  # 演示:Tensor 的基本用途  
print("=== 1. Tensor 演示 ===")  # 创建一个需要梯度追踪的张量  
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)  
print(f"初始张量: {x} | 追踪梯度: {x.requires_grad}")  # 执行运算(构建计算图)  
y = x * 2 + 3  
z = y.mean()  
print(f"中间结果 y: {y} | 最终标量 z: {z}")  # 反向传播  
z.backward()  
print(f"梯度 dz/dx: {x.grad}")  # 应为 [2/3, 2/3, 2/3]  
# GPU 加速(如果可用)  
if torch.cuda.is_available():  x_gpu = x.to('cuda')  print(f"GPU张量: {x_gpu} | 设备: {x_gpu.device}")  
else:  print("GPU不可用,使用CPU")  # =============================================================================  
# 2. Module:计算逻辑的蓝图(手动实现)  
# =============================================================================  
"""  
我们不使用 nn.Module 和 nn.Linear,  
而是手动创建权重和偏置,并定义前向传播函数。  
"""  print("\n=== 2. 手动实现 MLP(无 nn.Module / nn.Linear)===")  # 超参数  
input_size = 784   # 28x28 图像展平  
hidden_size = 128  # 隐藏层大小
output_size = 10   # MNIST 10 类  
learning_rate = 0.01  # 学习率
num_epochs = 3  # 训练轮数# 手动初始化参数(替代 nn.Linear)  
torch.manual_seed(42)  
W1 = torch.randn(input_size, hidden_size) * 0.01  
b1 = torch.zeros(hidden_size)  
W2 = torch.randn(hidden_size, output_size) * 0.01  
b2 = torch.zeros(output_size)  # 开启梯度追踪  
W1.requires_grad_(True)  
b1.requires_grad_(True)  
W2.requires_grad_(True)  
b2.requires_grad_(True)  print(f"W1: {W1.shape}, b1: {b1.shape}")  
print(f"W2: {W2.shape}, b2: {b2.shape}")  # 定义前向传播函数(替代 forward)  
def forward(x):  """  手动实现前向传播  :param x: 输入张量 (B, 784)    :return: logits (B, 10)    """    z1 = x @ W1 + b1      # 第一层线性变换  a1 = F.relu(z1)       # 激活函数  z2 = a1 @ W2 + b2     # 第二层线性变换  return z2  # =============================================================================  
# 3. Autograd:自动微分引擎  
# =============================================================================  
"""  
Autograd 会自动追踪所有操作,调用 loss.backward() 即可计算梯度。  
我们手动实现训练循环,替代 optimizer.step() 和 optimizer.zero_grad()"""  print("\n=== 3. 数据加载与训练 ===")  # 数据预处理  
transform = transforms.ToTensor()  # 加载 MNIST 数据集  
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)  
test_dataset = datasets.MNIST(root='./data', train=False, transform=transform)  train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)  
test_loader = DataLoader(test_dataset, batch_size=1000, shuffle=False)  print(f"训练集大小: {len(train_dataset)}")  
print(f"测试集大小: {len(test_dataset)}")  # 训练循环  
train_losses = []  for epoch in range(num_epochs):  epoch_loss = 0.0  count = 0  for x_batch, y_batch in train_loader:  # 展平图像: (B, 1, 28, 28) -> (B, 784)  x_batch = x_batch.view(x_batch.size(0), -1)  # 前向传播  logits = forward(x_batch)  loss = F.cross_entropy(logits, y_batch)  # 使用函数式 API  # 反向传播  loss.backward()  # 手动更新参数(替代 optimizer.step())  with torch.no_grad():  W1 -= learning_rate * W1.grad  b1 -= learning_rate * b1.grad  W2 -= learning_rate * W2.grad  b2 -= learning_rate * b2.grad  # 清零梯度(替代 optimizer.zero_grad())  W1.grad.zero_()  b1.grad.zero_()  W2.grad.zero_()  b2.grad.zero_()  epoch_loss += loss.item()  count += 1  avg_loss = epoch_loss / count  train_losses.append(avg_loss)  print(f"Epoch [{epoch+1}/{num_epochs}], Loss: {avg_loss:.4f}")  # =============================================================================  
# 4. 模型评估  
# =============================================================================  
print("\n=== 4. 模型评估 ===")  correct = 0  
total = 0  
with torch.no_grad():  # 推理时关闭梯度  for x_batch, y_batch in test_loader:  x_batch = x_batch.view(x_batch.size(0), -1)  logits = forward(x_batch)  _, predicted = torch.max(logits, 1)  total += y_batch.size(0)  correct += (predicted == y_batch).sum().item()  accuracy = 100 * correct / total  
print(f"测试准确率: {accuracy:.2f}%")  # =============================================================================  
# 5. Demo:线性回归(观察梯度生成过程)  
# =============================================================================  
print("\n=== 5. 线性回归 Demo:观察梯度如何生成 ===")  # 生成虚拟数据:y = 3x + 2 + noise  
x_reg = torch.tensor([[1.0], [2.0], [3.0], [4.0], [5.0]])  
true_w, true_b = 3.0, 2.0  
y_true = true_w * x_reg + true_b + torch.randn_like(x_reg) * 0.1  # 初始化参数  
w = torch.randn(1, 1, requires_grad=True)  
b = torch.zeros(1, requires_grad=True)  print(f"真实参数: w={true_w}, b={true_b}")  
print(f"初始参数: w={w.item():.3f}, b={b.item():.3f}")  # 前向传播  
y_pred = x_reg @ w + b  
loss = F.mse_loss(y_pred, y_true)  # 反向传播  
loss.backward()  print(f"\n调用 loss.backward() 后:")  
print(f"权重 w 的梯度: {w.grad.item():.4f}")  
print(f"偏置 b 的梯度: {b.grad.item():.4f}")  print("\n梯度方向正确:w 的梯度为正,说明当前 w 偏小,应增大")  # =============================================================================  
# 6. 总结口诀  
# =============================================================================  
print("\n" + "="*60)  
print("🎯 PyTorch 三件套核心口诀")  
print("="*60)  
print("Tensor 存数据,Module 定流程,Autograd 算梯度")  
print("三件套合训练成,手动实现才真懂!")  
print("="*60)

概念补充:

  • 超参数:一般指一些模型的配置参数,想当于模型的学习规则。
超参数说明为什么重要
学习率 (Learning Rate, lr)每次更新参数时的“步长”太大:学得快但可能错过最优解
太小:学得慢,可能卡住
批次大小 (Batch Size)一次训练使用的样本数量太大:内存压力大,泛化可能差
太小:训练不稳定
训练轮数 (Epochs)整个数据集被完整遍历的次数太少:欠拟合
太多:过拟合(死记硬背)
隐藏层大小 (Hidden Size)神经网络中隐藏层的神经元数量决定了模型的“容量”
太大:容易过拟合
太小:无法学习复杂模式
网络层数 (Number of Layers)有多少个隐藏层层数多 → “深度网络”,能学更复杂特征,但也更难训练
优化器类型使用 SGD、Adam 还是 RMSprop不同优化器收敛速度和稳定性不同
正则化参数如 Dropout 概率、L2 权重衰减用于防止过拟合

刻意练习

Q:如何给MLP增加一个隐藏层?

http://www.dtcms.com/a/319256.html

相关文章:

  • 旅游mcp配置(1)
  • cookie和session之间区别
  • 从BaseMapper到LambdaWrapper:MyBatis-Plus的封神之路
  • 组件安全漏洞
  • 站在Vue的角度,对比鸿蒙开发中的状态管理
  • 机器学习工程化 3.0:从“实验科学”到“持续交付”的 7 个关卡
  • 淘宝商品价格数据采集||淘宝SKU价格数据采集API
  • 从配置到远程访问:如何用群晖NAS FTP+ Cpolar搭建稳定文件传输通道
  • Charles中文版抓包工具使用指南 提高API调试和网络优化效率
  • 通信中间件 Fast DDS(一) :编译、安装和测试
  • rk3588s vscode索引失败的问题
  • Numpy科学计算与数据分析:Numpy随机数生成入门
  • Numpy科学计算与数据分析:Numpy数据分析基础之统计函数应用
  • 【线性代数】5特征值和特征向量
  • Android 原生与 Flutter 通信完整实现 (Kotlin 版)
  • C++基础:继承
  • qt系统--事件
  • 设计模式中的行为模式
  • sqli-labs-master/Less-41~Less-50
  • 论文Review 激光实时动态物体剔除 DUFOMap | KTH出品!RAL2024!| 不上感知,激光的动态物体在线剔除还能有什么方法?
  • DrissionPage自动化:高效Web操作新选择
  • 【人工智能99问】NLP(自然语言处理)大模型有哪些?(20/99)
  • 【多重BFS】Monsters
  • 调用阿里云-阿里云百炼 AI
  • 表驱动法-灵活编程范式
  • Java 中 Object 类的解析:知识点与注意事项
  • Oracle参数Process
  • 深度学习的视觉惯性里程计(VIO)算法优化实践
  • PCB制造中压接孔、插接孔、沉头孔、台阶孔的区别及生产流程
  • [Oracle] MOD()函数