Python PyTorch 从入门到实战:吃透深度学习,比 TensorFlow 更易上手的框架指南

部署运行你感兴趣的模型镜像

目录

前言:为什么 PyTorch 是深度学习新手的 “最优解”?

一、PyTorch 核心概念:用 “快递系统” 理解 3 大核心组件

1.1 张量(Tensor):深度学习的 “数据快递箱”

1.2 计算图(Computation Graph):动态搭建的 “快递分拣流水线”

1.3 模型(Module):可组装的 “智能分拣机”

二、环境搭建:3 步搞定 PyTorch(Windows/Linux/Mac 通用)

2.1 步骤 1:安装 Anaconda(Python 环境管理器)

2.2 步骤 2:创建并激活 PyTorch 环境

2.3 步骤 3:安装 PyTorch(CPU/GPU 版本)

安装方法(推荐官网生成命令)

示例命令(CPU 版本,通用)

示例命令(GPU 版本,需 NVIDIA 显卡)

2.4 验证安装成功

三、实战 1:线性回归 —— 用 PyTorch 预测房价(最简单的深度学习模型)

3.1 场景说明

3.2 完整代码实现

3.3 关键代码解析

3.4 新手踩坑点

四、实战 2:CNN 图像分类 —— 用 PyTorch 识别 CIFAR-10 数据集(小图片识别)

4.1 场景说明

4.2 完整代码实现

4.3 核心知识点解析

4.4 运行结果与优化

五、实战 3:RNN 文本生成 —— 用 PyTorch 写唐诗(序列数据处理)

5.1 场景说明

5.2 完整代码实现(简化版,聚焦核心逻辑)

5.3 核心知识点解析

5.4 运行结果与优化

六、PyTorch 新手避坑指南:10 个高频错误及解决方案

6.1 坑 1:张量在 CPU 和 GPU 之间不匹配

6.2 坑 2:忘记转换数据类型

6.3 坑 3:梯度计算相关错误

6.4 坑 4:数据形状不符合模型要求

6.5 坑 5:训练时未切换模型模式

6.6 坑 6:学习率设置不当

6.7 坑 7:数据加载时num_workers导致错误(Windows)

6.8 坑 8:模型保存与加载错误

6.9 坑 9:过拟合(训练准确率高,测试准确率低)

6.10 坑 10:张量无法转换为 NumPy(GPU 张量)

七、PyTorch 学习路线:从入门到实战高手

7.1 入门阶段(1~2 个月)

7.2 进阶阶段(2~3 个月)

7.3 实战阶段(3~6 个月)

7.4 高级阶段(6 个月以上)

八、总结:PyTorch 入门的核心是 “动手实战”


class 卑微码农:
    def __init__(self):
        self.技能 = ['能读懂十年前祖传代码', '擅长用Ctrl+C/V搭建世界', '信奉"能跑就别动"的玄学']
        self.发量 = 100  # 初始发量
        self.咖啡因耐受度 = '极限'
        
    def 修Bug(self, bug):
        try:
            # 试图用玄学解决问题
            if bug.严重程度 == '离谱':
                print("这一定是环境问题!")
            else:
                print("让我看看是谁又没写注释...哦,是我自己。")
        except Exception as e:
            # 如果try块都救不了,那就...
            print("重启一下试试?")
            self.发量 -= 1  # 每解决一个bug,头发-1
 
 
# 实例化一个我
我 = 卑微码农()

前言:为什么 PyTorch 是深度学习新手的 “最优解”?

你可能听说过 “深度学习框架” 这个词,但面对 TensorFlow、PyTorch、Keras 这些名字时,是不是像面对满桌菜系不知从何下筷?如果让我给新手推荐,PyTorch 一定是首选 —— 它就像深度学习界的 “Python”,简洁、灵活、易上手,连 Facebook(现 Meta)、特斯拉、斯坦福大学都在用它做科研和生产。

为什么说 PyTorch 比其他框架更适合新手?用一个比喻就能明白:

  • 如果你把深度学习模型比作 “乐高机器人”,PyTorch 就是 “散装乐高积木”—— 你可以边拼边改,哪里不合适当场拆开重搭,调试起来一目了然;
  • 而有些框架更像 “预制板建筑”—— 必须先画好完整图纸(定义计算图),才能动工,中途改设计要从头再来,新手很容易被劝退。

这篇博客我会用 “大白话 + 可运行代码” 的方式,从 0 开始带大家掌握 PyTorch:从环境搭建到核心概念,再到实战项目(房价预测、图像分类、唐诗生成),全程无晦涩公式,无学术黑话。哪怕你是刚学 Python 的新手,跟着敲代码也能快速入门,干货密度拉满!

一、PyTorch 核心概念:用 “快递系统” 理解 3 大核心组件

在写代码之前,我们先搞懂 PyTorch 的 3 个核心概念 ——张量(Tensor)、计算图(Computation Graph)、模型(Module)。用 “快递系统” 来类比,瞬间就能明白:

1.1 张量(Tensor):深度学习的 “数据快递箱”

张量是 PyTorch 中存储和处理数据的基本单位,简单说就是 “带 GPU 加速的多维数组”。就像快递系统中不同规格的箱子:

  • 标量(0 维张量):单个数值,比如 “1kg 苹果”,对应 Python 的 int/float。示例:torch.tensor(3.14)
  • 向量(1 维张量):一串数据,比如 “苹果 1kg、香蕉 2kg、橙子 3kg”,对应 Python 列表。示例:torch.tensor([1, 2, 3])
  • 矩阵(2 维张量):表格状数据,比如 “3 个客户的订单明细”,对应 Excel 表格。示例:torch.tensor([[1,2], [3,4], [5,6]])
  • 高阶张量(3 维及以上):多层表格叠起来,比如 “10 天内每天 3 个客户的订单”,常用在图像(高度 × 宽度 × 通道)、文本(句子数 × 单词数 × 词向量)等场景。示例:torch.tensor([[[1,2], [3,4]], [[5,6], [7,8]]])

实战代码:创建和操作张量PyTorch 的张量操作和 NumPy 几乎一致,新手可以无缝迁移知识:

import torch
import numpy as np

# 1. 创建张量(支持从Python数据、NumPy数组转换)
# 标量
scalar = torch.tensor(3.14)
print("标量张量:", scalar)  # 输出:tensor(3.1400)
print("标量值:", scalar.item())  # 转Python值:3.14

# 向量
vector = torch.tensor([1, 2, 3, 4])
print("向量张量:", vector)  # 输出:tensor([1, 2, 3, 4])
print("向量形状:", vector.shape)  # 输出:torch.Size([4])

# 矩阵
matrix = torch.tensor([[1, 2], [3, 4], [5, 6]])
print("矩阵张量:\n", matrix)
# 输出:
# tensor([[1, 2],
#         [3, 4],
#         [5, 6]])

# 2. 张量运算(加减乘除、矩阵乘法等)
a = torch.tensor([1, 2])
b = torch.tensor([3, 4])
print("a + b:", a + b)  # 输出:tensor([4, 6])
print("a * b:", a * b)  # 输出:tensor([3, 8])

# 矩阵乘法(用@符号或torch.matmul)
matrix1 = torch.tensor([[1, 2], [3, 4]])
matrix2 = torch.tensor([[5, 6], [7, 8]])
print("矩阵乘法:\n", matrix1 @ matrix2)
# 输出:
# tensor([[19, 22],
#         [43, 50]])

# 3. 张量变形(reshape)
tensor = torch.tensor([1, 2, 3, 4, 5, 6])
reshaped = tensor.reshape(2, 3)  # 改成2行3列
print("变形后:\n", reshaped)
# 输出:
# tensor([[1, 2, 3],
#         [4, 5, 6]])

# 4. GPU加速(关键优势!)
if torch.cuda.is_available():
    # 把张量移到GPU
    tensor_gpu = tensor.to('cuda')
    print("GPU张量:", tensor_gpu)  # 输出带device='cuda:0'
else:
    print("未检测到GPU,使用CPU")

核心优势:PyTorch 张量支持无缝切换 CPU/GPU,只需to('cuda')就能利用显卡加速计算,这对训练大型模型至关重要。

1.2 计算图(Computation Graph):动态搭建的 “快递分拣流水线”

计算图是 PyTorch 的 “灵魂”,它记录了张量的所有运算步骤,就像快递系统的 “分拣流水线”:包裹(张量)从起点进入,经过扫描、分类、运输(运算),最终送到终点(输出结果)。

但 PyTorch 的计算图是 “动态的”—— 你可以边运行边修改流水线。比如发现某个分拣步骤错了,不用停掉整条线,直接在运行中调整,这就是 PyTorch 比其他框架更灵活的原因。

最核心的应用:自动求导(反向传播)深度学习的本质是 “通过数据调整模型参数”,而参数调整依赖 “梯度”(导数)。PyTorch 的计算图能自动计算梯度,省去手动推导公式的麻烦:

# 自动求导示例:y = 2x² + 3x + 1,求x=1时的导数dy/dx
x = torch.tensor(1.0, requires_grad=True)  # requires_grad=True:需要计算梯度
y = 2 * x**2 + 3 * x + 1

# 反向传播:计算梯度(从y往x回溯)
y.backward()

# 查看x的梯度(dy/dx = 4x + 3,x=1时结果为7)
print("x的梯度:", x.grad)  # 输出:tensor(7.0)

这个过程就像:知道快递最终送达时间(y),自动反推每个分拣步骤(x 的运算)对时间的影响(梯度),从而优化流程 —— 这就是模型 “学习” 的核心逻辑。

1.3 模型(Module):可组装的 “智能分拣机”

如果说计算图是 “流水线步骤”,那模型就是 “完整的智能分拣机”—— 把一系列运算(线性层、卷积层等)组装起来,形成一个能 “输入数据、输出结果” 的完整系统。

PyTorch 用torch.nn.Module来定义模型,就像用乐高积木拼机器,每个积木是一个 “层”(如Linear线性层、Conv2d卷积层):

import torch.nn as nn

# 定义一个简单的线性回归模型(预测y = wx + b)
class LinearRegression(nn.Module):
    def __init__(self):
        super().__init__()  # 初始化父类
        # 定义一个线性层:1个输入特征→1个输出特征
        self.linear = nn.Linear(in_features=1, out_features=1)
    
    # 定义前向传播(数据流动路径)
    def forward(self, x):
        return self.linear(x)  # 输入x经过线性层处理

# 创建模型实例
model = LinearRegression()
print("模型结构:", model)
# 输出:
# LinearRegression(
#   (linear): Linear(in_features=1, out_features=1, bias=True)
# )

# 测试模型:输入一个x,看是否能输出预测值
x_test = torch.tensor([[2.0]])  # 注意形状:(样本数, 特征数)
y_pred = model(x_test)
print("预测结果:", y_pred)  # 输出随机值(未训练的模型参数随机)

forward方法是模型的 “核心”,定义了数据如何从输入经过各层处理,最终输出结果。训练模型的过程,就是调整这些层的参数(如wb),让预测结果越来越接近真实值。

二、环境搭建:3 步搞定 PyTorch(Windows/Linux/Mac 通用)

PyTorch 的环境搭建比想象中简单,推荐用 “Anaconda+PyTorch” 组合,避免版本冲突,新手也能轻松搞定。

2.1 步骤 1:安装 Anaconda(Python 环境管理器)

Anaconda 就像 “Python 环境的虚拟机”,能为不同项目创建独立环境,避免依赖冲突。

  • 下载地址:https://www.anaconda.com/products/distribution
  • 安装步骤:
    1. 双击安装包,勾选 “Add Anaconda3 to my PATH environment variable”(自动配置环境变量);
    2. 其他默认下一步,等待安装完成。

验证安装:打开终端(Windows 用 “Anaconda Prompt”),输入conda --version,输出版本号(如conda 23.11.0)说明成功。

2.2 步骤 2:创建并激活 PyTorch 环境

# 1. 创建环境(Python 3.9,环境名pytorch-env)
conda create -n pytorch-env python=3.9

# 2. 激活环境(Windows)
conda activate pytorch-env

# 2. 激活环境(Linux/Mac)
source activate pytorch-env

激活后终端前缀会显示(pytorch-env),表示当前在 PyTorch 环境中。

2.3 步骤 3:安装 PyTorch(CPU/GPU 版本)

PyTorch 分为 CPU 和 GPU 版本,按需选择:

  • CPU 版本:适合新手、无独立显卡的电脑,安装简单;
  • GPU 版本:需要 NVIDIA 显卡(支持 CUDA),训练速度比 CPU 快 10~100 倍。
安装方法(推荐官网生成命令)
  1. 打开 PyTorch 官网:https://pytorch.org/get-started/locally/
  2. 按自己的系统和需求选择:
    • 系统(Windows/Linux/Mac);
    • 安装方式(Pip);
    • 计算平台(CPU 或 CUDA 版本,如 CUDA 11.8)。
  3. 复制生成的命令,在终端执行。
示例命令(CPU 版本,通用)
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
示例命令(GPU 版本,需 NVIDIA 显卡)
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

2.4 验证安装成功

在终端输入python,进入 Python 环境,执行:

import torch
import torchvision

# 查看版本
print("PyTorch版本:", torch.__version__)  # 输出如2.1.0

# 检查GPU是否可用
print("GPU是否可用:", torch.cuda.is_available())  # GPU版本输出True,CPU版本False

无报错且版本号正常,说明环境搭建成功!

三、实战 1:线性回归 —— 用 PyTorch 预测房价(最简单的深度学习模型)

线性回归是入门深度学习的 “Hello World”,核心是学习 “输入特征 x” 和 “输出值 y” 的线性关系(y = wx + b)。我们用 “房屋面积预测房价” 的场景实战,让模型学会根据面积估算价格。

3.1 场景说明

假设有一组数据:5 套房子的 “面积(平方米)” 和 “价格(万元)”,我们要搭建模型,根据面积预测房价。

房屋面积(x)房价(y)
50100
70140
90180
110220
130260

从数据能看出规律:房价≈面积 ×2(比如 50×2=100),模型需要 “学会” 这个关系。

3.2 完整代码实现

import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt

# ---------------------- 1. 准备数据 ----------------------
# 输入特征:房屋面积(5个样本,每个样本1个特征)
x = torch.tensor([[50.0], [70.0], [90.0], [110.0], [130.0]])
# 标签:房价(5个样本,每个样本1个标签)
y = torch.tensor([[100.0], [140.0], [180.0], [220.0], [260.0]])

# 可视化原始数据
plt.scatter(x.numpy(), y.numpy(), color='blue', label='真实数据')
plt.xlabel('房屋面积(平方米)')
plt.ylabel('房价(万元)')
plt.title('房屋面积与房价关系')
plt.legend()
plt.show()

# ---------------------- 2. 定义模型 ----------------------
class LinearRegressionModel(nn.Module):
    def __init__(self):
        super().__init__()
        # 线性层:1个输入特征(面积)→1个输出特征(房价)
        self.linear = nn.Linear(in_features=1, out_features=1)
    
    def forward(self, x):
        return self.linear(x)  # 前向传播:x→线性层→输出

# 创建模型实例
model = LinearRegressionModel()

# ---------------------- 3. 定义损失函数和优化器 ----------------------
# 损失函数:均方误差(MSE),衡量预测值与真实值的差距
criterion = nn.MSELoss()

# 优化器:随机梯度下降(SGD),用于更新模型参数(w和b)
# lr=0.0001:学习率(步长,控制参数更新幅度)
optimizer = torch.optim.SGD(model.parameters(), lr=0.0001)

# ---------------------- 4. 训练模型 ----------------------
epochs = 10000  # 训练轮数(整个数据集训练10000次)
losses = []  # 记录每轮损失值

for epoch in range(epochs):
    # 前向传播:用当前模型预测房价
    y_pred = model(x)
    
    # 计算损失(预测值与真实值的差距)
    loss = criterion(y_pred, y)
    losses.append(loss.item())  # 保存损失值
    
    # 反向传播:计算梯度(自动求导)
    optimizer.zero_grad()  # 清空上一轮梯度(避免累积)
    loss.backward()        # 计算参数梯度
    
    # 更新参数(w和b)
    optimizer.step()
    
    # 每1000轮打印一次损失
    if (epoch + 1) % 1000 == 0:
        print(f'轮数:{epoch+1}, 损失:{loss.item():.4f}')

# ---------------------- 5. 可视化训练过程 ----------------------
plt.plot(range(epochs), losses, color='red')
plt.xlabel('训练轮数')
plt.ylabel('损失值(MSE)')
plt.title('训练过程中损失的变化')
plt.show()

# ---------------------- 6. 查看模型学到的参数 ----------------------
# 线性层的参数:weight(w)和bias(b)
w = model.linear.weight.item()
b = model.linear.bias.item()
print(f'模型学到的参数:w={w:.2f}, b={b:.2f}')
print(f'预测公式:房价 = {w:.2f} × 面积 + {b:.2f}')  # 接近 房价=2×面积+0

# ---------------------- 7. 模型预测 ----------------------
# 预测新数据:80平方米和150平方米的房价
x_new = torch.tensor([[80.0], [150.0]])
y_new_pred = model(x_new)

print(f'80平方米房价预测:{y_new_pred[0].item():.2f}万元(真实值约160万元)')
print(f'150平方米房价预测:{y_new_pred[1].item():.2f}万元(真实值约300万元)')

# 可视化预测结果(真实数据+预测直线)
x_range = torch.tensor([[40.0], [160.0]])  # 生成一系列面积值
y_range_pred = model(x_range)

plt.scatter(x.numpy(), y.numpy(), color='blue', label='真实数据')
plt.plot(x_range.numpy(), y_range_pred.detach().numpy(), 'r-', label='预测直线')
plt.scatter(x_new.numpy(), y_new_pred.detach().numpy(), color='green', s=100, label='新预测')
plt.xlabel('房屋面积(平方米)')
plt.ylabel('房价(万元)')
plt.title('房价预测:真实数据与模型预测')
plt.legend()
plt.show()

3.3 关键代码解析

  1. 数据准备:用torch.tensor创建输入 x(面积)和标签 y(房价),注意形状必须是(样本数, 特征数)(如(5,1));
  2. 模型定义:继承nn.Module,用nn.Linear定义线性层,forward方法指定数据流动路径;
  3. 损失函数与优化器
    • 损失函数MSELoss:计算预测值与真实值的平均平方差,值越小说明预测越准;
    • 优化器SGD:通过梯度下降更新参数(w 和 b),lr(学习率)是关键超参数,太大可能跳过最优值,太小则训练慢;
  4. 训练循环:核心是 “前向传播→计算损失→反向传播→更新参数” 四步,重复多轮直到损失足够小;
  5. 参数查看:训练后模型的weight(w)接近 2,bias(b)接近 0,完美学到 “房价 = 2× 面积” 的规律。

3.4 新手踩坑点

  • 数据形状错误:输入 x 必须是二维张量(样本数, 特征数),如果写成[50,70,90](1 维)会报错,需用reshape(-1,1)转换;
  • 学习率设置lr=0.001可能导致损失震荡不下降,lr=0.00001则训练太慢,建议从0.0001开始尝试;
  • 梯度清零optimizer.zero_grad()必须在loss.backward()前调用,否则梯度会累积,导致参数更新混乱。

四、实战 2:CNN 图像分类 —— 用 PyTorch 识别 CIFAR-10 数据集(小图片识别)

线性回归是 “数值预测”,而图像分类是 “类别预测”。我们用卷积神经网络(CNN) 识别 CIFAR-10 数据集(包含飞机、汽车、鸟等 10 类小图片),CNN 能自动提取图像特征(如边缘、纹理),是处理图像的 “神器”。

4.1 场景说明

CIFAR-10 数据集包含 60000 张 32×32 像素的彩色图片(3 通道:RGB),分为 10 类(每类 6000 张):['飞机', '汽车', '鸟', '猫', '鹿', '狗', '青蛙', '马', '船', '卡车']我们要搭建 CNN 模型,输入一张图片,输出它属于哪一类。

4.2 完整代码实现

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np

# ---------------------- 1. 加载并预处理数据集 ----------------------
# 数据转换:将图片转为张量,并标准化(均值、标准差为ImageNet数据集的统计值)
transform = transforms.Compose([
    transforms.ToTensor(),  # 转为张量(0~1)
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))  # 标准化到[-1,1]
])

# 加载CIFAR-10训练集和测试集(自动下载到./data目录)
trainset = torchvision.datasets.CIFAR10(
    root='./data', train=True, download=True, transform=transform
)
testset = torchvision.datasets.CIFAR10(
    root='./data', train=False, download=True, transform=transform
)

# 数据加载器(批量加载数据,支持打乱顺序、多线程)
trainloader = torch.utils.data.DataLoader(
    trainset, batch_size=64, shuffle=True, num_workers=2
)
testloader = torch.utils.data.DataLoader(
    testset, batch_size=64, shuffle=False, num_workers=2
)

# 类别名称
classes = ('飞机', '汽车', '鸟', '猫', '鹿', '狗', '青蛙', '马', '船', '卡车')

# ---------------------- 2. 可视化训练集图片 ----------------------
def imshow(img):
    img = img / 2 + 0.5  # 反标准化(从[-1,1]转回[0,1])
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))  # 转换通道顺序(C×H×W→H×W×C)
    plt.axis('off')

# 获取一批训练数据
dataiter = iter(trainloader)
images, labels = next(dataiter)

# 显示前6张图片
plt.figure(figsize=(10, 4))
for i in range(6):
    plt.subplot(1, 6, i+1)
    imshow(images[i])
    plt.title(classes[labels[i]])
plt.tight_layout()
plt.show()

# ---------------------- 3. 定义CNN模型 ----------------------
class CNN(nn.Module):
    def __init__(self):
        super().__init__()
        # 卷积部分:提取图像特征
        self.conv_layers = nn.Sequential(
            # 第1卷积块:卷积→激活→池化
            nn.Conv2d(3, 32, 3, padding=1),  # 3通道→32通道,3×3卷积核, padding=1保持尺寸
            nn.ReLU(),  # 激活函数(引入非线性)
            nn.MaxPool2d(2, 2),  # 2×2最大池化,尺寸减半(32×32→16×16)
            
            # 第2卷积块
            nn.Conv2d(32, 64, 3, padding=1),  # 32通道→64通道
            nn.ReLU(),
            nn.MaxPool2d(2, 2),  # 16×16→8×8
            
            # 第3卷积块
            nn.Conv2d(64, 64, 3, padding=1),  # 64通道→64通道
            nn.ReLU(),
            nn.MaxPool2d(2, 2)  # 8×8→4×4
        )
        
        # 全连接部分:分类
        self.fc_layers = nn.Sequential(
            nn.Flatten(),  # 展平特征图(64×4×4→1024)
            nn.Linear(64 * 4 * 4, 64),  # 1024→64
            nn.ReLU(),
            nn.Linear(64, 10)  # 64→10(10个类别)
        )
    
    def forward(self, x):
        x = self.conv_layers(x)  # 卷积部分处理
        x = self.fc_layers(x)    # 全连接部分分类
        return x

# 创建模型实例(自动检测GPU,优先使用GPU)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model = CNN().to(device)
print("模型运行设备:", device)
print("模型结构:\n", model)

# ---------------------- 4. 定义损失函数和优化器 ----------------------
criterion = nn.CrossEntropyLoss()  # 交叉熵损失(多分类问题常用)
optimizer = optim.Adam(model.parameters(), lr=0.001)  # Adam优化器(比SGD更智能)

# ---------------------- 5. 训练模型 ----------------------
epochs = 10  # 训练10轮
train_losses = []
train_accs = []

for epoch in range(epochs):
    running_loss = 0.0
    correct = 0
    total = 0
    
    # 遍历训练集
    for i, data in enumerate(trainloader, 0):
        # 获取输入和标签,并移到GPU(如果可用)
        inputs, labels = data[0].to(device), data[1].to(device)
        
        # 清零梯度
        optimizer.zero_grad()
        
        # 前向传播→计算损失→反向传播→更新参数
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        
        # 统计损失和准确率
        running_loss += loss.item()
        _, predicted = torch.max(outputs.data, 1)  # 取概率最大的类别
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
        
        # 每200批打印一次中间结果
        if i % 200 == 199:
            print(f'轮数:{epoch+1}, 批次:{i+1}, 平均损失:{running_loss/200:.3f}')
            running_loss = 0.0
    
    # 计算本轮训练的准确率
    train_acc = 100 * correct / total
    train_losses.append(running_loss / len(trainloader))
    train_accs.append(train_acc)
    print(f'第{epoch+1}轮训练准确率:{train_acc:.2f}%')

print('训练完成!')

# ---------------------- 6. 在测试集上评估模型 ----------------------
model.eval()  # 切换到评估模式(关闭Dropout等)
correct = 0
total = 0
class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))

with torch.no_grad():  # 关闭梯度计算(节省内存)
    for data in testloader:
        images, labels = data[0].to(device), data[1].to(device)
        outputs = model(images)
        _, predicted = torch.max(outputs, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
        
        # 按类别统计准确率
        c = (predicted == labels).squeeze()
        for i in range(labels.size(0)):
            label = labels[i]
            class_correct[label] += c[i].item()
            class_total[label] += 1

test_acc = 100 * correct / total
print(f'测试集总准确率:{test_acc:.2f}%')

# 打印每个类别的准确率
print("\n各类别准确率:")
for i in range(10):
    print(f'{classes[i]}: {100 * class_correct[i] / class_total[i]:.2f}%')

# ---------------------- 7. 可视化预测结果 ----------------------
# 获取测试集图片并预测
dataiter = iter(testloader)
images, labels = next(dataiter)
images_gpu = images.to(device)
outputs = model(images_gpu)
_, predicted = torch.max(outputs, 1)

# 显示前6张图片的预测结果
plt.figure(figsize=(10, 4))
for i in range(6):
    plt.subplot(1, 6, i+1)
    imshow(images[i])
    # 标题:真实标签 vs 预测标签(正确绿色,错误红色)
    color = 'green' if predicted[i] == labels[i] else 'red'
    plt.title(f'真实:{classes[labels[i]]}\n预测:{classes[predicted[i]]}', color=color)
plt.tight_layout()
plt.show()

# ---------------------- 8. 保存模型 ----------------------
torch.save(model.state_dict(), 'cifar10_cnn.pth')
print("\n模型参数已保存为:cifar10_cnn.pth")

4.3 核心知识点解析

  1. CNN 为什么适合图像?

    • 卷积层(Conv2d):用卷积核 “滑动扫描” 图片,提取局部特征(如边缘、角点),32/64 是卷积核数量(越多特征越丰富);
    • 池化层(MaxPool2d):缩小图片尺寸(如 32×32→16×16),减少计算量,同时保留关键特征;
    • 全连接层(Linear):将卷积层提取的特征 “汇总”,输出 10 个类别的概率。
  2. 数据预处理关键步骤

    • ToTensor():将图片(0~255)转为张量(0~1);
    • Normalize():标准化到 [-1,1],让模型训练更稳定;
    • DataLoader:批量加载数据,支持多线程加速,shuffle=True打乱训练集顺序。
  3. 训练技巧

    • 设备切换:to(device)将模型和数据移到 GPU,训练速度提升明显;
    • 评估模式:model.eval()关闭 Dropout、BatchNorm 等训练特有的层,确保评估准确;
    • 梯度关闭:with torch.no_grad()在评估时关闭梯度计算,节省内存。

4.4 运行结果与优化

  • 训练 10 轮后,测试集准确率约 70%~75%,能正确识别大部分图片;
  • 优化方向:增加训练轮数(20~30 轮)、添加nn.Dropout(0.5)防止过拟合、使用数据增强(transforms.RandomHorizontalFlip()等)、调整学习率。

五、实战 3:RNN 文本生成 —— 用 PyTorch 写唐诗(序列数据处理)

图像用 CNN,文本等 “序列数据”(按顺序排列的数据)用循环神经网络(RNN/LSTM) 处理。我们用 LSTM 模型学习唐诗的规律,自动生成新的唐诗(比如输入 “床前明月光”,续写完整诗句)。

5.1 场景说明

用唐诗数据集(约 5 万首)训练模型,模型输入一句诗的前几个字,能自动预测下一个字,逐步生成完整诗句。例如:输入:“春眠不觉晓”生成:“春眠不觉晓,处处闻啼鸟。夜来风雨声,花落知多少。”

5.2 完整代码实现(简化版,聚焦核心逻辑)

import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import os
import re
from collections import Counter

# ---------------------- 1. 准备数据(唐诗预处理) ----------------------
# 加载唐诗数据集(假设数据文件为tangshi.txt,每行一首诗)
def load_data(path='tangshi.txt'):
    if not os.path.exists(path):
        # 实际使用时可下载数据集,这里用示例数据替代
        with open(path, 'w', encoding='utf-8') as f:
            f.write('床前明月光,疑是地上霜。举头望明月,低头思故乡。\n')
            f.write('春眠不觉晓,处处闻啼鸟。夜来风雨声,花落知多少。\n')
            f.write('锄禾日当午,汗滴禾下土。谁知盘中餐,粒粒皆辛苦。\n')
    
    with open(path, 'r', encoding='utf-8') as f:
        poems = f.readlines()
    
    # 数据清洗:保留中文、逗号、句号
    poems = [re.sub(r'[^\u4e00-\u9fa5,。]', '', p) for p in poems]
    poems = [p for p in poems if len(p) > 5]  # 过滤太短的诗
    return poems

# 加载并预处理数据
poems = load_data()
print(f'加载唐诗数量:{len(poems)}')
print('示例唐诗:', poems[:3])

# 构建字表(将字映射到索引)
all_chars = [c for poem in poems for c in poem]
char_counts = Counter(all_chars)
chars = [c for c, _ in char_counts.most_common()]  # 按频率排序
char2idx = {c: i+1 for i, c in enumerate(chars)}  # 索引从1开始,0留作填充
char2idx['<PAD>'] = 0  # 填充标记
idx2char = {i: c for c, i in char2idx.items()}
vocab_size = len(char2idx)  # 词汇表大小
print(f'词汇表大小(不同字的数量):{vocab_size}')

# 生成训练数据(输入:前n个字,输出:下一个字)
seq_length = 5  # 输入序列长度(用前5个字预测第6个)
input_seqs = []
target_chars = []

for poem in poems:
    # 将诗转为索引序列
    poem_idx = [char2idx[c] for c in poem if c in char2idx]
    # 滑动窗口生成样本
    for i in range(len(poem_idx) - seq_length):
        input_seqs.append(poem_idx[i:i+seq_length])  # 输入:前5个索引
        target_chars.append(poem_idx[i+seq_length])  # 输出:第6个索引

# 转为张量
input_tensor = torch.tensor(input_seqs, dtype=torch.long)
target_tensor = torch.tensor(target_chars, dtype=torch.long)
print(f'训练样本数量:{len(input_tensor)}')
print(f'输入形状:{input_tensor.shape},目标形状:{target_tensor.shape}')

# 创建数据集和数据加载器
dataset = torch.utils.data.TensorDataset(input_tensor, target_tensor)
dataloader = torch.utils.data.DataLoader(
    dataset, batch_size=32, shuffle=True
)

# ---------------------- 2. 定义LSTM模型 ----------------------
class PoetryLSTM(nn.Module):
    def __init__(self, vocab_size, embedding_dim=128, hidden_dim=256):
        super().__init__()
        self.embedding = nn.Embedding(
            num_embeddings=vocab_size,  # 词汇表大小
            embedding_dim=embedding_dim  # 字向量维度(每个字用128维向量表示)
        )
        self.lstm = nn.LSTM(
            input_size=embedding_dim,
            hidden_size=hidden_dim,  # LSTM隐藏层维度
            batch_first=True  # 输入形状为(batch, seq_len, feature)
        )
        self.fc = nn.Linear(hidden_dim, vocab_size)  # 输出层:预测下一个字的概率
    
    def forward(self, x, hidden=None):
        # 嵌入层:索引→字向量(batch, seq_len)→(batch, seq_len, embedding_dim)
        x = self.embedding(x)
        
        # LSTM层:返回输出和隐藏状态
        lstm_out, hidden = self.lstm(x, hidden)  # hidden=(h0, c0)
        
        # 取最后一个时间步的输出(用于预测下一个字)
        last_out = lstm_out[:, -1, :]
        
        # 输出层:预测每个字的概率
        output = self.fc(last_out)
        return output, hidden

# 创建模型(使用GPU)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model = PoetryLSTM(vocab_size).to(device)
print("模型结构:\n", model)

# ---------------------- 3. 定义损失函数和优化器 ----------------------
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# ---------------------- 4. 训练模型 ----------------------
epochs = 20  # 训练20轮

for epoch in range(epochs):
    running_loss = 0.0
    for i, (inputs, targets) in enumerate(dataloader):
        inputs, targets = inputs.to(device), targets.to(device)
        
        # 清零梯度
        optimizer.zero_grad()
        
        # 前向传播
        outputs, _ = model(inputs)
        loss = criterion(outputs, targets)
        
        # 反向传播+更新参数
        loss.backward()
        optimizer.step()
        
        running_loss += loss.item()
    
    # 打印每轮损失
    avg_loss = running_loss / len(dataloader)
    print(f'轮数:{epoch+1}, 平均损失:{avg_loss:.4f}')

print('训练完成!')

# ---------------------- 5. 生成唐诗 ----------------------
def generate_poem(start_str, max_len=20, temperature=0.7):
    """
    生成唐诗
    start_str:起始字符串(如“床前明月光”)
    temperature:控制生成多样性(0~1,越小越保守,越大越随机)
    """
    model.eval()  # 评估模式
    with torch.no_grad():
        # 起始字符串转为索引
        input_seq = [char2idx[c] for c in start_str if c in char2idx]
        if len(input_seq) < seq_length:
            # 不足seq_length时填充
            input_seq = [0]*(seq_length - len(input_seq)) + input_seq
        input_tensor = torch.tensor([input_seq], dtype=torch.long).to(device)
        
        poem = start_str  # 生成的诗
        hidden = None  # LSTM隐藏状态
        
        for _ in range(max_len):
            # 预测下一个字
            output, hidden = model(input_tensor, hidden)
            
            # 按temperature调整概率分布
            output = output / temperature
            probs = torch.softmax(output, dim=1).cpu().numpy()[0]
            
            # 按概率采样下一个字(不是取最大概率,增加多样性)
            next_idx = np.random.choice(len(probs), p=probs)
            next_char = idx2char[next_idx]
            
            # 更新诗和输入序列
            poem += next_char
            input_seq = input_seq[1:] + [next_idx]
            input_tensor = torch.tensor([input_seq], dtype=torch.long).to(device)
            
            # 如果遇到句号,结束生成
            if next_char == '。':
                break
        
        return poem

# 测试生成功能
print("\n生成唐诗示例:")
print(generate_poem("床前明月光", max_len=30))
print(generate_poem("春眠不觉晓", max_len=30))
print(generate_poem("锄禾日当午", max_len=30))

# 保存模型
torch.save(model.state_dict(), 'tangshi_lstm.pth')
print("\n模型参数已保存为:tangshi_lstm.pth")

5.3 核心知识点解析

  1. 文本预处理关键步骤

    • 构建字表:将每个字映射到唯一索引(如 “床”→1,“前”→2),让模型能处理文本;
    • 生成样本:用滑动窗口截取序列(如 “床前明月光”→输入 “床前明月”,输出 “光”),让模型学习 “上下文→下一个字” 的规律。
  2. LSTM 模型核心组件

    • 嵌入层(Embedding):将字索引转为低维向量(如 128 维),捕捉字的语义(比如 “月” 和 “明” 的向量距离近);
    • LSTM 层:处理序列数据,记住前文信息(比如 “举头望” 后面更可能接 “明月”);
    • 输出层:预测下一个字的概率分布。
  3. 文本生成技巧

    • 温度参数(temperature):控制生成多样性,0.7 左右效果较好(太接近 0 会重复,太大则混乱);
    • 采样而非取最大概率:用np.random.choice按概率采样,避免生成固定句式。

5.4 运行结果与优化

  • 用示例数据训练后,生成的诗句可能不太通顺,但能模仿唐诗的格式(五言、带标点);
  • 优化方向:使用更大的唐诗数据集(数万首)、增加训练轮数(50~100 轮)、调大embedding_dimhidden_dim、使用双向 LSTM(nn.LSTM(bidirectional=True))。

六、PyTorch 新手避坑指南:10 个高频错误及解决方案

学习 PyTorch 时,新手很容易在细节上踩坑,这里总结 10 个最常见的问题及解决方法:

6.1 坑 1:张量在 CPU 和 GPU 之间不匹配

现象:报错 “RuntimeError: Expected all tensors to be on the same device”。原因:模型在 GPU,输入数据在 CPU(或反之),设备不一致。解决方案:确保模型和数据在同一设备:

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)  # 模型移到设备
inputs = inputs.to(device)  # 数据移到设备

6.2 坑 2:忘记转换数据类型

现象:报错 “RuntimeError: Expected floating point tensor for input, got Long tensor”。原因:输入数据是LongTensor(整数),但模型需要FloatTensor(浮点数)。解决方案:用.float()转换:

inputs = inputs.float()  # 转为浮点型

6.3 坑 3:梯度计算相关错误

现象:报错 “RuntimeError: Trying to backward through a tensor that does not require gradients”。原因:对不需要梯度的张量调用backward()解决方案:创建张量时指定requires_grad=True(仅对需要求导的参数):

x = torch.tensor(1.0, requires_grad=True)  # 需要计算梯度

6.4 坑 4:数据形状不符合模型要求

现象:报错 “RuntimeError: Expected 4-dimensional input for 4-dimensional weight, but got 3-dimensional input”。原因:CNN 输入需要 4 维(batch×channel×height×width),但给了 3 维(少了 batch 维度)。解决方案:用unsqueeze(0)增加 batch 维度:

img = img.unsqueeze(0)  # 从(3,32,32)→(1,3,32,32)

6.5 坑 5:训练时未切换模型模式

现象:评估时准确率波动大,和训练时差距大。原因:模型在train模式(默认),DropoutBatchNorm等层会影响评估。解决方案:评估前切换到eval模式:

model.eval()  # 评估模式
with torch.no_grad():  # 关闭梯度计算
    # 评估代码...

6.6 坑 6:学习率设置不当

现象:损失不下降(太大)或下降极慢(太小)。解决方案:从0.001开始尝试,用学习率调度器动态调整:

# 学习率每3轮乘以0.1
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=3, gamma=0.1)
# 训练循环中调用
scheduler.step()

6.7 坑 7:数据加载时num_workers导致错误(Windows)

现象:Windows 系统中,num_workers>0时报错 “BrokenPipeError”。原因:Windows 多进程支持问题。解决方案:Windows 下num_workers=0,或用if __name__ == '__main__':包裹主代码。

6.8 坑 8:模型保存与加载错误

现象:加载模型时参数不匹配,报错 “size mismatch”。原因:保存 / 加载方式不对,或模型结构有变化。解决方案

  • 保存参数:torch.save(model.state_dict(), 'model.pth')
  • 加载参数:model.load_state_dict(torch.load('model.pth'))

6.9 坑 9:过拟合(训练准确率高,测试准确率低)

现象:模型在训练集表现好,在新数据上表现差。解决方案

  • 添加nn.Dropout(0.5)层随机丢弃神经元;
  • 增加数据量或使用数据增强;
  • 减少模型参数(如减小隐藏层维度)。

6.10 坑 10:张量无法转换为 NumPy(GPU 张量)

现象:报错 “TypeError: can't convert cuda:0 device type tensor to numpy”。原因:GPU 上的张量不能直接转 NumPy。解决方案:先移到 CPU 再转换:

tensor_cpu = tensor.cpu()  # 移到CPU
arr = tensor_cpu.numpy()   # 转NumPy

七、PyTorch 学习路线:从入门到实战高手

掌握基础后,可按以下路线进阶,逐步成为 PyTorch 高手:

7.1 入门阶段(1~2 个月)

  • 核心 API:熟练使用张量操作、nn.Module、损失函数、优化器;
  • 基础模型:线性回归、逻辑回归、简单 CNN/RNN;
  • 实战项目:用 PyTorch 复现鸢尾花分类、波士顿房价预测。

7.2 进阶阶段(2~3 个月)

  • 高级层:nn.Transformer(Transformer 模型)、nn.BatchNorm(批量归一化);
  • 训练技巧:学习率调度、早停(Early Stopping)、混合精度训练;
  • 实战项目:CIFAR-10 图像分类(准确率 85%+)、LSTM 文本分类。

7.3 实战阶段(3~6 个月)

  • 预训练模型:用torchvision加载 ResNet、VGG 等预训练模型,实现迁移学习;
  • 自然语言处理:用 Hugging Face 库(与 PyTorch 兼容)微调 BERT 模型;
  • 部署:用TorchScript优化模型,导出为 ONNX 格式,部署到移动端或服务器。

7.4 高级阶段(6 个月以上)

  • 源码阅读:理解 PyTorch 自动求导、分布式训练的底层实现;
  • 自定义层:实现论文中的新型网络层;
  • 分布式训练:用torch.distributed实现多 GPU / 多机训练,处理大规模数据。

八、总结:PyTorch 入门的核心是 “动手实战”

很多人觉得深度学习难,是因为被 “神经网络”“反向传播” 等术语吓退了。但用 PyTorch 学习时,你会发现:这些复杂概念都被封装成了简单的 API,你不需要推导公式,只需调用nn.Linearloss.backward(),就能让模型自己 “学习”。

这篇博客从环境搭建到实战项目(线性回归、CNN 图像分类、LSTM 文本生成),再到避坑指南和学习路线,覆盖了 PyTorch 入门的核心内容。新手可以按 “线性回归→CNN→RNN” 的顺序学习,每个项目都动手敲代码、调参数,感受模型从 “随机预测” 到 “精准输出” 的过程 —— 这才是深度学习最有趣的地方。

记住:PyTorch 的优势在于 “灵活” 和 “易用”,不要害怕犯错,每一次调试都是一次进步。当你能用它解决实际问题(比如给图片分类、生成文本)时,你会发现深度学习其实没那么神秘!

如果觉得这篇博客有帮助,欢迎点赞、收藏、转发!有任何问题,欢迎在评论区留言,我会第一时间回复~

您可能感兴趣的与本文相关的镜像

PyTorch 2.7

PyTorch 2.7

PyTorch
Cuda

PyTorch 是一个开源的 Python 机器学习库,基于 Torch 库,底层由 C++ 实现,应用于人工智能领域,如计算机视觉和自然语言处理

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值