新手必看!PyTorch基础知识全面解析:从入门到实践
前言:AI时代,为何选择PyTorch?
在人工智能浪潮席卷全球的今天,深度学习已成为推动技术进步的核心引擎。而要驾驭这股力量,选择一个强大而灵活的深度学习框架至关重要。PyTorch,由Facebook AI研究院(FAIR)开发并维护,凭借其Pythonic的风格、动态计算图、易于调试等特点,迅速在学术界和工业界占据了一席之地,成为研究人员和开发者构建、训练神经网络的首选工具之一。
对于初学者而言,PyTorch的上手体验非常友好。它更贴近Python的编程习惯,让你能够以更直观的方式理解和实现复杂的深度学习模型。本文旨在为完全没有PyTorch经验的新手提供一份全面、深入的基础知识指南,帮助你从零开始,逐步掌握PyTorch的核心概念和操作,为未来的深度学习探索打下坚实的基础。
我们将从PyTorch最基本的数据结构——张量(Tensor)讲起,深入探索自动求导(Autograd)的奥秘,然后逐步构建起神经网络模型、理解优化器与损失函数,并最终掌握数据加载、模型训练以及GPU加速等实践技巧。准备好了吗?让我们一起踏上PyTorch的学习之旅!
第一章:安装与环境配置
万事开头难,但PyTorch的安装却异常简单。良好的开始是成功的一半。
1.1 选择合适的版本
在PyTorch官网 (pytorch.org) 你可以找到详细的安装指令。选择时需要注意以下几点:
*   操作系统: Windows、macOS、Linux。
*   包管理器: pip 或 conda (推荐,更易管理环境)。
*   Python版本: 通常支持较新的Python版本。
*   CUDA版本: 如果你的电脑有NVIDIA显卡并希望使用GPU加速,需要安装与你的显卡驱动兼容的CUDA版本。如果没有,可以选择CPU版本。
1.2 安装示例 (使用conda)
假设我们要在Linux系统上安装支持CUDA 11.8的PyTorch,推荐使用Anaconda或Miniconda来管理Python环境。
“`bash
创建一个新的conda环境 (可选,但推荐)
conda create -n my_pytorch_env python=3.9
conda activate my_pytorch_env
从PyTorch官网获取的安装命令
例如,如果你的CUDA版本是11.8
conda install pytorch torchvision torchaudio pytorch-cuda=11.8 -c pytorch -c nvidia
如果你不需要GPU加速,只安装CPU版本
conda install pytorch torchvision torchaudio cpuonly -c pytorch
“`
安装完成后,可以通过Python交互式环境进行验证:
python
import torch
print(torch.__version__)
print(torch.cuda.is_available()) # 检查GPU是否可用
如果 torch.cuda.is_available() 返回 True,恭喜你,你的GPU加速环境已配置成功!
第二章:PyTorch的基石——张量(Tensor)
张量是PyTorch中最基本的数据结构,它是一个多维数组,与NumPy的 ndarray 非常相似,但张量还支持GPU加速和自动求导功能。你可以把它想象成:
*   0维张量:标量(Scalar),一个独立的数值。
*   1维张量:向量(Vector),一串数值。
*   2维张量:矩阵(Matrix),一个二维表格。
*   N维张量:高维数组。
2.1 张量的创建
PyTorch提供了多种创建张量的方法:
a. 直接从数据创建
“`python
import torch
从Python列表创建
data = [[1, 2], [3, 4]]
x_data = torch.tensor(data)
print(“直接从数据创建的张量:\n”, x_data)
输出:
直接从数据创建的张量:
tensor([[1, 2],
[3, 4]])
从NumPy数组创建 (张量与NumPy数组共享内存,修改其中一个会影响另一个)
import numpy as np
np_array = np.array(data)
x_np = torch.from_numpy(np_array)
print(“从NumPy数组创建的张量:\n”, x_np)
输出:
从NumPy数组创建的张量:
tensor([[1, 2],
[3, 4]])
“`
b. 创建特定形状和值的张量
“`python
创建一个2×3的全1张量
x_ones = torch.ones(2, 3)
print(“全1张量:\n”, x_ones)
输出:
全1张量:
tensor([[1., 1., 1.],
[1., 1., 1.]])
创建一个2×3的全0张量
x_zeros = torch.zeros(2, 3)
print(“全0张量:\n”, x_zeros)
输出:
全0张量:
tensor([[0., 0., 0.],
[0., 0., 0.]])
创建一个2×3的随机张量 (均匀分布在[0,1)之间)
x_rand = torch.rand(2, 3)
print(“随机张量 (rand):\n”, x_rand)
输出:
随机张量 (rand):
tensor([[0.8206, 0.5057, 0.5061],
[0.6033, 0.4578, 0.4632]])
创建一个2×3的随机张量 (标准正态分布,均值为0,方差为1)
x_randn = torch.randn(2, 3)
print(“随机张量 (randn):\n”, x_randn)
输出:
随机张量 (randn):
tensor([[-0.2081, 1.1396, -0.6358],
[ 1.3435, 1.4243, -0.6433]])
创建一个张量,其形状和数据类型与另一个张量相同,但值为随机
x_like = torch.rand_like(x_data, dtype=torch.float)
print(“形状和数据类型与x_data相同,但值为随机的张量:\n”, x_like)
输出:
形状和数据类型与x_data相同,但值为随机的张量:
tensor([[0.2033, 0.8164],
[0.6074, 0.4468]])
“`
2.2 张量的属性
张量有三个核心属性,它们描述了张量的形状、数据类型和存储设备:
“`python
tensor = torch.rand(3, 4)
print(f”张量的形状 (Shape): {tensor.shape}”)
print(f”张量的数据类型 (Dtype): {tensor.dtype}”)
print(f”张量存储的设备 (Device): {tensor.device}”)
输出:
张量的形状 (Shape): torch.Size([3, 4])
张量的数据类型 (Dtype): torch.float32
张量存储的设备 (Device): cpu
``shape
*:张量的维度信息,例如(3, 4)表示3行4列的矩阵。dtype
*:张量中元素的数据类型,例如torch.float32(单精度浮点数)、torch.int64(长整型)等。device
*:张量所存储的设备,cpu表示中央处理器,cuda:0` 表示第一个GPU。
2.3 张量的操作
张量支持各种常见的数学运算、索引、切片和形状变换。
a. 算术运算
“`python
tensor = torch.ones(4, 4)
tensor[:,1] = 0 # 将第二列设置为0
print(“原始张量:\n”, tensor)
输出:
原始张量:
tensor([[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.]])
逐元素相加
tensor_add = tensor + tensor
print(“逐元素相加:\n”, tensor_add)
矩阵乘法
tensor_mul = tensor.matmul(tensor.T) # .T 是转置
print(“矩阵乘法:\n”, tensor_mul)
逐元素乘法
tensor_elem_mul = tensor * tensor
print(“逐元素乘法:\n”, tensor_elem_mul)
也可以使用函数形式
torch.add(tensor, tensor)
torch.mul(tensor, tensor)
torch.matmul(tensor, tensor.T)
“`
b. 索引和切片
与NumPy类似,可以使用索引和切片访问张量的子集:
“`python
tensor = torch.arange(16).reshape(4, 4)
print(“原始张量:\n”, tensor)
输出:
原始张量:
tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
访问第一行
print(“第一行:”, tensor[0])
访问第二列
print(“第二列:”, tensor[:, 1])
访问第一行第二列的元素
print(“第一行第二列的元素:”, tensor[0, 1])
访问从第二行到第三行(不包括第四行)的所有列
print(“部分行切片:\n”, tensor[1:3, :])
访问所有行,从第二列到第三列(不包括第四列)
print(“部分列切片:\n”, tensor[:, 1:3])
“`
c. 形状变换
“`python
tensor = torch.ones(4, 4)
使用 .view() 或 .reshape() 改变张量形状 (元素数量必须一致)
reshaped_tensor = tensor.view(2, 8)
print(“reshape后:\n”, reshaped_tensor)
-1 表示该维度大小由其他维度推断
reshaped_tensor_auto = tensor.view(-1, 2) # 自动推断行数
print(“自动推断reshape后:\n”, reshaped_tensor_auto)
使用 .squeeze() 和 .unsqueeze() 移除或添加维度
x = torch.zeros(1, 3, 1, 5)
print(“原始形状:”, x.shape) # torch.Size([1, 3, 1, 5])
y = x.squeeze() # 移除所有维度大小为1的维度
print(“squeeze后形状:”, y.shape) # torch.Size([3, 5])
z = y.unsqueeze(0) # 在第0维添加一个维度
print(“unsqueeze后形状:”, z.shape) # torch.Size([1, 3, 5])
“`
d. CPU与GPU之间传输
python
if torch.cuda.is_available():
tensor = tensor.to("cuda") # 将张量移动到GPU
print(f"张量在GPU上: {tensor.device}")
tensor = tensor.to("cpu")  # 将张量移回CPU
print(f"张量在CPU上: {tensor.device}")
else:
print("CUDA不可用,张量仍在CPU上。")
张量是PyTorch深度学习的基石,熟练掌握其创建、属性和操作是迈向高级应用的第一步。
第三章:自动求导的魔力——Autograd
深度学习的核心是反向传播(Backpropagation),它通过计算损失函数相对于模型参数的梯度来更新参数。PyTorch的Autograd(自动求导)系统正是幕后的“魔术师”,它能够自动为任何计算图中的张量计算梯度。
3.1 核心概念:requires_grad
默认情况下,张量的requires_grad属性是False。只有当一个张量的requires_grad设置为True时,PyTorch才会跟踪其上的所有操作,以便在后续计算梯度。
“`python
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
y = x * 2
z = y.sum()
print(“x:”, x)            # tensor([1., 2., 3.], requires_grad=True)
print(“y:”, y)            # tensor([2., 4., 6.], grad_fn=
print(“z:”, z)            # tensor(12., grad_fn=
对z进行反向传播
z.backward()
访问x的梯度
print(“x的梯度:”, x.grad)  # tensor([2., 2., 2.])
``z = (x_02) + (x_12) + (x_2*2)
在这个例子中,。dz/dx_0 = 2
**dz/dx_1 = 2*dz/dx_2 = 2因此,x.grad的结果是[2., 2., 2.]`。
3.2 梯度清零:zero_grad()
在神经网络训练中,每次迭代(或每个批次)都需要计算新的梯度。PyTorch会将梯度累加到.grad属性中。因此,在每次反向传播之前,我们必须手动将模型的梯度清零,以避免旧梯度的干扰。
“`python
x = torch.tensor([1.0, 2.0], requires_grad=True)
y = x * 2
z = y.sum()
z.backward()
print(“第一次反向传播后的x.grad:”, x.grad) # tensor([2., 2.])
再次进行计算
z2 = (x * 3).sum()
z2.backward()
print(“第二次反向传播后的x.grad:”, x.grad) # tensor([5., 5.]) (2+3累加了)
正确的做法是每次迭代前清零
x = torch.tensor([1.0, 2.0], requires_grad=True)
x.grad = None # 或者通过optimizer.zero_grad()来清零
y = x * 2
z = y.sum()
z.backward()
print(“清零后第一次反向传播后的x.grad:”, x.grad) # tensor([2., 2.])
x.grad.zero_() # 手动清零梯度
y2 = x * 3
z2 = y2.sum()
z2.backward()
print(“清零后第二次反向传播后的x.grad:”, x.grad) # tensor([3., 3.])
``optimizer.zero_grad()`。
通常,我们通过优化器对象来清零梯度:
3.3 禁用梯度计算:torch.no_grad()
在模型评估或推理阶段,我们不需要计算梯度,这不仅可以节省内存,还能提高计算速度。可以使用torch.no_grad()上下文管理器来临时禁用梯度计算。
“`python
x = torch.tensor([1.0, 2.0], requires_grad=True)
with torch.no_grad():
y = x * 2
print(“在no_grad上下文中,y是否需要梯度:”, y.requires_grad) # False
退出no_grad上下文,梯度计算恢复
z = x * 3
print(“在no_grad上下文外,z是否需要梯度:”, z.requires_grad) # True
“`
理解Autograd是理解神经网络训练过程的关键,它极大地简化了深度学习框架的实现难度。
第四章:构建神经网络——nn.Module
PyTorch的torch.nn模块提供了构建神经网络所需的各种层(layers)和模块。所有神经网络模型都应该继承自nn.Module类。
4.1 nn.Module 的基本结构
每个自定义的神经网络模型都必须:
*   继承 nn.Module。
*   在 __init__ 方法中定义网络的层。
*   在 forward 方法中定义数据如何在这些层之间流动(即前向传播)。
“`python
import torch.nn as nn
import torch.nn.functional as F
class SimpleNeuralNetwork(nn.Module):
def init(self):
super(SimpleNeuralNetwork, self).init()
# 定义网络层
# nn.Linear(in_features, out_features) 是全连接层
self.fc1 = nn.Linear(784, 128) # 输入784个特征(例如28×28图像展平),输出128个特征
self.relu = nn.ReLU()          # 激活函数,非线性变换
self.fc2 = nn.Linear(128, 10)  # 输出10个特征(例如10个类别的分类)
def forward(self, x):
    # 定义数据的前向传播路径
    x = self.fc1(x)
    x = self.relu(x)
    x = self.fc2(x)
    return x
实例化模型
model = SimpleNeuralNetwork()
print(model)
输出:
SimpleNeuralNetwork(
(fc1): Linear(in_features=784, out_features=128, bias=True)
(relu): ReLU()
(fc2): Linear(in_features=128, out_features=10, bias=True)
)
随机生成一个输入数据 (批大小为64,784个特征)
dummy_input = torch.randn(64, 784)
output = model(dummy_input)
print(“模型输出形状:”, output.shape) # torch.Size([64, 10])
“`
4.2 常用的神经网络层
torch.nn提供了丰富的层类型:
*   线性层 (Linear Layers): nn.Linear (全连接层)。
*   卷积层 (Convolutional Layers): nn.Conv1d, nn.Conv2d, nn.Conv3d (用于图像、序列数据等)。
*   池化层 (Pooling Layers): nn.MaxPool1d, nn.MaxPool2d, nn.AvgPool2d (用于降采样)。
*   激活函数 (Activation Functions): nn.ReLU, nn.Sigmoid, nn.Tanh, nn.LeakyReLU 等。这些也可以通过 torch.nn.functional 调用,如 F.relu()。
*   循环神经网络层 (Recurrent Layers): nn.RNN, nn.LSTM, nn.GRU (用于序列数据)。
*   批归一化 (Batch Normalization): nn.BatchNorm1d, nn.BatchNorm2d (用于加速训练和提高稳定性)。
*   Dropout 层: nn.Dropout (用于防止过拟合)。
4.3 序列化容器:nn.Sequential
对于简单的、按顺序堆叠的层,可以使用nn.Sequential来更简洁地定义模型:
“`python
model_sequential = nn.Sequential(
nn.Linear(784, 128),
nn.ReLU(),
nn.Linear(128, 64),
nn.ReLU(),
nn.Linear(64, 10)
)
print(model_sequential)
输出:
Sequential(
(0): Linear(in_features=784, out_features=128, bias=True)
(1): ReLU()
(2): Linear(in_features=128, out_features=64, bias=True)
(3): ReLU()
(4): Linear(in_features=64, out_features=10, bias=True)
)
``nn.Sequential`的优点是代码简洁,但缺点是灵活性较低,无法实现复杂的网络结构(如分支、跳跃连接等)。
第五章:优化器与损失函数
构建完模型后,我们需要定义如何衡量模型预测的“好坏”(损失函数),以及如何调整模型参数来最小化这个“坏”(优化器)。
5.1 损失函数(Loss Functions)
损失函数(或称成本函数、误差函数)衡量模型预测值与真实值之间的差异。PyTorch的nn模块也提供了多种常用的损失函数。
- 
分类问题:
nn.CrossEntropyLoss():常用于多分类问题,它结合了nn.LogSoftmax和nn.NLLLoss。输入是模型的原始输出(logits),目标是类别索引。nn.BCELoss():用于二分类问题,通常与nn.Sigmoid激活函数一起使用。nn.BCEWithLogitsLoss():BCELoss的稳定版本,集成了Sigmoid,直接接收模型的原始输出。
 - 
回归问题:
nn.MSELoss()(Mean Squared Error Loss):均方误差损失,适用于回归任务。nn.L1Loss()(Mean Absolute Error Loss):平均绝对误差损失,对异常值不敏感。
 
“`python
示例:交叉熵损失 (分类)
loss_fn_ce = nn.CrossEntropyLoss()
predictions = torch.randn(3, 5) # 3个样本,5个类别
targets = torch.tensor([1, 0, 4]) # 真实类别索引
loss = loss_fn_ce(predictions, targets)
print(“交叉熵损失:”, loss)
示例:均方误差损失 (回归)
loss_fn_mse = nn.MSELoss()
predictions_reg = torch.randn(5, 1) # 5个样本,1个输出
targets_reg = torch.randn(5, 1)
loss_reg = loss_fn_mse(predictions_reg, targets_reg)
print(“均方误差损失:”, loss_reg)
“`
5.2 优化器(Optimizers)
优化器的任务是根据损失函数计算出的梯度,更新模型的权重和偏置,使损失函数最小化。PyTorch的torch.optim模块提供了各种优化算法。
torch.optim.SGD(Stochastic Gradient Descent): 随机梯度下降,最基础的优化器,可以添加动量(momentum)来加速收敛。torch.optim.Adam: 自适应矩估计,广泛使用的优化器,通常收敛速度更快,性能更好。torch.optim.Adagrad:torch.optim.RMSprop:
“`python
import torch.optim as optim
实例化模型
model = SimpleNeuralNetwork()
实例化优化器
参数1: model.parameters() 告诉优化器需要更新哪些参数
参数2: lr (learning rate) 学习率,控制每次更新的步长
optimizer_sgd = optim.SGD(model.parameters(), lr=0.01)
optimizer_adam = optim.Adam(model.parameters(), lr=0.001)
在训练循环中的使用:
optimizer.zero_grad() # 清零梯度
loss.backward() # 计算梯度
optimizer.step() # 根据梯度更新参数
“`
优化器是深度学习训练的核心,它的选择和学习率的设置对模型的性能至关重要。
第六章:数据处理——Dataset与DataLoader
在实际的深度学习任务中,我们通常需要处理大量数据,并且以小批量(mini-batches)的形式进行训练。PyTorch提供了torch.utils.data.Dataset和torch.utils.data.DataLoader两个工具来高效地管理数据。
6.1 Dataset:数据样本的集合
Dataset是一个抽象类,表示一个数据集,其中包含样本及其对应的标签。你需要自定义一个继承自Dataset的类,并重写两个方法:
*   __len__:返回数据集中的样本总数。
*   __getitem__:根据给定的索引返回一个样本及其标签。
“`python
from torch.utils.data import Dataset, DataLoader
import pandas as pd
class CustomDataset(Dataset):
def init(self, csv_file, transform=None):
“””
Args:
csv_file (string): 包含数据和标签的CSV文件路径。
transform (callable, optional): 应用于样本的A函数/变换。
“””
self.data_frame = pd.read_csv(csv_file)
self.transform = transform
def __len__(self):
    return len(self.data_frame)
def __getitem__(self, idx):
    # 假设CSV文件第一列是特征,第二列是标签
    features = torch.tensor(self.data_frame.iloc[idx, 0].split(',')).float() # 示例:如果特征是逗号分隔的字符串
    label = torch.tensor(self.data_frame.iloc[idx, 1]).long()
    if self.transform:
        features = self.transform(features)
    sample = {'features': features, 'label': label}
    return sample
假设你有一个名为 ‘my_data.csv’ 的文件
示例数据格式 (my_data.csv):
“1.0,2.0,3.0”,0
“4.0,5.0,6.0”,1
(实际使用时需要根据数据格式调整__getitem__方法)
创建一个虚拟csv文件用于演示
with open(‘my_data.csv’, ‘w’) as f:
f.write(“1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,20.0,21.0,22.0,23.0,24.0,25.0,26.0,27.0,28.0,29.0,30.0,31.0,32.0,33.0,34.0,35.0,36.0,37.0,38.0,39.0,40.0,41.0,42.0,43.0,44.0,45.0,46.0,47.0,48.0,49.0,50.0,51.0,52.0,53.0,54.0,55.0,56.0,57.0,58.0,59.0,60.0,61.0,62.0,63.0,64.0,65.0,66.0,67.0,68.0,69.0,70.0,71.0,72.0,73.0,74.0,75.0,76.0,77.0,78.0,79.0,80.0,81.0,82.0,83.0,84.0,85.0,86.0,87.0,88.0,89.0,90.0,91.0,92.0,93.0,94.0,95.0,96.0,97.0,98.0,99.0,100.0,101.0,102.0,103.0,104.0,105.0,106.0,107.0,108.0,109.0,110.0,111.0,112.0,113.0,114.0,115.0,116.0,117.0,118.0,119.0,120.0,121.0,122.0,123.0,124.0,125.0,126.0,127.0,128.0″,0\n”)
f.write(“1.0,2.0,3.0,…,128.0″,1\n”) # 仅为示例,实际应有完整128个特征
dataset = CustomDataset(csv_file=’my_data.csv’)
print(“数据集大小:”, len(dataset))
sample = dataset[0]
print(“第一个样本特征形状:”, sample[‘features’].shape, ” 标签:”, sample[‘label’])
为了避免文件操作的复杂性,这里简化`CustomDataset`为直接生成数据:python
class SimpleDataset(Dataset):
def init(self, num_samples=100, feature_dim=784, num_classes=10):
self.features = torch.randn(num_samples, feature_dim)
self.labels = torch.randint(0, num_classes, (num_samples,))
def __len__(self):
    return len(self.labels)
def __getitem__(self, idx):
    return self.features[idx], self.labels[idx]
dataset = SimpleDataset()
print(“数据集大小:”, len(dataset))
sample_features, sample_label = dataset[0]
print(“第一个样本特征形状:”, sample_features.shape, ” 标签:”, sample_label)
“`
6.2 DataLoader:批量加载数据
DataLoader包装了Dataset,并提供以下功能:
*   批量处理 (batching): 将多个样本组合成一个批次。
*   数据混洗 (shuffling): 在每个epoch开始时打乱数据,减少模型对数据顺序的依赖。
*   并行加载 (multi-process data loading): 使用多进程来加速数据加载(num_workers参数)。
“`python
实例化自定义数据集
train_dataset = SimpleDataset(num_samples=1000, feature_dim=784, num_classes=10)
实例化DataLoader
train_loader = DataLoader(dataset=train_dataset,
batch_size=64,   # 每个批次64个样本
shuffle=True,    # 每个epoch开始时打乱数据
num_workers=2)   # 使用2个子进程加载数据 (Windows上设为0避免多进程问题)
遍历DataLoader
for batch_idx, (features, labels) in enumerate(train_loader):
print(f”批次 {batch_idx+1}:”)
print(”  特征形状:”, features.shape) # torch.Size([64, 784]) (最后一个批次可能小于64)
print(”  标签形状:”, labels.shape)   # torch.Size([64])
if batch_idx == 2: # 只打印前3个批次
break
``Dataset和DataLoader`是处理大规模数据集的关键工具,它们能够大大提高数据加载的效率和训练的便捷性。
第七章:完整的训练循环
现在,我们已经掌握了构建模型、定义损失函数和优化器,以及加载数据的所有基础知识。是时候把它们组合起来,创建一个完整的模型训练循环了。
“`python
1. 准备数据
train_dataset = SimpleDataset(num_samples=1000, feature_dim=784, num_classes=10)
train_loader = DataLoader(dataset=train_dataset, batch_size=64, shuffle=True, num_workers=0) # num_workers=0 for simple demo
2. 定义模型
class MyClassifier(nn.Module):
def init(self, input_dim, hidden_dim, output_dim):
super(MyClassifier, self).init()
self.fc1 = nn.Linear(input_dim, hidden_dim)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(hidden_dim, output_dim)
def forward(self, x):
    out = self.fc1(x)
    out = self.relu(out)
    out = self.fc2(out)
    return out
input_dim = 784
hidden_dim = 128
output_dim = 10 # 10个类别
model = MyClassifier(input_dim, hidden_dim, output_dim)
3. 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
4. 设置训练设备 (GPU或CPU)
device = torch.device(“cuda” if torch.cuda.is_available() else “cpu”)
model.to(device) # 将模型移动到指定设备
5. 训练循环
num_epochs = 10
print(f”开始在 {device} 上训练模型…”)
for epoch in range(num_epochs):
model.train() # 设置模型为训练模式
running_loss = 0.0
for i, (inputs, labels) in enumerate(train_loader):
inputs = inputs.to(device) # 将数据移动到指定设备
labels = labels.to(device)
    # 前向传播
    outputs = model(inputs)
    loss = criterion(outputs, labels)
    # 反向传播和优化
    optimizer.zero_grad() # 清零梯度
    loss.backward()       # 计算梯度
    optimizer.step()      # 更新模型参数
    running_loss += loss.item() * inputs.size(0)
epoch_loss = running_loss / len(train_dataset)
print(f"Epoch [{epoch+1}/{num_epochs}], Loss: {epoch_loss:.4f}")
print(“训练完成!”)
6. (可选) 模型评估
假设我们有一个测试数据集 test_loader
model.eval() # 设置模型为评估模式 (禁用Dropout等)
correct = 0
total = 0
with torch.no_grad(): # 在评估时禁用梯度计算
for inputs, labels in test_loader:
inputs = inputs.to(device)
labels = labels.to(device)
outputs = model(inputs)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print(f’测试集上的准确率: {100 * correct / total:.2f}%’)
“`
这个训练循环包含了深度学习模型训练的全部核心步骤:数据加载、前向传播、损失计算、反向传播、参数优化。
第八章:模型的保存与加载
训练好的模型通常需要保存起来,以便后续使用(如推理、继续训练或部署)。PyTorch提供了灵活的模型保存和加载机制。
8.1 仅保存和加载模型参数 (推荐)
这是最常见和推荐的方法。它只保存模型的state_dict(包含模型所有学习到的参数,如权重和偏置)。
优点:更灵活,可以在不同模型架构之间加载参数子集,也更小。
缺点:加载时需要先实例化模型架构。
“`python
保存模型参数
PATH = “my_model_state_dict.pth”
torch.save(model.state_dict(), PATH)
print(f”模型参数已保存到 {PATH}”)
加载模型参数
1. 首先需要重新实例化模型架构
loaded_model = MyClassifier(input_dim, hidden_dim, output_dim)
loaded_model.to(device) # 确保模型也在正确的设备上
2. 加载state_dict
loaded_model.load_state_dict(torch.load(PATH, map_location=device)) # map_location确保加载到正确设备
loaded_model.eval() # 加载后通常设置为评估模式
print(“模型参数已加载。”)
示例:使用加载的模型进行预测
dummy_input_eval = torch.randn(1, input_dim).to(device)
with torch.no_grad():
output_eval = loaded_model(dummy_input_eval)
print(“加载模型后的预测输出:”, output_eval)
“`
8.2 保存和加载整个模型
直接保存整个模型对象。
优点:方便,加载时无需重新实例化模型架构。
缺点:强依赖于模型定义的代码,如果代码结构改变,可能无法加载;文件大小通常更大。
“`python
保存整个模型
PATH_FULL = “my_model_full.pth”
torch.save(model, PATH_FULL)
print(f”整个模型已保存到 {PATH_FULL}”)
加载整个模型
注意:这需要模型定义的代码(MyClassifier)在当前环境中可用
loaded_full_model = torch.load(PATH_FULL, map_location=device)
loaded_full_model.eval()
print(“整个模型已加载。”)
``state_dict`的方式。
对于初学者和大多数场景,推荐使用保存和加载
第九章:GPU加速
利用NVIDIA GPU进行并行计算是深度学习能够快速发展的重要原因之一。PyTorch对GPU支持非常好,只需几行代码就能将计算转移到GPU上。
“`python
检查CUDA是否可用
if torch.cuda.is_available():
print(“CUDA可用!”)
device = torch.device(“cuda”)
print(f”当前使用的GPU设备: {torch.cuda.get_device_name(0)}”) # 假设有多个GPU,使用第一个
else:
print(“CUDA不可用,将使用CPU。”)
device = torch.device(“cpu”)
将模型移动到GPU
model = MyClassifier(input_dim, hidden_dim, output_dim)
model.to(device)
将数据移动到GPU
x = torch.randn(64, 784)
x = x.to(device)
进行计算
output = model(x)
print(f”模型输出的设备: {output.device}”) # cuda:0 或 cpu
``.cpu()
**重要提示:**
*   **模型和数据必须在同一个设备上。** 如果模型在GPU上,那么输入数据也必须在GPU上,反之亦然。否则会报错。
*   **输出结果也在同一个设备上。**
*   当你需要将GPU上的张量转换为NumPy数组或进行一些CPU特定的操作时,需要先使用将张量移回CPU,例如tensor.cpu().numpy()`。
总结与展望
恭喜你,已经完成了PyTorch基础知识的全面学习!我们从张量的操作开始,深入理解了自动求导的原理,学会了如何构建神经网络模型、选择合适的损失函数和优化器,掌握了数据加载的最佳实践,并实现了完整的模型训练循环和模型保存加载。最后,我们还探讨了GPU加速的关键技巧。
回顾重点:
*   张量 (Tensor): PyTorch的核心数据结构,多维数组,支持GPU和自动求导。
*   自动求导 (Autograd): 实现了反向传播,通过requires_grad和.backward()自动计算梯度。
*   nn.Module: 神经网络的基类,定义层和前向传播。
*   损失函数 (Loss Functions): 衡量模型预测与真实值差异。
*   优化器 (Optimizers): 根据梯度更新模型参数。
*   Dataset & DataLoader: 高效处理和加载数据。
*   训练循环: 将所有组件整合,实现模型训练。
*   模型保存/加载: 推荐使用state_dict。
*   GPU加速: 通过.to(device)将模型和数据移至GPU。
接下来的学习之路:
1.  实践项目: 动手尝试构建和训练一个简单的图像分类(如MNIST)或文本分类模型。
2.  深入理解: 学习更复杂的层(如卷积层、循环层),更高级的优化器。
3.  数据预处理: 探索torchvision.transforms等工具进行数据增强和预处理。
4.  模型部署: 了解如何将训练好的模型部署到实际应用中。
5.  阅读官方文档: PyTorch官方文档是最好的学习资源,包含丰富的教程和API参考。
PyTorch的生态系统强大且活跃,掌握了这些基础知识,你就已经具备了进入深度学习世界并解决实际问题的能力。祝你在未来的AI学习和实践中取得丰硕的成果!