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.pswp.cn/diannao/94685.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/94685.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python源码是如何运行起来的

为什么要了解底层原理 写出高质量代码 问题定位 满足好奇心 机械通感 开始 当我们编写并运行一行 print(Hello, World!) 时&#xff0c;背后究竟发生了什么&#xff1f;Python 代码是如何从我们可读的文本&#xff0c;变成计算机可以执行的指令的呢&#xff1f; 很多人将…

MacOS Docker 安装指南

MacOS Docker 安装指南 引言 Docker 是一个开源的应用容器引擎,它允许开发者打包他们的应用以及应用的依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app)。Docker …

Cisco 3750X交换机更新到IOS 15.2后无法启动 提示:Boot process failed...

背景及故障现象 一台新购入的二手Cisco 3750X-48P&#xff0c;原机自带IOS软件版本为12.x&#xff0c;可以正常工作。 但将IOS版本升级到15.2之后&#xff0c;在启动过程中卡住。 第一次加载IOS软件时是正常的&#xff0c;提示&#xff1a; Loading "flash:/c3750e-uni…

Redis Redis 常见数据类型

Redis 提供了 5 种数据结构&#xff0c;理解每种数据结构的特点对于 Redis 开发运维非常重要&#xff0c;同时掌握每种数据结构的常见命令&#xff0c;会在使用 Redis 的时候做到游刃有余。 一、预备知识 官方文档&#xff1a;Commands | Docs (redis.io) 1、最核心的两个命令…

金融风控实战:Spring Boot + LightGBM 贷款预测模型服务化(超详细版)

金融风控实战&#xff1a;Spring Boot LightGBM 贷款预测模型服务化&#xff08;超详细版&#xff09;一、整体架构设计二、模型训练与优化1. 特征工程&#xff08;Python&#xff09;2. 模型评估与优化三、Spring Boot 服务实现1. 项目结构2. ONNX 模型服务3. 特征工程服务4.…

前端学习 7:EDA 工具

目录 EDA 工具 Design Ware Synopsys CoreTools 套件 VCS verdi Design Compiler EDA 工具 常用的EDA工具主要来自三家公司&#xff1a;Synopsys、Cadence和Mentor&#xff08;已被Siemens收购&#xff09;。EDA&#xff0c;全称电子设计自动化&#xff08;Electronics …

windows有一个企业微信安装包,脚本执行并安装到d盘。

以下是将本地已有的企业微信安装包安装到D盘的完整PowerShell脚本&#xff0c;包含详细的错误处理和进度反馈&#xff1a; <# .SYNOPSIS使用本地企业微信安装包安装到D盘 .DESCRIPTION自动检测本地安装包&#xff0c;静默安装到指定目录支持.exe和.msi格式安装包 #># 强制…

[LVGL] 布局系统 lv_flex, lv_grid | 输入设备 lv_indev | union

第五章&#xff1a;布局系统&#xff08;lv_flex, lv_grid&#xff09; 欢迎回来&#xff01; 在第四章&#xff1a;样式&#xff08;lv_style&#xff09;中&#xff0c;我们掌握了如何通过色彩、字体和圆角等特性美化部件。当界面元素具备视觉吸引力后&#xff0c;如何优雅…

Linux中的mkdir命令

基本语法mkdir 命令的基本语法如下&#xff1a;mkdir [选项] 目录名创建单个目录要创建一个新目录&#xff0c;只需在 mkdir 后跟上目录名称。例如&#xff1a;mkdir new_folder这会在当前工作目录下创建一个名为 new_folder 的目录。创建多个目录可以一次性创建多个目录&#…

基于大数据的美食视频播放数据可视化系统 Python+Django+Vue.js

本文项目编号 25003 &#xff0c;文末自助获取源码 \color{red}{25003&#xff0c;文末自助获取源码} 25003&#xff0c;文末自助获取源码 目录 一、系统介绍二、系统录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状 六、核心代码6.1 查询数据6.2 新…

微信小程序精品项目-基于springboot+Android的计算机精品课程学习系统(源码+LW+部署文档+全bao+远程调试+代码讲解等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

(五)系统可靠性设计

2024年博主考软考高级系统架构师没通过&#xff0c;于是决定集中精力认真学习系统架构的每一个环节&#xff0c;并在2025年软考中取得了不错的成绩&#xff0c;虽然做信息安全的考架构师很难&#xff0c;但找对方法&#xff0c;问题就不大&#xff01; 本文主要是博主在学习过程…

Shuffle SOAR使用学习经验

Shuffle SOAR 1. 基础操作与配置1.1 环境搭建与系统要求1.1.1 硬件与操作系统要求Shuffle SOAR 平台作为一款开源的安全编排、自动化与响应&#xff08;SOAR&#xff09;工具&#xff0c;其部署方式灵活&#xff0c;支持云端和自托管两种模式。对于自托管部署&#xff0c;官方推…

腾讯云 EdgeOne 产品分析与免费套餐体验指南

本文围绕腾讯云 EdgeOne 展开&#xff0c;全方位介绍它的核心能力、免费套餐内容&#xff0c;以及如何快速上手、监控和排查常见问题&#xff0c;帮助个人开发者和中小企业在不产生额外成本的前提下体验高性能的边缘加速与安全防护。 一、产品概述 EdgeOne 定位 一体化云服务平…

npm ERR! Unsupported URL Type “workspace:“: workspace:./lib

如下 npm install npm ERR! code EUNSUPPORTEDPROTOCOL npm ERR! Unsupported URL Type "workspace:": workspace:./libnpm ERR! A complete log of this run can be found in: D:\IDEA\nodejs\node_cache\_logs\2025-08-06T08_21_32_592Z-debug-0.log原因及解决 pac…

微积分: 变化与累积

微积分,这门研究变化与累积的数学分支,其核心思想竟与东方哲学中"易"的概念不谋而合。《易经》有云:“易有太极,是生两仪”,而微积分正是通过"微分"与"积分"这对辩证统一的操作,揭示了世间万物变化与永恒的奥秘。 #mermaid-svg-UjO6qqMm0h…

web-vue工作流程

接续bmcweb流程。 当登录openbmc web页面后,浏览器会根据index.html中的js文件中的routes信息,自动获取信息,比如当前的网络设置信息、Datetime时区时间信息等。 以获取网络配置信息为例: 浏览器从app.js获取到settins->network的route:”/settings/network”,加载对应…

全球化2.0 | 泰国IT服务商携手云轴科技ZStack重塑云租赁新生态

在全球数字化转型不断加速的今天&#xff0c;泰国企业对于高质量云服务的需求日益旺盛。作为深耕本地市场逾二十年的行业领先IT服务商&#xff0c;泰国IT服务商不仅覆盖了IT系统、软件、硬件及网络等多个领域&#xff0c;还持续引领当地技术服务创新。近期&#xff0c;该泰国IT…

一文搞懂Hive临时表操作秘籍

Hive 临时表&#xff1a;数据处理的得力助手 在大数据处理的广阔领域中&#xff0c;Hive 凭借其强大的数据仓库功能&#xff0c;成为了众多数据分析师和开发者的得力工具。Hive 提供了类似 SQL 的查询语言 HiveQL&#xff0c;让我们能够方便地对存储在 Hadoop 分布式文件系统&a…

瞬态吸收光谱仪的基本原理

目录 1. 基态与激发态 2. 时间上的动力学信息 3. pump-probe探测技术 4. 时间延迟和同一光源 5. 延时线和OPA 6. 差分信号 7. 斩波器 原视频链接&#xff1a;瞬态吸收光谱仪的基本原理_哔哩哔哩_bilibili 1. 基态与激发态 当光照射在物质上时&#xff0c;组成物质的微观…