欢迎光临
我们一直在努力

PyTorch 学习:从张量到完整训练循环

目录

  • 1. PyTorch 是什么
  • 2. 环境安装与验证
  • 3. 学习路线
  • 4. Tensor:PyTorch 的基础数据结构
  • 5. Autograd:自动求导
  • 6. nn.Module:搭建神经网络
  • 7. Dataset 和 DataLoader:组织数据
  • 8. 完整示例一:线性回归
  • 9. 完整示例二:二分类神经网络
  • 10. 完整示例三:FashionMNIST 图像分类
  • 11. 保存、加载与推理
  • 12. GPU、混合精度与性能优化
  • 13. 常见错误与排查
  • 14. 建议练习
  • 15. 参考资料

1. PyTorch 是什么

PyTorch 是一个用于深度学习和张量计算的 Python 框架。你可以把它理解成:

  • NumPy 的增强版:支持 GPU 加速。
  • 自动求导系统:可以自动计算梯度。
  • 神经网络工具箱:提供 torch.nn、torch.optim、DataLoader 等常用组件。
  • 研究和工程都常用的框架:适合快速实验,也能部署模型。

一个典型 PyTorch 训练流程如下:

准备数据 -> 构建模型 -> 定义损失函数 -> 定义优化器 -> 前向传播 -> 计算损失 -> 反向传播 -> 更新参数 -> 保存模型

最常用的模块:

import torch
from torch import nn
from torch.utils.data import Dataset, DataLoader

2. 环境安装与验证

建议在项目目录中创建虚拟环境,避免依赖混乱。

2.1 创建虚拟环境

Windows PowerShell:

python m venv .venv
.\\.venv\\Scripts\\activate
python m pip install upgrade pip setuptools wheel

Linux/macOS:

python3 -m venv .venv
source .venv/bin/activate
python -m pip install –upgrade pip setuptools wheel

2.2 安装 PyTorch

CPU 版本通常可以这样安装:

pip install torch torchvision torchaudio

如果你有 NVIDIA GPU,建议去 PyTorch 官方安装页选择你的系统、包管理器和 CUDA 版本,再复制生成的命令:

https://pytorch.org/get-started/locally/

不要随便复制旧教程里的 CUDA 安装命令,因为 PyTorch、CUDA、显卡驱动版本会变化。

2.3 验证安装

新建 check_torch.py:

import torch

print("torch version:", torch.__version__)
print("cuda available:", torch.cuda.is_available())

if torch.cuda.is_available():
print("gpu:", torch.cuda.get_device_name(0))

运行:

python check_torch.py

3. 学习路线

推荐按下面顺序学:

  • Tensor:创建、形状、索引、矩阵乘法、广播、设备转移。
  • autograd:理解 requires_grad、backward()、grad。
  • nn.Module:用类封装模型。
  • 损失函数:如 MSELoss、CrossEntropyLoss、BCEWithLogitsLoss。
  • 优化器:如 SGD、Adam。
  • Dataset / DataLoader:规范组织数据。
  • 训练循环:前向传播、损失、清梯度、反向传播、更新参数。
  • 保存加载:保存 state_dict,加载后推理。
  • GPU 和性能:device、混合精度、torch.compile。
  • 4. Tensor:PyTorch 的基础数据结构

    Tensor 是 PyTorch 的核心数据结构,类似 NumPy 数组,但可以放到 GPU 上,也可以参与自动求导。

    4.1 创建 Tensor

    import torch

    a = torch.tensor([1, 2, 3])
    b = torch.zeros(2, 3)
    c = torch.ones(2, 3)
    d = torch.randn(2, 3) # 标准正态分布随机数
    e = torch.arange(0, 10, 2) # 0, 2, 4, 6, 8

    print(a)
    print(b.shape)
    print(d.dtype)

    4.2 形状、类型、设备

    import torch

    x = torch.randn(4, 5)

    print(x.shape) # torch.Size([4, 5])
    print(x.ndim) # 2
    print(x.dtype) # torch.float32
    print(x.device) # cpu

    y = x.to(dtype=torch.float64)
    print(y.dtype)

    device = "cuda" if torch.cuda.is_available() else "cpu"
    x = x.to(device)
    print(x.device)

    4.3 常用操作

    import torch

    x = torch.arange(12).reshape(3, 4)
    print(x)

    print(x[0]) # 第 0 行
    print(x[:, 1]) # 第 1 列
    print(x[1:3, :2]) # 切片

    print(x.reshape(2, 6))
    print(x.T) # 转置

    4.4 数学运算

    import torch

    a = torch.tensor([[1.0, 2.0], [3.0, 4.0]])
    b = torch.tensor([[10.0, 20.0], [30.0, 40.0]])

    print(a + b)
    print(a * b) # 对应元素相乘
    print(a @ b) # 矩阵乘法
    print(torch.matmul(a, b))
    print(a.mean())
    print(a.sum(dim=0)) # 按列求和

    4.5 广播机制

    广播可以让不同形状的 Tensor 自动扩展后计算。

    import torch

    x = torch.randn(3, 4)
    bias = torch.tensor([1.0, 2.0, 3.0, 4.0])

    y = x + bias
    print(y.shape) # torch.Size([3, 4])

    常见规则:从右往左对齐维度,维度相同或其中一个为 1 时可以广播。

    4.6 与 NumPy 互转

    import numpy as np
    import torch

    arr = np.array([1, 2, 3], dtype=np.float32)
    x = torch.from_numpy(arr)

    y = x.numpy()

    print(type(x), x)
    print(type(y), y)

    注意:CPU Tensor 和 NumPy 数组可能共享内存。修改其中一个,另一个可能也变。

    5. Autograd:自动求导

    深度学习训练的关键是求梯度。PyTorch 会根据 Tensor 运算动态构建计算图,并通过 backward() 自动反向传播。

    5.1 一个最小例子

    import torch

    x = torch.tensor(2.0, requires_grad=True)
    y = x ** 2 + 3 * x + 1

    y.backward()

    print("y =", y.item())
    print("dy/dx =", x.grad.item()) # 2x + 3,当 x=2 时为 7

    5.2 多变量求导

    import torch

    w = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
    x = torch.tensor([4.0, 5.0, 6.0])

    y = (w * x).sum()
    y.backward()

    print(w.grad) # tensor([4., 5., 6.])

    5.3 禁用梯度

    推理时不需要梯度,使用 torch.no_grad() 可以减少内存和计算开销。

    import torch

    x = torch.tensor(2.0, requires_grad=True)

    with torch.no_grad():
    y = x * 10

    print(y.requires_grad) # False

    5.4 detach

    detach() 会创建一个不再追踪梯度的新 Tensor。

    import torch

    x = torch.tensor(3.0, requires_grad=True)
    y = x * x
    z = y.detach()

    print(y.requires_grad) # True
    print(z.requires_grad) # False

    6. nn.Module:搭建神经网络

    PyTorch 中推荐用 nn.Module 定义模型。

    6.1 最简单的模型

    import torch
    from torch import nn

    class LinearModel(nn.Module):
    def __init__(self):
    super().__init__()
    self.linear = nn.Linear(in_features=1, out_features=1)

    def forward(self, x):
    return self.linear(x)

    model = LinearModel()
    x = torch.tensor([[1.0], [2.0], [3.0]])
    y_pred = model(x)

    print(y_pred)
    print(model)

    6.2 Sequential 写法

    简单模型也可以用 nn.Sequential:

    from torch import nn

    model = nn.Sequential(
    nn.Linear(10, 32),
    nn.ReLU(),
    nn.Linear(32, 2)
    )

    print(model)

    6.3 常见层

    • nn.Linear:全连接层。
    • nn.Conv2d:二维卷积层,常用于图像。
    • nn.BatchNorm1d / nn.BatchNorm2d:批归一化。
    • nn.ReLU / nn.GELU / nn.Sigmoid:激活函数。
    • nn.Dropout:随机丢弃神经元,减少过拟合。
    • nn.Embedding:词表索引转向量,常用于 NLP。
    • nn.LSTM / nn.GRU / nn.TransformerEncoder:序列建模。

    7. Dataset 和 DataLoader:组织数据

    实际项目中,不建议把全部训练数据直接写进训练循环。推荐用 Dataset 描述“如何取一条样本”,用 DataLoader 负责批量、打乱和并行加载。

    7.1 自定义 Dataset

    import torch
    from torch.utils.data import Dataset, DataLoader

    class ToyDataset(Dataset):
    def __init__(self):
    self.x = torch.arange(0, 100, dtype=torch.float32).reshape(1, 1)
    self.y = 2 * self.x + 3

    def __len__(self):
    return len(self.x)

    def __getitem__(self, index):
    return self.x[index], self.y[index]

    dataset = ToyDataset()
    loader = DataLoader(dataset, batch_size=8, shuffle=True)

    for batch_x, batch_y in loader:
    print(batch_x.shape, batch_y.shape)
    break

    7.2 DataLoader 常用参数

    loader = DataLoader(
    dataset,
    batch_size=32,
    shuffle=True,
    num_workers=0,
    drop_last=False
    )

    参数说明:

    • batch_size:每批样本数。
    • shuffle:训练集通常设为 True。
    • num_workers:数据加载进程数。Windows 上初学时可以先用 0。
    • drop_last:最后一个 batch 不够大小时是否丢弃。

    8. 完整示例一:线性回归

    目标:让模型学会 y = 3x + 2 + noise。

    把下面代码保存为 linear_regression_demo.py 运行。

    import torch
    from torch import nn
    from torch.utils.data import DataLoader, TensorDataset

    def main():
    torch.manual_seed(42)

    x = torch.linspace(5, 5, 200).reshape(1, 1)
    noise = torch.randn_like(x) * 0.5
    y = 3 * x + 2 + noise

    dataset = TensorDataset(x, y)
    loader = DataLoader(dataset, batch_size=32, shuffle=True)

    model = nn.Linear(1, 1)
    loss_fn = nn.MSELoss()
    optimizer = torch.optim.SGD(model.parameters(), lr=0.05)

    for epoch in range(100):
    total_loss = 0.0

    for batch_x, batch_y in loader:
    pred = model(batch_x)
    loss = loss_fn(pred, batch_y)

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    total_loss += loss.item() * batch_x.size(0)

    if (epoch + 1) % 10 == 0:
    avg_loss = total_loss / len(dataset)
    print(f"epoch {epoch + 1:03d}, loss={avg_loss:.4f}")

    weight = model.weight.item()
    bias = model.bias.item()
    print(f"learned: y = {weight:.3f}x + {bias:.3f}")

    test_x = torch.tensor([[10.0]])
    with torch.no_grad():
    test_y = model(test_x)
    print("x=10 prediction:", test_y.item())

    if __name__ == "__main__":
    main()

    9. 完整示例二:二分类神经网络

    这个例子不用下载数据,直接生成二维点。模型要判断一个点在圆内还是圆外。

    保存为 binary_classification_demo.py。

    import torch
    from torch import nn
    from torch.utils.data import DataLoader, TensorDataset, random_split

    class MLP(nn.Module):
    def __init__(self):
    super().__init__()
    self.net = nn.Sequential(
    nn.Linear(2, 32),
    nn.ReLU(),
    nn.Linear(32, 32),
    nn.ReLU(),
    nn.Linear(32, 1)
    )

    def forward(self, x):
    return self.net(x)

    def accuracy_from_logits(logits, y):
    probs = torch.sigmoid(logits)
    pred = (probs >= 0.5).float()
    return (pred == y).float().mean().item()

    def main():
    torch.manual_seed(0)

    n = 2000
    x = torch.rand(n, 2) * 4 2
    radius = torch.sqrt((x ** 2).sum(dim=1, keepdim=True))
    y = (radius < 1.0).float()

    dataset = TensorDataset(x, y)
    train_size = int(0.8 * len(dataset))
    val_size = len(dataset) train_size
    train_set, val_set = random_split(dataset, [train_size, val_size])

    train_loader = DataLoader(train_set, batch_size=64, shuffle=True)
    val_loader = DataLoader(val_set, batch_size=256, shuffle=False)

    device = "cuda" if torch.cuda.is_available() else "cpu"
    model = MLP().to(device)
    loss_fn = nn.BCEWithLogitsLoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)

    for epoch in range(30):
    model.train()
    train_loss = 0.0

    for batch_x, batch_y in train_loader:
    batch_x = batch_x.to(device)
    batch_y = batch_y.to(device)

    logits = model(batch_x)
    loss = loss_fn(logits, batch_y)

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    train_loss += loss.item() * batch_x.size(0)

    model.eval()
    val_acc_sum = 0.0
    val_count = 0

    with torch.no_grad():
    for batch_x, batch_y in val_loader:
    batch_x = batch_x.to(device)
    batch_y = batch_y.to(device)

    logits = model(batch_x)
    batch_acc = accuracy_from_logits(logits, batch_y)

    val_acc_sum += batch_acc * batch_x.size(0)
    val_count += batch_x.size(0)

    avg_loss = train_loss / len(train_set)
    val_acc = val_acc_sum / val_count
    print(f"epoch {epoch + 1:02d}, loss={avg_loss:.4f}, val_acc={val_acc:.4f}")

    sample = torch.tensor([[0.2, 0.3], [1.5, 1.5]], device=device)
    model.eval()
    with torch.no_grad():
    probs = torch.sigmoid(model(sample))
    print("sample probabilities:", probs.cpu().squeeze().tolist())

    if __name__ == "__main__":
    main()

    关键点:

    • 二分类推荐输出一个 logit,然后用 nn.BCEWithLogitsLoss()。
    • 不要在模型最后手动加 Sigmoid 再送进 BCEWithLogitsLoss,它内部已经处理了数值稳定问题。
    • 训练时用 model.train(),验证和推理时用 model.eval()。

    10. 完整示例三:FashionMNIST 图像分类

    这个例子需要 torchvision,第一次运行会下载 FashionMNIST 数据集。

    保存为 fashion_mnist_demo.py。

    import torch
    from torch import nn
    from torch.utils.data import DataLoader
    from torchvision import datasets, transforms

    class SmallCNN(nn.Module):
    def __init__(self):
    super().__init__()
    self.features = nn.Sequential(
    nn.Conv2d(1, 16, kernel_size=3, padding=1),
    nn.ReLU(),
    nn.MaxPool2d(2),
    nn.Conv2d(16, 32, kernel_size=3, padding=1),
    nn.ReLU(),
    nn.MaxPool2d(2),
    )
    self.classifier = nn.Sequential(
    nn.Flatten(),
    nn.Linear(32 * 7 * 7, 128),
    nn.ReLU(),
    nn.Dropout(0.2),
    nn.Linear(128, 10)
    )

    def forward(self, x):
    x = self.features(x)
    return self.classifier(x)

    def evaluate(model, loader, device):
    model.eval()
    correct = 0
    total = 0

    with torch.no_grad():
    for x, y in loader:
    x = x.to(device)
    y = y.to(device)
    logits = model(x)
    pred = logits.argmax(dim=1)
    correct += (pred == y).sum().item()
    total += y.size(0)

    return correct / total

    def main():
    torch.manual_seed(42)

    transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))
    ])

    train_set = datasets.FashionMNIST(
    root="./data",
    train=True,
    download=True,
    transform=transform
    )
    test_set = datasets.FashionMNIST(
    root="./data",
    train=False,
    download=True,
    transform=transform
    )

    train_loader = DataLoader(train_set, batch_size=64, shuffle=True, num_workers=0)
    test_loader = DataLoader(test_set, batch_size=256, shuffle=False, num_workers=0)

    device = "cuda" if torch.cuda.is_available() else "cpu"
    model = SmallCNN().to(device)
    loss_fn = nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)

    for epoch in range(5):
    model.train()
    total_loss = 0.0

    for x, y in train_loader:
    x = x.to(device)
    y = y.to(device)

    logits = model(x)
    loss = loss_fn(logits, y)

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    total_loss += loss.item() * x.size(0)

    train_loss = total_loss / len(train_set)
    test_acc = evaluate(model, test_loader, device)
    print(f"epoch {epoch + 1}, train_loss={train_loss:.4f}, test_acc={test_acc:.4f}")

    torch.save(model.state_dict(), "fashion_mnist_cnn.pt")
    print("saved to fashion_mnist_cnn.pt")

    if __name__ == "__main__":
    main()

    多分类任务常见组合:

    model output: shape = [batch_size, num_classes]
    target: shape = [batch_size],类别编号如 012
    loss_fn = nn.CrossEntropyLoss()

    注意:使用 CrossEntropyLoss 时,模型最后一层不要加 Softmax,因为损失函数内部会处理。

    11. 保存、加载与推理

    推荐保存模型参数 state_dict,而不是直接保存整个模型对象。

    11.1 保存

    torch.save(model.state_dict(), "model.pt")

    11.2 加载

    加载前需要先创建相同结构的模型。

    import torch
    from torch import nn

    class SmallModel(nn.Module):
    def __init__(self):
    super().__init__()
    self.net = nn.Sequential(
    nn.Linear(4, 16),
    nn.ReLU(),
    nn.Linear(16, 3)
    )

    def forward(self, x):
    return self.net(x)

    device = "cuda" if torch.cuda.is_available() else "cpu"

    model = SmallModel().to(device)
    model.load_state_dict(torch.load("model.pt", map_location=device))
    model.eval()

    x = torch.randn(1, 4, device=device)
    with torch.no_grad():
    logits = model(x)
    pred = logits.argmax(dim=1)

    print(pred.item())

    11.3 保存更多训练信息

    如果希望中断后继续训练,可以保存 checkpoint。

    checkpoint = {
    "model": model.state_dict(),
    "optimizer": optimizer.state_dict(),
    "epoch": epoch,
    "loss": loss.item()
    }

    torch.save(checkpoint, "checkpoint.pt")

    加载:

    checkpoint = torch.load("checkpoint.pt", map_location=device)
    model.load_state_dict(checkpoint["model"])
    optimizer.load_state_dict(checkpoint["optimizer"])
    start_epoch = checkpoint["epoch"] + 1

    12. GPU、混合精度与性能优化

    12.1 使用 device

    模型和数据必须在同一个设备上。

    device = "cuda" if torch.cuda.is_available() else "cpu"

    model = model.to(device)
    batch_x = batch_x.to(device)
    batch_y = batch_y.to(device)

    如果报错:

    Expected all tensors to be on the same device

    通常是模型在 GPU,但数据还在 CPU,或反过来。

    12.2 混合精度训练

    混合精度常用于 NVIDIA GPU,可以减少显存占用并提升速度。

    import torch

    scaler = torch.amp.GradScaler("cuda")

    for batch_x, batch_y in train_loader:
    batch_x = batch_x.to("cuda")
    batch_y = batch_y.to("cuda")

    optimizer.zero_grad()

    with torch.amp.autocast(device_type="cuda"):
    logits = model(batch_x)
    loss = loss_fn(logits, batch_y)

    scaler.scale(loss).backward()
    scaler.step(optimizer)
    scaler.update()

    如果没有 CUDA,就先不要使用这段代码。

    12.3 torch.compile

    PyTorch 2.x 提供了 torch.compile,可以尝试编译模型以提升运行速度。

    model = torch.compile(model)

    建议:

    • 先保证普通训练代码正确。
    • 再尝试 torch.compile(model)。
    • 如果遇到兼容问题,先移除它排查。

    13. 常见错误与排查

    13.1 形状不匹配

    错误示例:

    RuntimeError: mat1 and mat2 shapes cannot be multiplied

    排查方式:

    print(x.shape)
    print(logits.shape)
    print(y.shape)

    尤其注意 nn.Linear(in_features, out_features) 的 in_features 是否等于输入最后一维。

    13.2 标签类型不对

    CrossEntropyLoss 要求:

    logits.dtype == torch.float32
    y.dtype == torch.long

    示例:

    y = y.long()
    loss = nn.CrossEntropyLoss()(logits, y)

    13.3 忘记清梯度

    PyTorch 默认会累积梯度,所以训练时必须清空旧梯度:

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    13.4 训练和推理模式混用

    带有 Dropout、BatchNorm 的模型,训练和推理行为不同。

    model.train() # 训练
    model.eval() # 验证、测试、推理

    13.5 把概率传给 CrossEntropyLoss

    错误写法:

    prob = torch.softmax(logits, dim=1)
    loss = nn.CrossEntropyLoss()(prob, y)

    正确写法:

    loss = nn.CrossEntropyLoss()(logits, y)

    14. 建议练习

    练习 1:Tensor 基础

    写代码完成:

    • 创建一个形状为 [5, 3] 的随机 Tensor。
    • 取第 2 行。
    • 计算每一列的平均值。
    • 转成 GPU Tensor,如果没有 GPU 就保持 CPU。

    参考答案:

    import torch

    x = torch.randn(5, 3)
    print(x[1])
    print(x.mean(dim=0))

    device = "cuda" if torch.cuda.is_available() else "cpu"
    x = x.to(device)
    print(x.device)

    练习 2:改造线性回归

    把线性回归示例中的真实函数改成:

    y = 4 * x + 7 + noise

    观察模型最后学到的 weight 和 bias 是否接近 -4 和 7。

    练习 3:给二分类模型加层

    把二分类示例中的模型改成:

    self.net = nn.Sequential(
    nn.Linear(2, 64),
    nn.ReLU(),
    nn.Linear(64, 64),
    nn.ReLU(),
    nn.Linear(64, 32),
    nn.ReLU(),
    nn.Linear(32, 1)
    )

    比较训练速度和验证准确率。

    练习 4:保存并加载模型

    在二分类例子训练结束后保存:

    torch.save(model.state_dict(), "circle_classifier.pt")

    然后新建模型,加载参数,对几个点做推理。

    练习 5:手写一个 Dataset

    写一个 CSVDataset,从 CSV 文件读取特征和标签。可以先假设最后一列是标签,其余列是特征。

    提示:

    import pandas as pd
    import torch
    from torch.utils.data import Dataset

    class CSVDataset(Dataset):
    def __init__(self, path):
    df = pd.read_csv(path)
    self.x = torch.tensor(df.iloc[:, :1].values, dtype=torch.float32)
    self.y = torch.tensor(df.iloc[:, 1].values, dtype=torch.long)

    def __len__(self):
    return len(self.x)

    def __getitem__(self, index):
    return self.x[index], self.y[index]

    15. 参考资料

    • PyTorch 官方安装页:https://pytorch.org/get-started/locally/
    • PyTorch 官方教程首页:https://docs.pytorch.org/tutorials/
    • PyTorch Quickstart:https://docs.pytorch.org/tutorials/beginner/basics/quickstart_tutorial.html
    • Tensor 教程:https://docs.pytorch.org/tutorials/beginner/basics/tensorqs_tutorial.html
    • Autograd 教程:https://docs.pytorch.org/tutorials/beginner/blitz/autograd_tutorial.html

    16. 面试常见问题

    16.1 什么是 PyTorch 的动态图机制?

    PyTorch 默认采用动态图,计算图是在前向传播时动态构建的。它的好处是调试方便、代码直观,适合研究和快速迭代。

    16.2 requires_grad 是什么?

    它表示这个 Tensor 是否需要记录梯度。参数一般需要梯度,输入数据通常不需要。

    16.3 backward() 做了什么?

    它会根据当前标量损失,沿计算图自动反向传播,计算叶子节点的梯度并写入 tensor.grad。

    16.4 为什么训练前要 optimizer.zero_grad()?

    因为 PyTorch 默认会累积梯度,如果不清零,上一次和这一次的梯度会叠加,导致参数更新错误。

    16.5 model.train() 和 model.eval() 有什么区别?

    train() 启用训练行为,eval() 启用推理行为。Dropout、BatchNorm 在这两种模式下行为不同。

    16.6 为什么 CrossEntropyLoss 前面不要再加 Softmax?

    因为 CrossEntropyLoss 内部已经包含了 LogSoftmax 和负对数似然计算,直接传 logits 更稳定。

    16.7 二分类为什么常用 BCEWithLogitsLoss?

    它把 Sigmoid 和二元交叉熵合在一起,数值更稳定,适合输出单个 logit 的二分类任务。

    16.8 state_dict 和直接保存整个模型有什么区别?

    state_dict 只保存参数,轻量、兼容性更好,是推荐方式。直接保存整个模型对象更依赖代码结构,迁移性较差。

    16.9 PyTorch 中 CPU 和 GPU Tensor 可以直接混用吗?

    不可以。模型和数据必须在同一个设备上,否则会报 device 不一致错误。

    16.10 DataLoader 的作用是什么?

    它负责把 Dataset 组织成 batch,并支持打乱、并行加载和最后一批处理。

    16.11 torch.no_grad() 适合什么时候用?

    验证和推理时用。它会关闭梯度记录,减少内存占用并提升速度。

    16.12 detach() 有什么用?

    它会把 Tensor 从当前计算图中分离出来,后续运算不再追踪梯度。

    16.13 nn.Module 为什么要继承?

    这样可以统一管理参数、子模块、前向逻辑和设备迁移,是 PyTorch 组织模型的标准方式。

    16.14 训练时 loss 不下降怎么办?

    优先检查这些点:

    • 学习率是否过大或过小。
    • 标签类型和损失函数是否匹配。
    • 输入输出维度是否正确。
    • 模型是否忘记 train()。
    • 梯度是否正常反传。
    • 数据是否有问题,比如归一化、噪声、标签错误。

    16.15 面试时如何概括 PyTorch 的核心流程?

    可以这样回答:

    先用 Dataset/DataLoader 组织数据,再用 nn.Module 定义模型,选择合适的 loss 和 optimizer,
    训练时做前向传播、算损失、清梯度、反向传播、参数更新,验证和推理时切换到 eval 模式,
    最后保存 state_dict 方便部署和复现。

    训练循环固定模式:

    pred = model(batch_x)
    loss = loss_fn(pred, batch_y)

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    这四步是 PyTorch 入门最重要的肌肉记忆。

    附录:最小训练模板

    以后写 PyTorch 项目,可以从这个模板开始改。

    import torch
    from torch import nn
    from torch.utils.data import DataLoader, TensorDataset

    class Model(nn.Module):
    def __init__(self):
    super().__init__()
    self.net = nn.Sequential(
    nn.Linear(10, 32),
    nn.ReLU(),
    nn.Linear(32, 2)
    )

    def forward(self, x):
    return self.net(x)

    def main():
    torch.manual_seed(42)

    x = torch.randn(1000, 10)
    y = torch.randint(0, 2, (1000,))

    dataset = TensorDataset(x, y)
    loader = DataLoader(dataset, batch_size=32, shuffle=True)

    device = "cuda" if torch.cuda.is_available() else "cpu"
    model = Model().to(device)
    loss_fn = nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)

    for epoch in range(10):
    model.train()
    total_loss = 0.0

    for batch_x, batch_y in loader:
    batch_x = batch_x.to(device)
    batch_y = batch_y.to(device)

    logits = model(batch_x)
    loss = loss_fn(logits, batch_y)

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    total_loss += loss.item() * batch_x.size(0)

    print(f"epoch={epoch + 1}, loss={total_loss / len(dataset):.4f}")

    torch.save(model.state_dict(), "model.pt")

    if __name__ == "__main__":
    main()

    赞(0)
    未经允许不得转载:171主机测评 » PyTorch 学习:从张量到完整训练循环
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址