PyTorch 教程:从基础到实践的完整指南
PyTorch 是一个开源的机器学习库,广泛应用于深度学习领域。它以其灵活性、易用性和强大的功能而受到研究人员和开发者的青睐。本教程将带您从 PyTorch 的基础知识开始,逐步深入到实际应用,帮助您掌握使用 PyTorch 构建和训练神经网络的技能。
目录
- PyTorch 简介
- 安装 PyTorch
- PyTorch 基础:张量 (Tensors)
- 创建张量
- 张量操作
- 张量与 NumPy 转换
- GPU 加速
- 自动微分 (Autograd)
requires_grad属性- 计算梯度
- 构建神经网络 (torch.nn)
nn.Module- 层 (Layers)
- 激活函数
- 损失函数
- 优化器
- 数据加载与预处理 (torch.utils.data)
DatasetDataLoader- 数据转换 (Transforms)
- 模型训练流程
- 定义模型
- 定义损失函数和优化器
- 训练循环
- 模型评估
- 模型保存与加载
- 实践案例:图像分类 (CNN)
- 数据准备
- 构建 CNN 模型
- 训练与评估
- 总结与展望
1. PyTorch 简介
PyTorch 最初由 Facebook (现在是 Meta) 的人工智能研究实验室开发。它提供了两个主要功能:
* 一个强大的 N 维数组 (张量) 库,支持 GPU 加速计算。
* 一个基于自动微分的深度学习框架,用于构建和训练神经网络。
PyTorch 的设计理念是“define-by-run”,即计算图在运行时动态构建。这使得 PyTorch 在调试和研究方面非常灵活。
2. 安装 PyTorch
安装 PyTorch 非常简单。推荐使用 conda 或 pip。请访问 PyTorch 官方网站 (pytorch.org) 选择适合您操作系统和 CUDA 版本的安装命令。
示例 (使用 pip,CUDA 11.8):
bash
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
示例 (使用 pip,仅 CPU):
bash
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
安装完成后,您可以通过运行以下 Python 代码来验证安装:
python
import torch
print(torch.__version__)
print(torch.cuda.is_available()) # 检查 GPU 是否可用
3. PyTorch 基础:张量 (Tensors)
张量是 PyTorch 的核心数据结构,类似于 NumPy 的 ndarray,但支持 GPU 加速。
创建张量
您可以从 Python 列表、NumPy 数组或其他张量创建张量。
“`python
import torch
import numpy as np
从列表创建张量
data = [[1, 2], [3, 4]]
x_data = torch.tensor(data)
print(f”From list: \n{x_data}\n”)
从 NumPy 数组创建张量
np_array = np.array(data)
x_np = torch.from_numpy(np_array)
print(f”From NumPy: \n{x_np}\n”)
创建全为 1 的张量
x_ones = torch.ones_like(x_data) # 形状与 x_data 相同
print(f”Ones Tensor: \n{x_ones}\n”)
创建随机张量
x_rand = torch.rand_like(x_data, dtype=torch.float) # 形状与 x_data 相同,指定数据类型
print(f”Random Tensor: \n{x_rand}\n”)
创建特定形状的张量
shape = (2, 3,)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)
rand_tensor = torch.rand(shape)
print(f”Ones Tensor: \n{ones_tensor}\n”)
print(f”Zeros Tensor: \n{zeros_tensor}\n”)
print(f”Random Tensor: \n{rand_tensor}\n”)
“`
张量的属性:
python
tensor = torch.rand(3, 4)
print(f"Shape of tensor: {tensor.shape}")
print(f"Dtype of tensor: {tensor.dtype}")
print(f"Device of tensor: {tensor.device}")
张量操作
张量支持各种数学运算,类似于 NumPy。
“`python
tensor = torch.ones(4, 4)
print(f”First row: {tensor[0]}”)
print(f”First column: {tensor[:, 0]}”)
print(f”Last column: {tensor[…, -1]}”)
tensor[:,1] = 0
print(tensor)
拼接张量
t1 = torch.cat([tensor, tensor, tensor], dim=1)
print(f”Concatenated tensor: \n{t1}\n”)
矩阵乘法
tensor_mul = tensor.matmul(tensor.T) # 矩阵乘法
print(f”Matrix multiplication: \n{tensor_mul}\n”)
tensor_mul_alt = tensor @ tensor.T # 也可以使用 @ 运算符
print(f”Matrix multiplication (alt): \n{tensor_mul_alt}\n”)
元素级乘法
tensor_elem_mul = tensor.mul(tensor)
print(f”Element-wise multiplication: \n{tensor_elem_mul}\n”)
tensor_elem_mul_alt = tensor * tensor # 也可以使用 * 运算符
print(f”Element-wise multiplication (alt): \n{tensor_elem_mul_alt}\n”)
原地操作 (in-place operations)
以 _ 结尾的操作会原地修改张量,例如 add_()
print(f”Original tensor: \n{tensor}\n”)
tensor.add_(5)
print(f”Tensor after add_: \n{tensor}\n”)
“`
张量与 NumPy 转换
PyTorch 张量和 NumPy 数组可以方便地互相转换。
“`python
张量转 NumPy
np_array = tensor.numpy()
print(f”Tensor to NumPy: \n{np_array}\n”)
NumPy 转张量
tensor_from_np = torch.from_numpy(np_array)
print(f”NumPy to Tensor: \n{tensor_from_np}\n”)
“`
注意: CPU 上的张量和 NumPy 数组会共享底层内存。修改其中一个,另一个也会改变。
GPU 加速
如果您的系统配备了 NVIDIA GPU 并安装了 CUDA,PyTorch 可以将张量和计算移动到 GPU 上,从而显著加速计算。
“`python
if torch.cuda.is_available():
print(“CUDA is available! Moving tensor to GPU…”)
device = “cuda”
else:
print(“CUDA not available. Using CPU…”)
device = “cpu”
tensor = torch.ones(4, 4, device=device) # 在 GPU 上创建张量
print(f”Tensor on device: {tensor.device}\n”)
将现有张量移动到 GPU
tensor_cpu = torch.rand(4, 4)
tensor_gpu = tensor_cpu.to(device)
print(f”Tensor on CPU: {tensor_cpu.device}”)
print(f”Tensor on GPU: {tensor_gpu.device}\n”)
GPU 上的计算
result_gpu = tensor_gpu.matmul(tensor_gpu.T)
print(f”Result on GPU: \n{result_gpu}\n”)
“`
4. 自动微分 (Autograd)
PyTorch 的 autograd 模块是其深度学习功能的核心。它能够自动计算神经网络中的所有梯度。
requires_grad 属性
每个张量都有一个 requires_grad 属性。如果设置为 True,PyTorch 将会跟踪该张量上的所有操作,以便在反向传播时计算梯度。
“`python
x = torch.ones(5, 5, requires_grad=True)
print(x.requires_grad)
y = x + 2
print(y.requires_grad) # y 的 requires_grad 属性会根据 x 自动继承
z = y * y * 3
out = z.mean()
print(z.requires_grad)
print(out.requires_grad)
“`
计算梯度
当您在张量上执行操作并设置 requires_grad=True 时,PyTorch 会构建一个计算图。调用 backward() 方法时,PyTorch 会遍历这个图,计算所有参与计算的张量的梯度。
“`python
x = torch.tensor([[1.0, 2.0], [3.0, 4.0]], requires_grad=True)
print(f”x: \n{x}\n”)
y = x**2 + 5
print(f”y: \n{y}\n”)
z = y.sum() # 对 y 的所有元素求和
print(f”z: {z}\n”)
反向传播,计算梯度
z.backward()
访问梯度
print(f”x.grad: \n{x.grad}\n”)
理论上,z = (x1^2 + 5) + (x2^2 + 5) + (x3^2 + 5) + (x4^2 + 5)
dz/dx1 = 2*x1
所以 x.grad 应该为 [[21, 22], [23, 24]] = [[2, 4], [6, 8]]
“`
注意:
* 梯度会累加到 grad 属性中,因此在每次迭代之前,通常需要使用 optimizer.zero_grad() 来清零梯度。
* 对于标量输出 (如 loss.backward()),不需要传入 gradient 参数。对于非标量输出,需要传入一个与张量形状相同的梯度张量 (通常是 torch.ones_like(output))。
5. 构建神经网络 (torch.nn)
torch.nn 模块提供了构建神经网络所需的各种组件。
nn.Module
所有神经网络模块都应该继承 nn.Module 类。它提供了参数管理和前向传播的方法。
“`python
import torch.nn as nn
import torch.nn.functional as F
class MyNeuralNetwork(nn.Module):
def init(self):
super(MyNeuralNetwork, self).init()
# 定义网络层
self.fc1 = nn.Linear(784, 128) # 输入层到隐藏层
self.relu = nn.ReLU() # 激活函数
self.fc2 = nn.Linear(128, 10) # 隐藏层到输出层
def forward(self, x):
# 定义前向传播的计算
x = self.fc1(x)
x = self.relu(x)
x = self.fc2(x)
return x
model = MyNeuralNetwork()
print(model)
“`
层 (Layers)
nn 模块包含了各种预定义的层:
* 线性层 (Linear Layer): nn.Linear(in_features, out_features) 实现 y = xW^T + b。
* 卷积层 (Convolutional Layer): nn.Conv2d(in_channels, out_channels, kernel_size, ...) 用于图像处理。
* 池化层 (Pooling Layer): nn.MaxPool2d(...), nn.AvgPool2d(...) 用于下采样。
* 循环神经网络层 (RNN Layers): nn.RNN, nn.LSTM, nn.GRU 用于序列数据。
* 批归一化层 (Batch Normalization Layer): nn.BatchNorm1d, nn.BatchNorm2d 用于稳定训练。
* Dropout 层: nn.Dropout(...) 用于防止过拟合。
激活函数
激活函数引入了非线性,使得神经网络能够学习复杂的模式。torch.nn.functional 包含了大多数激活函数,也可以直接作为 nn.Module 使用。
* ReLU: nn.ReLU() 或 F.relu()
* Sigmoid: nn.Sigmoid() 或 F.sigmoid()
* Tanh: nn.Tanh() 或 F.tanh()
* Softmax: nn.Softmax(dim=...) 或 F.softmax(dim=...)
损失函数 (Loss Functions)
损失函数衡量模型的预测结果与真实标签之间的差异。
* 分类任务:
* nn.CrossEntropyLoss(): 适用于多类别分类,内部包含了 log_softmax 和 NLLLoss。
* nn.BCELoss(): 适用于二分类问题。
* 回归任务:
* nn.MSELoss() (Mean Squared Error Loss): 均方误差损失。
* nn.L1Loss() (Mean Absolute Error Loss): 平均绝对误差损失。
优化器 (Optimizers)
优化器根据损失函数的梯度来更新模型的权重。
* torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9): 随机梯度下降。
* torch.optim.Adam(model.parameters(), lr=0.001): Adam 优化器。
* torch.optim.RMSprop(model.parameters(), lr=0.001): RMSprop 优化器。
6. 数据加载与预处理 (torch.utils.data)
torch.utils.data 模块提供了高效的数据加载和预处理工具。
Dataset
Dataset 是一个抽象类,代表了一个数据样本的集合。你需要继承它并实现 __len__ 和 __getitem__ 方法。
“`python
from torch.utils.data import Dataset
class CustomDataset(Dataset):
def init(self, data, labels):
self.data = data
self.labels = labels
def __len__(self):
return len(self.labels)
def __getitem__(self, idx):
sample = self.data[idx]
label = self.labels[idx]
return sample, label
示例数据
sample_data = torch.randn(100, 10) # 100个样本,每个样本10个特征
sample_labels = torch.randint(0, 2, (100,)) # 100个二分类标签
custom_dataset = CustomDataset(sample_data, sample_labels)
print(f”Dataset size: {len(custom_dataset)}”)
print(f”First item: {custom_dataset[0]}”)
“`
DataLoader
DataLoader 负责从 Dataset 中按批次 (batch) 加载数据,并提供多线程加载和数据打乱等功能。
“`python
from torch.utils.data import DataLoader
dataloader = DataLoader(custom_dataset, batch_size=4, shuffle=True)
for batch_idx, (data, labels) in enumerate(dataloader):
print(f”Batch {batch_idx}: Data shape {data.shape}, Labels shape {labels.shape}”)
if batch_idx == 2: # 只打印前3个批次
break
“`
数据转换 (Transforms)
torchvision.transforms 模块提供了对图像数据进行预处理的常用操作,例如裁剪、缩放、归一化等。
“`python
from torchvision import datasets, transforms
定义图像转换
transform = transforms.Compose([
transforms.ToTensor(), # 将 PIL Image 或 NumPy array 转换为 Tensor
transforms.Normalize((0.5,), (0.5,)) # 归一化图像数据到 [-1, 1]
])
加载 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)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)
“`
7. 模型训练流程
一个典型的 PyTorch 模型训练流程包括以下步骤:
定义模型
“`python
假设我们定义了一个简单的全连接网络
class SimpleNN(nn.Module):
def init(self, input_size, hidden_size, num_classes):
super(SimpleNN, self).init()
self.fc1 = nn.Linear(input_size, hidden_size)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(hidden_size, num_classes)
def forward(self, x):
out = self.fc1(x)
out = self.relu(out)
out = self.fc2(out)
return out
input_size = 784 # 例如 MNIST 图像展平后的大小
hidden_size = 128
num_classes = 10
model = SimpleNN(input_size, hidden_size, num_classes)
“`
定义损失函数和优化器
python
criterion = nn.CrossEntropyLoss() # 适用于分类问题
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
训练循环
“`python
num_epochs = 5
假设 train_loader 是您的数据加载器
将模型移动到 GPU (如果可用)
device = torch.device(“cuda” if torch.cuda.is_available() else “cpu”)
model.to(device)
模拟一个 train_loader
class DummyDataset(Dataset):
def init(self):
self.data = torch.randn(1000, input_size)
self.labels = torch.randint(0, num_classes, (1000,))
def len(self): return len(self.labels)
def getitem(self, idx): return self.data[idx], self.labels[idx]
dummy_train_dataset = DummyDataset()
dummy_train_loader = DataLoader(dummy_train_dataset, batch_size=64, shuffle=True)
for epoch in range(num_epochs):
for i, (images, labels) in enumerate(dummy_train_loader):
# 将数据移动到 GPU (如果可用)
images = images.reshape(-1, input_size).to(device)
labels = labels.to(device)
# 前向传播
outputs = model(images)
loss = criterion(outputs, labels)
# 反向传播和优化
optimizer.zero_grad() # 清零梯度
loss.backward() # 计算梯度
optimizer.step() # 更新权重
if (i+1) % 100 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{len(dummy_train_loader)}], Loss: {loss.item():.4f}')
“`
模型评估
在训练完成后,您需要评估模型在测试集上的性能。
“`python
假设 test_loader 是您的测试数据加载器
模拟一个 test_loader
dummy_test_dataset = DummyDataset()
dummy_test_loader = DataLoader(dummy_test_dataset, batch_size=64, shuffle=False)
model.eval() # 切换到评估模式 (关闭 Dropout 和 Batch Norm 的训练行为)
with torch.no_grad(): # 在评估模式下不需要计算梯度
correct = 0
total = 0
for images, labels in dummy_test_loader:
images = images.reshape(-1, input_size).to(device)
labels = labels.to(device)
outputs = model(images)
_, predicted = torch.max(outputs.data, 1) # 获取预测类别
total += labels.size(0)
correct += (predicted == labels).sum().item()
print(f'Accuracy of the model on the test images: {100 * correct / total:.2f}%')
“`
模型保存与加载
“`python
保存模型
torch.save(model.state_dict(), ‘model.pth’)
加载模型
loaded_model = SimpleNN(input_size, hidden_size, num_classes) # 必须先实例化模型
loaded_model.load_state_dict(torch.load(‘model.pth’))
loaded_model.to(device) # 确保模型在正确的设备上
loaded_model.eval() # 加载后通常切换到评估模式
“`
8. 实践案例:图像分类 (CNN)
我们将使用 PyTorch 和卷积神经网络 (CNN) 来对 CIFAR-10 数据集进行图像分类。
数据准备
“`python
import torchvision
import torchvision.transforms as transforms
数据预处理
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) # 对R,G,B三个通道进行归一化
])
加载 CIFAR-10 训练集
trainset = torchvision.datasets.CIFAR10(root=’./data’, train=True,
download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
shuffle=True, num_workers=2)
加载 CIFAR-10 测试集
testset = torchvision.datasets.CIFAR10(root=’./data’, train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
shuffle=False, num_workers=2)
classes = (‘plane’, ‘car’, ‘bird’, ‘cat’,
‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’)
“`
构建 CNN 模型
“`python
class Net(nn.Module):
def init(self):
super(Net, self).init()
self.conv1 = nn.Conv2d(3, 6, 5) # 输入通道3 (RGB), 输出通道6, 卷积核大小5×5
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16 * 5 * 5, 120) # 展平后的输入大小
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = torch.flatten(x, 1) # 展平除 batch 维度外的所有维度
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
net = Net()
“`
训练与评估
“`python
import torch.optim as optim
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
device = torch.device(“cuda:0” if torch.cuda.is_available() else “cpu”)
net.to(device)
print(“Starting training…”)
for epoch in range(2): # 训练 2 个 epoch
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
inputs, labels = data[0].to(device), data[1].to(device)
optimizer.zero_grad()
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
if i % 2000 == 1999: # 每 2000 个 mini-batch 打印一次
print(f'[{epoch + 1}, {i + 1:5d}] loss: {running_loss / 2000:.3f}')
running_loss = 0.0
print(‘Finished Training’)
保存模型
PATH = ‘./cifar_net.pth’
torch.save(net.state_dict(), PATH)
评估模型
net = Net() # 重新实例化模型
net.load_state_dict(torch.load(PATH))
net.to(device)
correct = 0
total = 0
with torch.no_grad():
for data in testloader:
images, labels = data[0].to(device), data[1].to(device)
outputs = net(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print(f’Accuracy of the network on the 10000 test images: {100 * correct / total:.2f}%’)
“`
9. 总结与展望
本教程涵盖了 PyTorch 的核心概念,从张量操作到自动微分,再到构建和训练神经网络。通过 CIFAR-10 图像分类的实践案例,您应该对如何使用 PyTorch 解决实际问题有了初步的了解。
PyTorch 社区活跃,生态系统丰富。您可以进一步学习以下内容:
* 迁移学习: 利用预训练模型进行快速开发。
* GANs, Transformers, Reinforcement Learning: 探索更高级的深度学习模型和应用。
* PyTorch Lightning, Hugging Face Transformers: 使用更高级别的库来简化开发流程。
* PyTorch Profiler: 优化模型性能。
希望本教程能为您在 PyTorch 的学习旅程中提供坚实的基础。祝您在深度学习的世界中取得更多成就!