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生态系统

二、张量基础(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()
  • 使用 tensorboardwandb 记录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)

八、核心要点总结

九、进一步学习建议

实践路径推荐

  1. 入门项目: 使用PyTorch实现MNIST手写数字识别(全连接网络 + CrossEntropyLoss)
  2. 进阶项目: CIFAR-10图像分类(卷积神经网络 + 数据增强)
  3. 高级项目: 使用迁移学习微调预训练模型(ResNet、BERT等)
  4. 实战项目: 自定义数据集 + 完整训练流水线 + 模型部署

推荐学习资源

  • 官方文档: pytorch.org/docs - 最权威的API参考
  • PyTorch Tutorials: pytorch.org/tutorials - 官方入门教程
  • d2l.ai: 《动手学深度学习》- 理论与代码结合的最佳教材
  • HuggingFace Course: 专注于NLP和Transformer的实战课程
  • Kaggle: 参与竞赛,在实践中提升技能

免责声明

本学习笔记为本人学习PyTorch框架的总结整理,仅供个人学习参考。示例代码仅用于演示目的,在实际项目中请根据具体情况调整参数和架构。深度学习模型的训练效果受多种因素影响,代码不能保证在任何环境下直接运行成功。