PyTorch 教程:从基础到实践的完整指南 – wiki词典

PyTorch 教程:从基础到实践的完整指南

PyTorch 是一个开源的机器学习库,广泛应用于深度学习领域。它以其灵活性、易用性和强大的功能而受到研究人员和开发者的青睐。本教程将带您从 PyTorch 的基础知识开始,逐步深入到实际应用,帮助您掌握使用 PyTorch 构建和训练神经网络的技能。

目录

  1. PyTorch 简介
  2. 安装 PyTorch
  3. PyTorch 基础:张量 (Tensors)
    • 创建张量
    • 张量操作
    • 张量与 NumPy 转换
    • GPU 加速
  4. 自动微分 (Autograd)
    • requires_grad 属性
    • 计算梯度
  5. 构建神经网络 (torch.nn)
    • nn.Module
    • 层 (Layers)
    • 激活函数
    • 损失函数
    • 优化器
  6. 数据加载与预处理 (torch.utils.data)
    • Dataset
    • DataLoader
    • 数据转换 (Transforms)
  7. 模型训练流程
    • 定义模型
    • 定义损失函数和优化器
    • 训练循环
    • 模型评估
    • 模型保存与加载
  8. 实践案例:图像分类 (CNN)
    • 数据准备
    • 构建 CNN 模型
    • 训练与评估
  9. 总结与展望

1. PyTorch 简介

PyTorch 最初由 Facebook (现在是 Meta) 的人工智能研究实验室开发。它提供了两个主要功能:
* 一个强大的 N 维数组 (张量) 库,支持 GPU 加速计算。
* 一个基于自动微分的深度学习框架,用于构建和训练神经网络。

PyTorch 的设计理念是“define-by-run”,即计算图在运行时动态构建。这使得 PyTorch 在调试和研究方面非常灵活。

2. 安装 PyTorch

安装 PyTorch 非常简单。推荐使用 condapip。请访问 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_softmaxNLLLoss
* 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 的学习旅程中提供坚实的基础。祝您在深度学习的世界中取得更多成就!

滚动至顶部