引言:为什么选择 PyTorch?
在深度学习框架的 "江湖" 中,PyTorch 无疑是近年来最炙手可热的存在。无论是学术界的科研人员,还是工业界的算法工程师,都对它青睐有加。这背后,离不开 PyTorch 的三大核心优势:动态计算图 、极致的易用性 和强大的生态系统。
PyTorch 由 Facebook(现 Meta)人工智能研究院(FAIR)于 2016 年推出,其前身是 Torch(基于 Lua 语言)。相较于早期的深度学习框架(如 TensorFlow 1.x 的静态计算图),PyTorch 的动态计算图特性让开发者可以 "边写边调试"------ 每一行代码的执行都会即时构建计算图,无需提前定义完整流程,这对新手友好,也让复杂模型的迭代效率大幅提升。
如今,PyTorch 已成为深度学习领域的 "全能选手":在计算机视觉(CV)领域,有 TorchVision 提供预训练模型和数据工具;在自然语言处理(NLP)领域,Hugging Face Transformers 库基于 PyTorch 构建,支持 BERT、GPT 等主流大模型;在推荐系统、强化学习等领域,PyTorch 也有丰富的工具链支撑。
如果你是深度学习初学者,或想从其他框架迁移到更灵活的工具,这篇博客将带你 "从零到一" 掌握 PyTorch------ 从环境准备(下载安装)到核心概念(Tensor、Autograd、nn 模块),再到实战案例(手写数字识别),每一步都配有详细说明和代码示例,帮你彻底入门 PyTorch。
第一部分:PyTorch 下载与安装(全平台指南)
安装 PyTorch 的核心是匹配环境依赖------ 包括操作系统(Windows/macOS/Linux)、Python 版本、CUDA 版本(是否使用 GPU 加速)。下面分场景详细讲解,确保你能一次安装成功。
1.1 安装前的环境检查
在安装 PyTorch 前,需先确认以下信息,避免版本不兼容导致报错:
(1)检查 Python 版本
PyTorch 对 Python 版本有明确要求(如 PyTorch 2.0 + 支持 Python 3.8~3.11),过低或过高的 Python 版本都会导致安装失败。
检查方法:
-
Windows/macOS/Linux 通用命令(终端 / CMD/PowerShell): bash
python --version # 或 python3 --version(部分系统)
-
输出示例:
Python 3.9.7
(若版本不符合,需先安装对应 Python,推荐用Anaconda管理环境)。
(2)检查是否有 NVIDIA 显卡(是否支持 GPU 加速)
PyTorch 分为CPU 版本 和GPU 版本:
- CPU 版本:无需显卡,适合新手入门或无独立显卡的设备(如笔记本),但训练速度较慢;
- GPU 版本:需 NVIDIA 显卡(支持 CUDA),训练速度是 CPU 的 10~100 倍,适合实际项目。
检查方法(判断是否有 NVIDIA 显卡 + CUDA 版本):
- 安装 NVIDIA 显卡驱动(已安装可跳过):
- 下载地址:NVIDIA 驱动下载页,根据显卡型号选择驱动。
- 检查 CUDA 版本:
- Windows:打开 CMD/PowerShell,输入
nvidia-smi
; - Linux/macOS:打开终端,输入
nvidia-smi
; - 输出示例中,"CUDA Version: 12.2" 即为当前支持的最高 CUDA 版本(无需严格一致,PyTorch 会兼容低版本 CUDA)。
- Windows:打开 CMD/PowerShell,输入
若输入 nvidia-smi
报错,说明无 NVIDIA 显卡,只能安装 CPU 版本。
1.2 安装方式选择:Conda vs Pip
PyTorch 支持两种主流安装方式:Conda (推荐新手)和Pip(适合熟悉 Python 环境的用户)。两者的核心区别如下:
特性 | Conda | Pip |
---|---|---|
环境管理 | 支持创建独立环境,避免版本冲突 | 依赖系统 Python 环境,易冲突 |
CUDA 自动配置 | 自动安装对应 CUDA 工具包 | 需手动确保 CUDA 环境正确 |
适用场景 | 新手、多环境开发 | 单环境、已有 Python 环境 |
命令示例(GPU 版) | conda install pytorch... |
pip3 install torch... |
推荐新手用 Conda:Anaconda(或 Miniconda)会自动管理 Python 版本和 CUDA 依赖,减少 "踩坑" 概率。
1.3 全平台安装步骤(Windows/macOS/Linux)
场景 1:Windows 系统(最常见)
方式 A:Conda 安装(推荐)
-
安装 Anaconda/Miniconda:
- 下载地址:Anaconda 官网(带 GUI,体积大)或Miniconda 官网(轻量版,仅命令行)。
- 安装时勾选 "Add Anaconda to my PATH environment variable"(方便终端调用)。
-
打开 Anaconda Prompt(Conda 自带终端):
-
输入以下命令创建并激活 PyTorch 环境(避免影响系统默认 Python): bash
# 创建名为pytorch_env的环境,指定Python 3.9(兼容大部分PyTorch版本) conda create -n pytorch_env python=3.9 # 激活环境(后续操作需在该环境下执行) conda activate pytorch_env
-
-
安装 PyTorch(根据 CUDA 版本选择命令):
-
首先通过
nvidia-smi
查看 CUDA 版本(如 12.2),然后到PyTorch 官网选择对应命令(官网会自动推荐适配命令)。 -
示例 1:CUDA 12.1(GPU 版): bash
conda install pytorch torchvision torchaudio pytorch-cuda=12.1 -c pytorch -c nvidia
-
示例 2:无 CUDA(CPU 版): bash
conda install pytorch torchvision torchaudio cpuonly -c pytorch
-
方式 B:Pip 安装
-
升级 Pip(避免旧版本 Pip 安装失败):
bash
python -m pip install --upgrade pip
-
安装 PyTorch(根据 CUDA 版本选择):
-
GPU 版(CUDA 12.1): bash
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
-
CPU 版: bash
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
-
场景 2:macOS 系统(含 M1/M2 芯片)
macOS 用户需注意芯片类型(Intel 芯片 vs Apple Silicon 芯片,如 M1/M2),PyTorch 对 Apple Silicon 的支持已成熟。
方式 A:Conda 安装
-
安装 Miniconda(推荐,Anaconda 对 M1 支持稍差):
- 下载地址:Miniconda for macOS,选择 "Miniconda3 macOS Apple M1 64-bit pkg"(M1/M2)或 "Miniconda3 macOS Intel x86 64-bit pkg"(Intel)。
-
打开终端,创建并激活环境:
bash
conda create -n pytorch_env python=3.9 conda activate pytorch_env
-
安装 PyTorch:
-
M1/M2 芯片(支持 Metal 加速,类似 GPU 加速): bash
conda install pytorch torchvision torchaudio -c pytorch
-
Intel 芯片(CPU 版,无 GPU 加速): bash
conda install pytorch torchvision torchaudio cpuonly -c pytorch
-
方式 B:Pip 安装
-
升级 Pip:
bash
python3 -m pip install --upgrade pip
-
安装 PyTorch:
-
M1/M2 芯片: bash
pip3 install torch torchvision torchaudio
-
Intel 芯片(CPU 版): bash
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
-
场景 3:Linux 系统(服务器常用)
Linux 是深度学习服务器的主流系统,安装步骤与 Windows 类似,但需注意 "无 root 权限" 场景(如实验室服务器)。
方式 A:Conda 安装(推荐,无需 root)
-
安装 Miniconda(无 root 也可安装):
bash
# 下载Miniconda(Linux x86_64) wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh # 执行安装脚本,按提示操作(安装路径选自己有权限的目录,如~/miniconda3) bash Miniconda3-latest-Linux-x86_64.sh # 激活Miniconda(或重启终端) source ~/miniconda3/bin/activate
-
创建并激活环境:
bash
conda create -n pytorch_env python=3.9 conda activate pytorch_env
-
安装 PyTorch(GPU 版,假设 CUDA 12.1):
bash
conda install pytorch torchvision torchaudio pytorch-cuda=12.1 -c pytorch -c nvidia
1.4 安装验证:确保 PyTorch 能正常使用
安装完成后,需通过简单代码验证是否成功,尤其是 GPU 版本是否能调用显卡。
验证步骤:
-
打开 Python 交互环境(终端输入
python
或python3
); -
输入以下代码: python
# 导入PyTorch import torch # 导入TorchVision(验证配套工具是否安装) import torchvision # 1. 验证PyTorch版本 print("PyTorch版本:", torch.__version__) # 2. 验证GPU是否可用(返回True则支持GPU) print("GPU是否可用:", torch.cuda.is_available()) # 3. 若GPU可用,查看GPU数量和型号 if torch.cuda.is_available(): print("GPU数量:", torch.cuda.device_count()) print("GPU型号:", torch.cuda.get_device_name(0)) # 0表示第一个GPU
成功输出示例(GPU 版):
plaintext
PyTorch版本: 2.1.0
GPU是否可用: True
GPU数量: 1
GPU型号: NVIDIA GeForce RTX 3060
常见问题与解决:
-
问题 1:
import torch
报错 "ModuleNotFoundError"?原因:未激活 Conda 环境(需执行
conda activate pytorch_env
),或 Pip 安装时用了错误的 Python 环境(检查which python
或where python
路径)。 -
问题 2:
torch.cuda.is_available()
返回 False(明明有 NVIDIA 显卡)?原因:CUDA 版本与 PyTorch 不兼容,或显卡驱动版本过低。解决方法:重新安装对应 CUDA 版本的 PyTorch,或升级 NVIDIA 驱动。
第二部分:PyTorch 核心知识点(从基础到进阶)
掌握 PyTorch 的核心,需从 "数据结构→自动求导→模型构建→数据加载→训练流程" 逐步深入。下面逐一拆解每个知识点,配合代码示例帮你理解。
2.1 基础数据结构:Tensor(张量)
Tensor 是 PyTorch 中最基础的数据结构,类比于 NumPy 的数组(ndarray),但支持 GPU 加速和自动求导。可以理解为:Tensor = 增强版 NumPy 数组 + 深度学习特性。
2.1.1 Tensor 的定义与属性
Tensor 可以是 0 维(标量)、1 维(向量)、2 维(矩阵)或更高维(如 3 维用于时序数据,4 维用于图像数据)。
(1)创建 Tensor 的常用方法
python
import torch
# 1. 从Python列表创建(指定数据类型)
t1 = torch.tensor([1, 2, 3], dtype=torch.float32)
print("t1:", t1) # 输出:t1: tensor([1., 2., 3.])
# 2. 创建全0 Tensor(指定形状)
t2 = torch.zeros((2, 3), dtype=torch.int64) # 2行3列,整数型
print("t2:\n", t2)
# 输出:
# t2:
# tensor([[0, 0, 0],
# [0, 0, 0]])
# 3. 创建全1 Tensor
t3 = torch.ones((3, 2)) # 默认float32
print("t3:\n", t3)
# 4. 创建随机Tensor(正态分布,均值0,方差1)
t4 = torch.randn((2, 2)) # 2x2随机矩阵
print("t4:\n", t4)
# 5. 创建指定范围的Tensor(类似range)
t5 = torch.arange(0, 10, step=2) # 从0到10,步长2
print("t5:", t5) # 输出:t5: tensor([0, 2, 4, 6, 8])
# 6. 从NumPy数组转换(共享内存,修改一个会影响另一个)
import numpy as np
np_arr = np.array([[1, 2], [3, 4]])
t6 = torch.from_numpy(np_arr)
print("t6:\n", t6)
# 反向转换:Tensor → NumPy
np_arr2 = t6.numpy()
print("np_arr2:\n", np_arr2)
(2)Tensor 的核心属性
每个 Tensor 都有以下关键属性,用于查看其状态:
python
t = torch.randn((2, 3), dtype=torch.float32, device="cuda" if torch.cuda.is_available() else "cpu")
print("形状(shape):", t.shape) # 输出:torch.Size([2, 3])
print("数据类型(dtype):", t.dtype) # 输出:torch.float32
print("设备(device):", t.device) # 输出:cuda:0(GPU)或cpu
print("维度(ndim):", t.ndim) # 输出:2(2维Tensor)
print("元素总数(numel):", t.numel()) # 输出:6(2*3)
- shape :Tensor 的维度大小,如
(2,3)
表示 2 行 3 列; - dtype :数据类型,常用
torch.float32
(默认)、torch.int64
(整数); - device:Tensor 所在设备(CPU/GPU),只有在同一设备的 Tensor 才能运算;
- numel :Tensor 的元素总数,相当于
np.prod(t.shape)
。
2.1.2 Tensor 的常用操作
Tensor 的操作可分为 "形状变换""算术运算""索引与切片" 三大类,与 NumPy 用法相似,但支持 GPU 加速。
(1)形状变换(Reshape/View/Transpose)
形状变换不改变元素值,只改变 Tensor 的维度结构:
python
t = torch.arange(0, 6) # 形状:(6,),1维Tensor
# 1. view():重塑形状(需保证元素总数不变)
t1 = t.view(2, 3) # 重塑为2x3
print("t1.shape:", t1.shape) # 输出:torch.Size([2, 3])
# 2. reshape():与view类似,更灵活(推荐)
t2 = t.reshape(3, 2) # 重塑为3x2
print("t2.shape:", t2.shape) # 输出:torch.Size([3, 2])
# 3. unsqueeze():增加维度(常用在添加 batch 维度)
t3 = t1.unsqueeze(0) # 在第0维增加维度
print("t3.shape:", t3.shape) # 输出:torch.Size([1, 2, 3])(3维Tensor)
# 4. squeeze():删除维度为1的维度
t4 = t3.squeeze(0) # 删除第0维(维度为1)
print("t4.shape:", t4.shape) # 输出:torch.Size([2, 3])
# 5. transpose():转置(交换两个维度)
t5 = t1.transpose(0, 1) # 交换0维和1维(2x3 → 3x2)
print("t5.shape:", t5.shape) # 输出:torch.Size([3, 2])
# 6. permute():任意交换维度(适合高维Tensor,如图像)
# 示例:图像Tensor形状为 (batch, channel, height, width) → 转置为 (batch, height, width, channel)
img = torch.randn(16, 3, 224, 224) # 16张图,3通道,224x224
img_permute = img.permute(0, 2, 3, 1)
print("img_permute.shape:", img_permute.shape) # 输出:torch.Size([16, 224, 224, 3])
(2)算术运算(加减乘除 / 矩阵乘法)
Tensor 的算术运算支持 "元素级运算" 和 "矩阵运算",需注意区分:
python
a = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32)
b = torch.tensor([[5, 6], [7, 8]], dtype=torch.float32)
# 1. 元素级运算(对应位置元素计算)
print("a + b:", a + b) # 加法(也可写 torch.add(a, b))
print("a - b:", a - b) # 减法
print("a * b:", a * b) # 乘法(元素级,非矩阵乘法)
print("a / b:", a / b) # 除法
# 2. 矩阵乘法(用 @ 或 torch.matmul())
print("a @ b:\n", a @ b) # 矩阵乘法,结果为 2x2
# 输出:
# tensor([[19., 22.],
# [43., 50.]])
# 3. 广播机制(类似NumPy,自动扩展维度匹配)
c = torch.tensor([10, 20], dtype=torch.float32) # 1维Tensor (2,)
print("a + c:\n", a + c) # c自动扩展为 (2,2),每一行都是[10,20]
# 输出:
# tensor([[11., 22.],
# [13., 24.]])
(3)索引与切片(提取部分元素)
Tensor 的索引切片规则与 Python 列表、NumPy 一致,支持整数索引、切片、布尔索引:
python
t = torch.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 1. 整数索引(提取单个元素)
print("t[0,1]:", t[0, 1]) # 第0行第1列,输出:tensor(2)
# 2. 切片(提取范围元素,左闭右开)
print("t[0:2, 1:3]:\n", t[0:2, 1:3]) # 第0-1行,第1-2列
# 输出:
# tensor([[2, 3],
# [5, 6]])
# 3. 布尔索引(按条件提取)
mask = t > 5 # 生成布尔Tensor
print("mask:\n", mask)
print("t[mask]:", t[mask]) # 提取大于5的元素,输出:tensor([6, 7, 8, 9])
# 4. 高级索引(用列表指定行/列)
print("t[[0,2], :]:\n", t[[0, 2], :]) # 提取第0行和第2行
2.2 深度学习核心:Autograd(自动求导)
深度学习的本质是 "通过反向传播优化参数",而反向传播的核心是计算梯度(参数对损失函数的偏导数)。PyTorch 的 Autograd 模块会自动帮我们计算梯度,无需手动推导公式 ------ 这也是框架的核心价值之一。
2.2.1 计算图与 Autograd 原理
Autograd 的核心是动态计算图:
- 每一次 Tensor 的运算都会生成一个 "计算节点",这些节点构成一张有向无环图(DAG);
- 图的起点是 "输入 Tensor",终点是 "输出 Tensor"(如损失函数值);
- 反向传播时,Autograd 从终点出发,沿着图的反向遍历,用链式法则计算每个参数的梯度。
关键概念:
requires_grad=True
:标记 Tensor 需要计算梯度(默认 False);grad
:存储 Tensor 的梯度值(反向传播后才会有值);grad_fn
:记录 Tensor 的运算方式(用于反向传播时找到梯度来源)。
2.2.2 自动求导的基本用法
下面通过一个简单的函数(y = x^2 + 2x + 1
)演示 Autograd 的使用:
python
import torch
# 1. 创建需要求导的Tensor(设置requires_grad=True)
x = torch.tensor(2.0, requires_grad=True) # x是标量,值为2.0
# 2. 定义计算过程(构建计算图)
y = x ** 2 + 2 * x + 1 # y = 2² + 2*2 +1 = 9.0
# 3. 反向传播(计算梯度 dy/dx)
y.backward() # 只有标量才能直接调用backward()
# 4. 查看梯度(x的梯度存储在x.grad中)
print("dy/dx at x=2.0:", x.grad) # 输出:tensor(6.)
# 理论验证:dy/dx = 2x + 2,x=2时梯度为6,与Autograd计算结果一致
高维 Tensor 的求导
若输出是高维 Tensor(如向量 / 矩阵),需在backward()
中指定grad_tensors
(梯度权重),确保反向传播的维度匹配:
python
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True) # 1维Tensor
y = x ** 2 # y = [1,4,9](向量)
# 计算y对x的梯度,需指定grad_tensors(如全1向量,表示对y的每个元素求和后求导)
y.backward(torch.tensor([1.0, 1.0, 1.0]))
print("dy/dx:", x.grad) # 输出:tensor([2., 4., 6.])(dy/dx=2x)
2.2.3 梯度清零与阻止求导
在模型训练中,梯度会累加 (即每次backward()
后,梯度会叠加到grad
中),因此需要手动清零;此外,有时需要临时阻止某些操作的求导(如验证阶段)。
(1)梯度清零
python
x = torch.tensor(2.0, requires_grad=True)
# 第一次求导
y1 = x ** 2
y1.backward()
print("第一次梯度:", x.grad) # 输出:tensor(4.)
# 第二次求导(未清零,梯度累加)
y2 = x ** 3
y2.backward()
print("第二次梯度(累加后):", x.grad) # 输出:tensor(16.)(4 + 12,12是y2对x的梯度3x²=12)
# 梯度清零(用zero_()方法,下划线表示原地操作)
x.grad.zero_()
# 第三次求导(已清零,梯度重新计算)
y3 = x ** 3
y3.backward()
print("第三次梯度(清零后):", x.grad) # 输出:tensor(12.)
(2)阻止求导
有两种常用方式阻止 Autograd 计算梯度:
torch.no_grad()
:上下文管理器,内部操作不记录计算图;detach()
:返回一个与原 Tensor 共享数据但requires_grad=False
的新 Tensor。
python
x = torch.tensor(2.0, requires_grad=True)
# 方式1:torch.no_grad()
with torch.no_grad():
y = x ** 2
print("y.requires_grad:", y.requires_grad) # 输出:False(未记录求导)
# 方式2:detach()
z = x.detach() ** 2
print("z.requires_grad:", z.requires_grad) # 输出:False
2.3 模型构建核心:nn 模块
PyTorch 的torch.nn
模块是构建神经网络的 "工具箱"------ 提供了常用的层(如卷积层、线性层)、损失函数、激活函数等,无需手动实现复杂的前向传播逻辑。
nn
模块的核心是nn.Module
类:所有自定义模型都需继承该类,并在__init__
中定义层,在forward
中定义前向传播流程(反向传播由 Autograd 自动完成)。
2.3.1 自定义模型的基本框架
下面以一个简单的 "全连接神经网络"(用于 MNIST 手写数字识别)为例,演示自定义模型的写法:
python
import torch
import torch.nn as nn
import torch.nn.functional as F # 提供激活函数、池化等函数
class SimpleFCNet(nn.Module):
def __init__(self, input_dim=784, hidden_dim=256, output_dim=10):
"""
初始化函数:定义模型的层
input_dim: 输入维度(MNIST图像展平后为28*28=784)
hidden_dim: 隐藏层维度
output_dim: 输出维度(10个类别:0-9)
"""
super(SimpleFCNet, self).__init__() # 调用父类构造函数
# 1. 定义全连接层(线性层)
self.fc1 = nn.Linear(input_dim, hidden_dim) # 输入层→隐藏层
self.fc2 = nn.Linear(hidden_dim, hidden_dim) # 隐藏层→隐藏层
self.fc3 = nn.Linear(hidden_dim, output_dim) # 隐藏层→输出层
# 2. 定义 dropout 层(防止过拟合,可选)
self.dropout = nn.Dropout(p=0.5) # 随机丢弃50%的神经元
def forward(self, x):
"""
前向传播:定义数据如何通过层
x: 输入Tensor,形状为 (batch_size, input_dim)
"""
# 1. 输入层→隐藏层:线性变换 + ReLU激活 + Dropout
x = self.fc1(x)
x = F.relu(x) # 激活函数(引入非线性)
x = self.dropout(x) # 防止过拟合
# 2. 隐藏层→隐藏层
x = self.fc2(x)
x = F.relu(x)
x = self.dropout(x)
# 3. 隐藏层→输出层(无需激活,后续用交叉熵损失函数)
x = self.fc3(x)
return x
# 实例化模型
model = SimpleFCNet(input_dim=784, hidden_dim=256, output_dim=10)
# 查看模型结构
print(model)
模型结构输出:
plaintext
SimpleFCNet(
(fc1): Linear(in_features=784, out_features=256, bias=True)
(fc2): Linear(in_features=256, out_features=256, bias=True)
(fc3): Linear(in_features=256, out_features=10, bias=True)
(dropout): Dropout(p=0.5, inplace=False)
)
2.3.2 常用的 nn 层详解
nn
模块提供了数十种层,覆盖 CV、NLP 等多个领域,下面介绍最常用的几类:
(1)线性层(nn.Linear)
线性层即全连接层,实现公式:y = x * W^T + b
,其中W
是权重,b
是偏置。
参数说明:
in_features
:输入特征数(如 784);out_features
:输出特征数(如 256);bias
:是否添加偏置(默认 True)。
用法示例:
python
fc = nn.Linear(in_features=784, out_features=256)
x = torch.randn(16, 784) # 16个样本,每个样本784维
output = fc(x)
print("output.shape:", output.shape) # 输出:torch.Size([16, 256])
(2)卷积层(nn.Conv2d,CV 常用)
卷积层用于提取图像的局部特征(如边缘、纹理),是 CNN 的核心层。
参数说明(以 2D 卷积为例):
in_channels
:输入通道数(如 RGB 图像为 3,灰度图像为 1);out_channels
:输出通道数(卷积核数量,决定提取的特征数);kernel_size
:卷积核大小(如 3 表示 3x3 卷积核);stride
:步长(卷积核移动的步幅,默认 1);padding
:填充(在图像边缘补 0,默认 0,用于保持输入输出尺寸一致)。
用法示例(处理 MNIST 灰度图像):
python
# 定义卷积层:1输入通道(灰度),32输出通道(32个卷积核),3x3卷积核,步长1, padding=1
conv = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3, stride=1, padding=1)
# 输入图像:16个样本,1通道,28x28大小
x = torch.randn(16, 1, 28, 28)
output = conv(x)
print("output.shape:", output.shape) # 输出:torch.Size([16, 32, 28, 28])
# 尺寸计算:(28 + 2*1 - 3)/1 + 1 = 28,与输入尺寸一致(因padding=1)
(3)池化层(nn.MaxPool2d,CV 常用)
池化层用于 "降维"------ 减少特征图的尺寸,降低计算量,同时增强鲁棒性(对微小位移不敏感)。常用的是最大池化(取局部最大值)。
参数说明:
kernel_size
:池化核大小(如 2 表示 2x2 池化);stride
:步长(默认与 kernel_size 相同,避免重叠);padding
:填充(默认 0)。
用法示例:
python
# 定义2x2最大池化层
pool = nn.MaxPool2d(kernel_size=2, stride=2)
# 输入:16个样本,32通道,28x28特征图
x = torch.randn(16, 32, 28, 28)
output = pool(x)
print("output.shape:", output.shape) # 输出:torch.Size([16, 32, 14, 14])
# 尺寸计算:(28 - 2)/2 + 1 =14,尺寸缩小为原来的1/2
(4)循环层(nn.LSTM,NLP 常用)
循环层用于处理时序数据(如文本、语音),LSTM(长短期记忆网络)是最常用的循环层,能解决 RNN 的 "长期依赖" 问题。
参数说明:
input_size
:输入特征数(如每个词的嵌入维度为 128);hidden_size
:隐藏层维度(LSTM 输出的特征数);num_layers
:LSTM 层数(默认 1,多层可提取更复杂特征);batch_first
:是否将batch_size
作为第一个维度(默认 False,推荐设为 True,符合 PyTorch 习惯)。
用法示例:
python
# 定义LSTM层:输入维度128,隐藏层维度256,2层,batch_first=True
lstm = nn.LSTM(input_size=128, hidden_size=256, num_layers=2, batch_first=True)
# 输入:16个样本,每个样本10个时间步,每个时间步128维特征(如10个词,每个词嵌入128维)
x = torch.randn(16, 10, 128)
# LSTM输出:output(所有时间步的隐藏状态),(h_n, c_n)(最后一个时间步的隐藏状态和细胞状态)
output, (h_n, c_n) = lstm(x)
print("output.shape:", output.shape) # 输出:torch.Size([16, 10, 256])
print("h_n.shape:", h_n.shape) # 输出:torch.Size([2, 16, 256])(2层,16样本,256维)
2.3.3 损失函数与优化器
模型训练需要两个核心组件:损失函数 (衡量预测值与真实值的差距)和优化器(根据梯度更新参数)。
(1)常用损失函数(nn 模块)
根据任务类型选择损失函数:
- 分类任务 :
nn.CrossEntropyLoss()
(交叉熵损失,自动包含 Softmax 激活); - 回归任务 :
nn.MSELoss()
(均方误差损失); - 二分类任务 :
nn.BCELoss()
(二元交叉熵损失,需配合 Sigmoid 激活)。
用法示例(分类任务):
python
# 1. 定义损失函数(交叉熵损失)
criterion = nn.CrossEntropyLoss()
# 2. 模拟模型输出和真实标签
model_output = torch.randn(16, 10) # 16个样本,10个类别(未经过Softmax)
true_labels = torch.randint(0, 10, (16,)) # 16个真实标签(0-9)
# 3. 计算损失
loss = criterion(model_output, true_labels)
print("损失值:", loss.item()) # 输出:如2.3(值越小,预测越准)
(2)常用优化器(torch.optim 模块)
优化器用于更新模型参数(model.parameters()
),常用的有:
optim.SGD()
:随机梯度下降(基础优化器,需配合动量momentum
);optim.Adam()
:自适应动量估计(收敛快,推荐新手使用);optim.RMSprop()
:自适应学习率(适合非平稳目标)。
用法示例:
python
# 1. 实例化模型和损失函数
model = SimpleFCNet()
criterion = nn.CrossEntropyLoss()
# 2. 定义优化器(Adam,学习率lr=0.001)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# 3. 模拟训练中的参数更新流程
for epoch in range(5): # 训练5轮
# 前向传播
model_output = model(torch.randn(16, 784))
loss = criterion(model_output, torch.randint(0, 10, (16,)))
# 反向传播与参数更新
optimizer.zero_grad() # 梯度清零(避免累加)
loss.backward() # 计算梯度
optimizer.step() # 更新参数(w = w - lr * grad)
print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}")
2.4 数据加载:Dataset 与 DataLoader
在实际项目中,数据通常存储在文件夹或 CSV 文件中,直接用 Python 读取效率低且难以批量处理。PyTorch 的torch.utils.data
模块提供了Dataset
和DataLoader
,用于高效加载和预处理数据。
- Dataset:定义数据的 "来源" 和 "预处理逻辑"(如读取图片、转换标签);
- DataLoader :封装
Dataset
,实现 "批量加载""数据打乱""多线程读取",大幅提升训练效率。
2.4.1 自定义 Dataset
自定义Dataset
需继承torch.utils.data.Dataset
,并实现两个核心方法:
__len__()
:返回数据集的总样本数;__getitem__(idx)
:根据索引idx
返回单个样本((data, label)
)。
下面以 "自定义图像数据集" 为例(假设图像存储在./data/images
,标签存储在./data/labels.csv
):
python
import os
import pandas as pd
from PIL import Image
from torch.utils.data import Dataset
from torchvision import transforms
class CustomImageDataset(Dataset):
def __init__(self, img_dir, label_path, transform=None):
"""
初始化函数
img_dir: 图像文件夹路径
label_path: 标签CSV文件路径(格式:image_name, label)
transform: 图像预处理变换(如Resize、ToTensor)
"""
self.img_dir = img_dir
# 读取标签文件(假设CSV有两列:image_name和label)
self.labels = pd.read_csv(label_path)
self.transform = transform
def __len__(self):
# 返回数据集总样本数
return len(self.labels)
def __getitem__(self, idx):
# 根据索引idx获取单个样本
# 1. 读取图像
img_name = self.labels.iloc[idx, 0] # 第idx行的image_name列
img_path = os.path.join(self.img_dir, img_name)
image = Image.open(img_path).convert("RGB") # 读取为RGB图像
# 2. 读取标签
label = self.labels.iloc[idx, 1] # 第idx行的label列
label = torch.tensor(label, dtype=torch.long) # 转换为Tensor
# 3. 图像预处理(若有transform)
if self.transform:
image = self.transform(image)
return image, label # 返回 (数据, 标签)
2.4.2 用 DataLoader 批量加载数据
DataLoader
将Dataset
封装为可迭代的 "数据生成器",支持以下核心功能:
batch_size
:每次加载的样本数(如 64、128);shuffle
:是否打乱数据(训练集设为 True,测试集设为 False);num_workers
:多线程读取数据(加快加载速度,Windows 下建议设为 0);drop_last
:是否丢弃最后一个不足batch_size
的样本(避免维度不匹配)。
用法示例:
python
from torch.utils.data import DataLoader
from torchvision import transforms
# 1. 定义图像预处理变换(组合多个变换)
transform = transforms.Compose([
transforms.Resize((224, 224)), # 缩放图像到224x224
transforms.RandomHorizontalFlip(p=0.5), # 随机水平翻转(数据增强)
transforms.ToTensor(), # 转换为Tensor(HWC→CHW,值归一化到[0,1])
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # 标准化(ImageNet均值和标准差)
])
# 2. 实例化自定义Dataset
train_dataset = CustomImageDataset(
img_dir="./data/images/train",
label_path="./data/labels/train_labels.csv",
transform=transform
)
# 3. 实例化DataLoader
train_loader = DataLoader(
train_dataset,
batch_size=16, # 每次加载16个样本
shuffle=True, # 训练集打乱
num_workers=4, # 4个线程读取数据(Linux/macOS),Windows设为0
drop_last=False
)
# 4. 迭代DataLoader获取批量数据(训练循环中常用)
for batch_idx, (data, labels) in enumerate(train_loader):
print(f"Batch {batch_idx+1}:")
print(f" 数据形状: {data.shape}") # 输出:torch.Size([16, 3, 224, 224])(16样本,3通道,224x224)
print(f" 标签形状: {labels.shape}") # 输出:torch.Size([16])
# 这里可添加模型前向传播、损失计算等逻辑
if batch_idx == 2: # 只打印前3个batch
break
2.4.3 内置 Dataset(TorchVision)
TorchVision 提供了多个经典数据集的内置Dataset
(如 MNIST、CIFAR-10、ImageNet),无需手动编写读取逻辑,直接调用即可。
示例:加载 MNIST 数据集(手写数字识别):
python
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
# 定义预处理变换(MNIST是灰度图像,通道数为1)
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,)) # MNIST的均值和标准差(预计算值)
])
# 加载训练集(download=True表示自动下载到指定路径)
train_dataset = datasets.MNIST(
root="./data", # 数据存储路径
train=True, # 训练集
download=True, # 自动下载
transform=transform
)
# 加载测试集
test_dataset = datasets.MNIST(
root="./data",
train=False, # 测试集
download=True,
transform=transform
)
# 实例化DataLoader
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=1000, shuffle=False)
# 查看数据
for data, labels in train_loader:
print("数据形状:", data.shape) # 输出:torch.Size([64, 1, 28, 28])
print("标签形状:", labels.shape) # 输出:torch.Size([64])
break
2.5 模型训练与评估流程
掌握了 Tensor、Autograd、nn 模块和 DataLoader 后,即可串联起完整的模型训练与评估流程。下面以 "MNIST 手写数字识别" 为例,演示端到端的训练过程。
2.5.1 完整训练流程代码
python
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
# -------------------------- 1. 配置超参数 --------------------------
batch_size = 64 # 批量大小
test_batch_size = 1000 # 测试集批量大小
epochs = 10 # 训练轮数
lr = 0.001 # 学习率
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 设备(GPU/CPU)
seed = 1 # 随机种子(保证实验可复现)
torch.manual_seed(seed) # 设置PyTorch随机种子
# -------------------------- 2. 加载数据 --------------------------
# 数据预处理
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,)) # MNIST均值和标准差
])
# 加载训练集和测试集
train_dataset = datasets.MNIST(
root="./data", train=True, download=True, transform=transform
)
test_dataset = datasets.MNIST(
root="./data", train=False, download=True, transform=transform
)
# 实例化DataLoader
train_loader = DataLoader(
train_dataset, batch_size=batch_size, shuffle=True
)
test_loader = DataLoader(
test_dataset, batch_size=test_batch_size, shuffle=False
)
# -------------------------- 3. 定义模型 --------------------------
class MNISTNet(nn.Module):
def __init__(self):
super(MNISTNet, self).__init__()
# 卷积层:1→32→64
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
# 池化层:2x2最大池化
self.pool = nn.MaxPool2d(2)
# 全连接层:64*12*12→128→10(12=(28-3+1-2)/2 +1,卷积+池化后的尺寸)
self.fc1 = nn.Linear(64 * 12 * 12, 128)
self.fc2 = nn.Linear(128, 10)
# Dropout层(防止过拟合)
self.dropout = nn.Dropout(0.5)
def forward(self, x):
# 卷积1 + ReLU + 池化
x = self.conv1(x)
x = nn.functional.relu(x)
x = self.pool(x)
# 卷积2 + ReLU + 池化
x = self.conv2(x)
x = nn.functional.relu(x)
x = self.pool(x)
# 展平(适配全连接层)
x = x.view(-1, 64 * 12 * 12) # -1表示自动计算batch_size
# 全连接1 + ReLU + Dropout
x = self.fc1(x)
x = nn.functional.relu(x)
x = self.dropout(x)
# 全连接2(输出)
x = self.fc2(x)
return x
# 实例化模型并移动到设备(GPU/CPU)
model = MNISTNet().to(device)
# -------------------------- 4. 定义损失函数和优化器 --------------------------
criterion = nn.CrossEntropyLoss() # 交叉熵损失(分类任务)
optimizer = optim.Adam(model.parameters(), lr=lr) # Adam优化器
# -------------------------- 5. 训练函数 --------------------------
def train(model, device, train_loader, optimizer, epoch):
model.train() # 切换到训练模式(启用Dropout、BatchNorm更新)
for batch_idx, (data, target) in enumerate(train_loader):
# 数据移动到设备
data, target = data.to(device), target.to(device)
# 前向传播
output = model(data)
loss = criterion(output, target)
# 反向传播与参数更新
optimizer.zero_grad() # 梯度清零
loss.backward() # 计算梯度
optimizer.step() # 更新参数
# 打印训练日志(每100个batch打印一次)
if batch_idx % 100 == 0:
print(f"Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)} ({100. * batch_idx / len(train_loader):.0f}%)]\tLoss: {loss.item():.6f}")
# -------------------------- 6. 测试函数 --------------------------
def test(model, device, test_loader):
model.eval() # 切换到评估模式(禁用Dropout、固定BatchNorm)
test_loss = 0
correct = 0 # 正确预测的样本数
# 禁用梯度计算(节省内存,加速评估)
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
# 累加测试损失(reduction='sum'表示求和,而非平均)
test_loss += criterion(output, target, reduction='sum').item()
# 预测类别(取输出最大值的索引)
pred = output.argmax(dim=1, keepdim=True)
# 累加正确预测数(比较pred和target的形状并求和)
correct += pred.eq(target.view_as(pred)).sum().item()
# 计算平均测试损失和准确率
test_loss /= len(test_loader.dataset)
accuracy = 100. * correct / len(test_loader.dataset)
# 打印测试日志
print(f"\nTest set: Average loss: {test_loss:.4f}, Accuracy: {correct}/{len(test_loader.dataset)} ({accuracy:.2f}%)\n")
# -------------------------- 7. 开始训练与评估 --------------------------
for epoch in range(1, epochs + 1):
train(model, device, train_loader, optimizer, epoch) # 训练一轮
test(model, device, test_loader) # 评估一轮
# -------------------------- 8. 保存模型 --------------------------
torch.save(model.state_dict(), "mnist_cnn.pt") # 保存模型参数(推荐)
# 加载模型:model.load_state_dict(torch.load("mnist_cnn.pt"))
2.5.2 关键步骤解释
-
训练模式与评估模式:
model.train()
:启用 Dropout 层(随机丢弃神经元)、更新 BatchNorm 的均值和方差(用于训练);model.eval()
:禁用 Dropout 层、固定 BatchNorm 的参数(用于评估,避免影响结果)。
-
梯度禁用:
- 评估阶段用
with torch.no_grad()
包裹,禁用梯度计算,减少内存占用并加速计算(评估无需反向传播)。
- 评估阶段用
-
模型保存与加载:
- 推荐保存模型参数(
model.state_dict()
),而非整个模型(model
),因为参数文件更小,且加载时更灵活(可适配不同设备)。 - 加载参数:
model.load_state_dict(torch.load("mnist_cnn.pt"))
。
- 推荐保存模型参数(
-
准确率计算:
output.argmax(dim=1)
:取每个样本输出的最大值索引(即预测类别);pred.eq(target.view_as(pred))
:比较预测值与真实值,返回布尔 Tensor;sum().item()
:将布尔 Tensor 求和,得到正确预测的样本数。
第三部分:PyTorch 生态与进阶方向
PyTorch 的强大不仅在于核心框架,更在于其丰富的生态系统。掌握这些工具,能让你在不同领域(CV、NLP、强化学习)快速落地项目。
3.1 计算机视觉(CV)生态:TorchVision
TorchVision 是 PyTorch 官方的 CV 工具库,提供三大核心功能:
- 内置数据集:MNIST、CIFAR-10/100、ImageNet、COCO 等;
- 预训练模型:ResNet、VGG、AlexNet、YOLO、Faster R-CNN 等,可直接用于迁移学习;
- 数据变换与工具 :
transforms
(图像预处理)、utils
(图像保存、可视化)。
示例:用预训练的 ResNet18 做图像分类:
python
import torch
from torchvision import models, transforms
from PIL import Image
# 1. 加载预训练模型(pretrained=True表示加载预训练权重)
model = models.resnet18(pretrained=True).eval()
model = model.to(device)
# 2. 图像预处理(需与预训练模型的输入要求一致)
transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
# 3. 读取并预处理图像
image = Image.open("cat.jpg").convert("RGB")
image_tensor = transform(image).unsqueeze(0).to(device) # 增加batch维度
# 4. 预测
with torch.no_grad():
output = model(image_tensor)
pred = torch.argmax(output, dim=1).item()
# 5. 加载ImageNet类别标签(共1000类)
with open("imagenet_classes.txt") as f:
classes = [line.strip() for line in f]
print(f"预测类别:{classes[pred]}") # 输出:如"tabby cat"(虎斑猫)
3.2 自然语言处理(NLP)生态:Hugging Face Transformers
Hugging Face Transformers 是目前最流行的 NLP 工具库,基于 PyTorch/TensorFlow 构建,支持几乎所有主流预训练模型(BERT、GPT、T5、RoBERTa 等)。