PyTorch深度学习框架入门
掌握PyTorch深度学习框架
一、PyTorch概述
PyTorch是由Facebook(现Meta)人工智能研究团队开发的开源深度学习框架,自2017年发布以来迅速成为学术界和工业界最受欢迎的深度学习工具之一。其设计哲学以命令式编程和动态计算图为核心,使开发者能够以直观的方式构建和调试神经网络模型。
PyTorch vs TensorFlow
| 对比维度 |
PyTorch |
TensorFlow |
| 计算图 |
动态计算图(Define-by-Run) |
静态计算图(Define-and-Run),2.0后支持Eager模式 |
| 调试体验 |
原生Python调试器,调试直观 |
需借助tfdbg,调试较复杂 |
| 学习曲线 |
平缓,接近NumPy风格 |
较陡峭,API变更频繁 |
| 生产部署 |
TorchScript / TorchServe |
TF Serving / TF Lite,生态更成熟 |
| 社区生态 |
学术界首选,HuggingFace基于PyTorch |
工业部署广泛,移动端支持好 |
PyTorch设计哲学
- 命令式编程: 代码逐行执行,即时返回结果,类似于NumPy的操作方式
- 动态计算图: 计算图在每次前向传播时动态构建,允许在运行时修改网络结构
- Python优先: 深度融入Python生态,支持原生Python控制流(if、for、while)
- 张量为核心: 所有计算建立在张量(Tensor)之上,提供GPU加速支持
PyTorch生态系统
- torchvision: 计算机视觉工具包,包含数据集、模型架构和图像变换工具
- torchtext: 自然语言处理工具包,支持文本数据处理和预训练词向量
- torchaudio: 音频处理工具包,提供音频I/O、特征提取和数据集
- HuggingFace Transformers: 基于PyTorch的预训练模型库,支持BERT、GPT等主流模型
- PyTorch Lightning: 高层封装,简化训练循环编写,提升代码可复用性
二、张量基础(Tensor)
张量(Tensor)是PyTorch的核心数据结构,可以理解为多维数组。它与NumPy的ndarray类似,但额外支持GPU加速和自动求导功能。
张量的创建
import torch
# 从Python列表创建
a = torch.tensor([[1, 2], [3, 4]])
print(a) # tensor([[1, 2], [3, 4]])
# 创建特殊张量
zeros = torch.zeros(3, 4) # 全零张量,shape (3, 4)
ones = torch.ones(2, 3) # 全一张量,shape (2, 3)
randn = torch.randn(3, 3) # 标准正态分布随机张量
eye = torch.eye(5) # 单位矩阵,shape (5, 5)
arange = torch.arange(0, 10, 2) # [0, 2, 4, 6, 8]
linspace = torch.linspace(0, 1, 5) # [0.0, 0.25, 0.5, 0.75, 1.0]
实用提示
torch.tensor() 会复制数据并创建新的张量;而 torch.as_tensor() 和 torch.from_numpy() 会尽量共享内存,效率更高。
张量的属性
x = torch.randn(2, 3, 4)
print(x.shape) # torch.Size([2, 3, 4]) - 形状
print(x.dtype) # torch.float32 - 数据类型
print(x.device) # device(type='cpu') - 所在设备
# 数据类型转换
x_float = x.float()
x_int = x.long()
x_half = x.half() # 半精度浮点,GPU训练常用
常用数据类型
- torch.float32 / torch.float:默认浮点类型
- torch.float16 / torch.half:半精度,GPU训练加速
- torch.int64 / torch.long:标签索引常用
- torch.bool:布尔类型
张量运算
a = torch.tensor([[1, 2], [3, 4]])
b = torch.tensor([[5, 6], [7, 8]])
# 基本算术运算(逐元素)
c = a + b # 加法
d = a - b # 减法
e = a * b # 逐元素乘法
f = a / b # 除法
# 矩阵乘法
g = torch.mm(a, b) # 二维矩阵相乘
h = a @ b # Python运算符语法糖
# 形状操作
flat = a.view(-1) # 展平为 [1, 2, 3, 4]
reshaped = a.reshape(1, 4) # 重塑形状
t = a.t() # 转置
transposed = a.transpose(0, 1) # 指定维度转置
与NumPy的互操作
import numpy as np
# NumPy -> PyTorch张量(共享内存)
np_array = np.array([[1, 2], [3, 4]])
tensor = torch.from_numpy(np_array)
# PyTorch张量 -> NumPy(共享内存)
back_to_np = tensor.numpy()
# 修改共享内存的张量会影响另一个!
np_array[0, 0] = 100
print(tensor[0, 0]) # tensor(100) - 同步变化!
注意事项
torch.from_numpy() 和 tensor.numpy() 返回的张量与原始数组共享内存。如需独立副本,应使用 torch.tensor(np_array) 或 tensor.clone().numpy()。CPU张量才能调用 .numpy(),GPU张量需先 .cpu()。
GPU张量
# 检查GPU是否可用
if torch.cuda.is_available():
device = torch.device('cuda')
else:
device = torch.device('cpu')
# 将张量移动到GPU
x = torch.randn(3, 3)
x_gpu = x.to(device)
# 或直接创建在GPU上
x_gpu2 = torch.randn(3, 3, device='cuda')
# 方法二:使用.cuda()
x_cuda = x.cuda() if torch.cuda.is_available() else x
# 从GPU移回CPU
x_cpu = x_gpu.cpu()
GPU加速要点
- .to() 方法统一处理CPU/GPU切换,推荐使用
- 模型和数据需在同一设备上,否则运行时错误
- 小规模张量在GPU上的计算开销可能大于收益
- 使用 pin_memory=True 的DataLoader可加速数据传输到GPU
三、自动求导(Autograd)
autograd是PyTorch的自动微分引擎,它自动计算张量操作的梯度,是训练神经网络的基础。框架会记录所有张量操作构建计算图,然后通过反向传播自动计算梯度。
基础用法
# 创建需要梯度的张量
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
y = torch.tensor([4.0, 5.0, 6.0], requires_grad=True)
# 执行计算(自动构建计算图)
z = x * y + 2
loss = z.sum()
# 反向传播,计算梯度
loss.backward()
# 查看梯度
print(x.grad) # tensor([4., 5., 6.])
print(y.grad) # tensor([1., 2., 3.])
requires_grad与计算图
当张量的 requires_grad=True 时,PyTorch会跟踪该张量上的所有操作,构建一个有向无环图(DAG)。图的叶子节点是输入张量,根节点是输出张量。调用 .backward() 时,链式法则从根向叶子传播梯度。
重要操作
# .detach() - 从计算图分离
x = torch.randn(3, requires_grad=True)
y = x * 2
z = y.detach() # z不再跟踪梯度
w = z + 1 # 这里w不会计算x的梯度
# with torch.no_grad(): - 上下文管理
x = torch.randn(3, requires_grad=True)
with torch.no_grad():
y = x * 2 # 此操作不会跟踪梯度
# 常用于评估阶段,节省内存和计算
# optimizer.zero_grad() - 梯度清零
optimizer.zero_grad() # 每次反向传播前必须清空梯度,否则梯度会累加
梯度累加技巧
某些场景(如GPU内存不足)下,可以不清零梯度,通过多次前向传播累加梯度,等效于增大batch size。调用 optimizer.step() 时使用累加的梯度更新参数。
Autograd使用要点
- 默认情况下,梯度会累加(非覆盖),每次 backward 前需 zero_grad()
- 只有浮点类型的张量支持 requires_grad
- .grad 属性在非叶子节点上默认不保存,除非使用 .retain_grad()
- 评估/推理时务必使用 with torch.no_grad():,既省显存又加速
- 调用 .backward() 后,计算图被释放(默认 retain_graph=False)
四、神经网络模块(nn.Module)
torch.nn 模块提供了构建神经网络所需的层、激活函数和损失函数。所有网络结构的基类是 nn.Module,自定义网络需要继承该类。
定义网络结构
import torch.nn as nn
import torch.nn.functional as F
class SimpleNet(nn.Module):
def __init__(self, input_size, hidden_size, num_classes):
super(SimpleNet, self).__init__()
# 定义网络层
self.fc1 = nn.Linear(input_size, hidden_size)
self.fc2 = nn.Linear(hidden_size, hidden_size)
self.fc3 = nn.Linear(hidden_size, num_classes)
self.dropout = nn.Dropout(0.5)
def forward(self, x):
# 前向传播:定义数据如何流过网络
x = F.relu(self.fc1(x))
x = self.dropout(x)
x = F.relu(self.fc2(x))
x = self.dropout(x)
x = self.fc3(x)
return x
# 实例化
model = SimpleNet(784, 256, 10)
print(model)
常用层类型
| 层名称 |
用途 |
关键参数 |
| nn.Linear |
全连接层(线性变换) |
in_features, out_features, bias |
| nn.Conv2d |
二维卷积层(图像) |
in_channels, out_channels, kernel_size, stride, padding |
| nn.LSTM |
长短期记忆网络(序列) |
input_size, hidden_size, num_layers, batch_first |
| nn.BatchNorm1d/2d |
批归一化(加速训练) |
num_features, eps, momentum |
| nn.Dropout |
随机失活(防过拟合) |
p(失活概率) |
| nn.Embedding |
词嵌入层(NLP) |
num_embeddings, embedding_dim |
激活函数
# 常用激活函数
relu = nn.ReLU(inplace=True) # ReLU: f(x) = max(0, x)
sigmoid = nn.Sigmoid() # Sigmoid: f(x) = 1/(1+e^{-x})
tanh = nn.Tanh() # Tanh: f(x) = (e^x-e^{-x})/(e^x+e^{-x})
softmax = nn.Softmax(dim=1) # Softmax: 多分类概率输出
# 函数式API(不需要实例化)
x = F.relu(x)
x = F.softmax(x, dim=1)
损失函数
# 回归任务
criterion_mse = nn.MSELoss()
# loss = criterion_mse(pred, target)
# 多分类任务
criterion_ce = nn.CrossEntropyLoss()
# 内部已包含Softmax,输入为原始logits
# 二分类任务
criterion_bce = nn.BCELoss()
# 输入需要先通过Sigmoid
# 二分类含Sigmoid(数值稳定版)
criterion_bce_logits = nn.BCEWithLogitsLoss()
nn.Module核心机制
- 参数注册: __init__ 中定义的 nn.Parameter 自动注册为可训练参数
- 前向传播: 在 forward() 中定义数据流向,不直接调用 forward() 而是调用 model(x)
- 模块嵌套: 子模块自动注册,model.parameters() 递归获取所有参数
- 设备移动: model.to(device) 将整个模型及其参数移至指定设备
五、优化器(Optimizer)
torch.optim 模块提供了各种优化算法,用于根据计算出的梯度更新模型参数。选择合适的优化器和学习率对训练效果至关重要。
常用优化器
import torch.optim as optim
model = SimpleNet(784, 256, 10)
# SGD(随机梯度下降)
optimizer_sgd = optim.SGD(model.parameters(), lr=0.01, momentum=0.9, weight_decay=1e-4)
# Adam(自适应矩估计,最常用)
optimizer_adam = optim.Adam(model.parameters(), lr=0.001, betas=(0.9, 0.999))
# RMSprop(适用于RNN)
optimizer_rms = optim.RMSprop(model.parameters(), lr=0.01, alpha=0.99)
| 优化器 |
特点 |
适用场景 |
| SGD + Momentum |
简单稳定,可调参数少 |
CV任务、大数据集、需要精细调优 |
| Adam |
自适应学习率,收敛快 |
大多数场景首选,NLP、GAN等 |
| AdamW |
Adam + 解耦权重衰减 |
Transformer、大规模预训练 |
| RMSprop |
自适应学习率,处理非平稳目标 |
RNN、强化学习 |
学习率设置
# 固定学习率
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 学习率调度器
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)
# 每30个epoch学习率乘以0.1
# 余弦退火
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=100)
# ReduceLROnPlateau(当指标不再改善时降低学习率)
scheduler = optim.lr_scheduler.ReduceLROnPlateau(
optimizer, mode='min', factor=0.5, patience=10
)
# 每个epoch结束后调用
# scheduler.step() 或 scheduler.step(val_loss)
优化器参数组
# 对不同层设置不同学习率
optimizer = optim.SGD([
{'params': model.features.parameters()},
{'params': model.classifier.parameters(), 'lr': 1e-3}
], lr=1e-4, momentum=0.9)
# 分组作用:
# - features层使用 lr=1e-4
# - classifier层使用 lr=1e-3
# - 都使用 momentum=0.9
优化器选择建议
初学者建议直接使用 Adam(lr=0.001),收敛速度快,超参数鲁棒性强。对于熟悉任务的调优阶段,可切换为 SGD + Momentum 以获得更好的泛化性能。AdamW 在Transformer架构中已成为标准配置。
六、训练循环
训练循环是深度学习项目的核心流程,涵盖数据加载、前向传播、损失计算、反向传播和参数更新等步骤。掌握标准训练循环是进行深度学习实践的基本功。
标准训练循环
for epoch in range(num_epochs):
model.train() # 切换到训练模式
running_loss = 0.0
for batch_idx, (data, target) in enumerate(train_loader):
# 1. 将数据移动到设备
data, target = data.to(device), target.to(device)
# 2. 梯度清零
optimizer.zero_grad()
# 3. 前向传播
output = model(data)
# 4. 计算损失
loss = criterion(output, target)
# 5. 反向传播
loss.backward()
# 6. 参数更新
optimizer.step()
# 7. 统计损失
running_loss += loss.item()
数据加载(DataLoader + Dataset)
from torch.utils.data import Dataset, DataLoader
from torchvision import datasets, transforms
# 方式一:使用内置数据集
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
train_dataset = datasets.MNIST(
'./data', train=True, download=True, transform=transform
)
# 方式二:自定义Dataset
class CustomDataset(Dataset):
def __init__(self, data_path, transform=None):
# 加载数据、读取文件列表等
self.transform = transform
self.data = ... # 加载数据
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
sample = self.data[idx]
if self.transform:
sample = self.transform(sample)
return sample
# DataLoader配置
train_loader = DataLoader(
train_dataset,
batch_size=64, # 每批样本数
shuffle=True, # 训练时打乱
num_workers=4, # 数据加载线程数
pin_memory=True # 加速GPU数据传输
)
DataLoader核心参数
- batch_size: 每批次样本数,影响内存占用和梯度质量
- shuffle: 训练集 True(打乱顺序),验证/测试集 False
- num_workers: 子进程数加载数据,Windows推荐0或1
- pin_memory: 使用锁页内存,加速CPU到GPU的数据传输
- drop_last: 丢弃最后一个不完整批次(避免BatchNorm报错)
训练模式与评估模式
# 训练模式 - 启用Dropout、BatchNorm统计
model.train()
# 前向传播、反向传播...
# 评估模式 - 禁用Dropout、使用固定BatchNorm统计
model.eval()
with torch.no_grad(): # 不跟踪梯度,节省内存
for data, target in val_loader:
data, target = data.to(device), target.to(device)
output = model(data)
val_loss = criterion(output, target)
# 计算准确率
pred = output.argmax(dim=1, keepdim=True)
correct += pred.eq(target.view_as(pred)).sum().item()
完整验证循环
def evaluate(model, val_loader, criterion, device):
model.eval()
val_loss = 0.0
correct = 0
total = 0
with torch.no_grad():
for data, target in val_loader:
data, target = data.to(device), target.to(device)
output = model(data)
val_loss += criterion(output, target).item()
_, predicted = torch.max(output, 1)
total += target.size(0)
correct += (predicted == target).sum().item()
avg_loss = val_loss / len(val_loader)
accuracy = 100.0 * correct / total
return avg_loss, accuracy
# 每个epoch后调用
for epoch in range(num_epochs):
train(...) # 训练一个epoch
val_loss, acc = evaluate(model, val_loader, criterion, device)
print(f'Epoch {epoch}: Val Loss={val_loss:.4f}, Acc={acc:.2f}%')
训练流水线最佳实践
- 每个epoch交替执行 train() + eval()
- 使用 tensorboard 或 wandb 记录loss和metric曲线
- 实现 Early Stopping:验证loss连续N个epoch不下降时停止训练
- 保存 最佳模型:根据验证集指标保存最优checkpoint
- 使用 梯度裁剪(grad_clip)防止梯度爆炸
七、模型保存与加载
模型持久化是深度学习工作流的重要组成部分,支持模型部署、断点续训和迁移学习等场景。
保存与加载state_dict(推荐)
# 保存模型参数(推荐方式)
torch.save(model.state_dict(), 'model.pth')
# 加载模型参数
model = SimpleNet(784, 256, 10) # 必须先创建模型实例
model.load_state_dict(torch.load('model.pth'))
model.eval() # 切换到评估模式
为什么推荐 state_dict?
- 文件体积小(仅保存参数,不保存网络结构)
- 兼容性更好(不同版本的PyTorch之间更稳定)
- 安全性高(不会执行序列化的任意代码)
- 加载灵活(可部分加载,适用于迁移学习)
保存完整模型(不推荐)
# 保存完整模型(包括网络结构和参数)
torch.save(model, 'model_full.pth')
# 加载完整模型
model = torch.load('model_full.pth')
model.eval()
# 缺点:依赖原始类定义,代码必须保持可用
断点续训(Checkpoint)
# 保存checkpoint
checkpoint = {
'epoch': epoch,
'model_state_dict': model.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
'scheduler_state_dict': scheduler.state_dict(),
'best_val_loss': best_val_loss,
'train_losses': train_losses,
'val_losses': val_losses,
}
torch.save(checkpoint, 'checkpoint_epoch_50.pth')
# 加载checkpoint恢复训练
checkpoint = torch.load('checkpoint_epoch_50.pth')
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
scheduler.load_state_dict(checkpoint['scheduler_state_dict'])
start_epoch = checkpoint['epoch'] + 1
best_val_loss = checkpoint['best_val_loss']
设备映射技巧
在GPU上训练但需要在CPU上加载时:
torch.load('model.pth', map_location=torch.device('cpu'))
从CPU移动到GPU:
torch.load('model.pth', map_location=torch.device('cuda'))
迁移学习:部分加载
# 加载预训练模型的部分层
pretrained_dict = torch.load('pretrained.pth')
model_dict = model.state_dict()
# 筛选匹配的层(忽略不匹配的key)
pretrained_dict = {
k: v for k, v in pretrained_dict.items()
if k in model_dict and v.shape == model_dict[k].shape
}
# 更新模型参数
model_dict.update(pretrained_dict)
model.load_state_dict(model_dict)
八、核心要点总结
- 张量是核心: PyTorch以张量(Tensor)为基本数据结构,支持CPU/GPU无缝切换,与NumPy高效互操作
- 动态计算图: Autograd引擎自动跟踪张量操作并构建计算图,实现自动微分和梯度计算
- 模块化构建: nn.Module提供灵活的神经网络构建方式,支持任意复杂度的网络结构
- 优化器选择: Adam适合快速实验,SGD+Momentum适合精细调优,AdamW适合Transformer
- 标准训练循环: zero_grad() -> forward() -> loss -> backward() -> step() 五步训练范式
- 数据流水线: Dataset + DataLoader 构成高效的数据加载机制,支持多进程预取
- 模型持久化: 推荐使用 state_dict 保存参数,checkpoint 包含优化器状态用于断点续训
- 训练/评估切换: model.train() 和 model.eval() 影响Dropout和BatchNormalization行为
九、进一步学习建议
实践路径推荐
- 入门项目: 使用PyTorch实现MNIST手写数字识别(全连接网络 + CrossEntropyLoss)
- 进阶项目: CIFAR-10图像分类(卷积神经网络 + 数据增强)
- 高级项目: 使用迁移学习微调预训练模型(ResNet、BERT等)
- 实战项目: 自定义数据集 + 完整训练流水线 + 模型部署
推荐学习资源
- 官方文档: pytorch.org/docs - 最权威的API参考
- PyTorch Tutorials: pytorch.org/tutorials - 官方入门教程
- d2l.ai: 《动手学深度学习》- 理论与代码结合的最佳教材
- HuggingFace Course: 专注于NLP和Transformer的实战课程
- Kaggle: 参与竞赛,在实践中提升技能
免责声明
本学习笔记为本人学习PyTorch框架的总结整理,仅供个人学习参考。示例代码仅用于演示目的,在实际项目中请根据具体情况调整参数和架构。深度学习模型的训练效果受多种因素影响,代码不能保证在任何环境下直接运行成功。