从零开始轻松搭建你的第一个PyTorch项目实战教程包含环境配置模型训练与评估完整流程让你快速掌握深度学习核心技能无需编程基础也能轻松上手
引言
PyTorch是当前最受欢迎的深度学习框架之一,它以其灵活性、易用性和强大的功能而闻名。本教程将带你从零开始,一步步构建你的第一个PyTorch项目,即使你没有编程基础也能轻松上手。我们将涵盖环境配置、数据处理、模型构建、训练与评估的完整流程,帮助你快速掌握深度学习的核心技能。
1. 环境配置
1.1 安装Python
首先,我们需要安装Python,它是PyTorch的基础。推荐使用Python 3.8或更高版本。
- 访问Python官网
- 下载适合你操作系统的Python安装包
- 运行安装程序,记得勾选”Add Python to PATH”选项
安装完成后,打开命令提示符或终端,输入以下命令验证安装:
python --version
1.2 安装PyTorch
PyTorch的安装取决于你的操作系统和是否拥有NVIDIA GPU。我们将介绍最常用的安装方法。
- 访问PyTorch官网
- 根据你的系统配置选择合适的安装命令
对于Windows用户,如果没有NVIDIA GPU,可以使用CPU版本的PyTorch:
pip install torch torchvision torchaudio
如果有NVIDIA GPU并安装了CUDA,请选择对应CUDA版本的安装命令。例如,对于CUDA 11.8:
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
对于macOS用户:
pip install torch torchvision torchaudio
对于Linux用户,根据是否支持CUDA选择相应的命令。
1.3 安装其他必要库
除了PyTorch,我们还需要一些其他库来辅助我们的深度学习项目:
pip install numpy matplotlib scikit-learn jupyter
1.4 设置开发环境
对于初学者,推荐使用Jupyter Notebook作为开发环境,它提供了交互式的编程体验。
安装完成后,通过以下命令启动Jupyter Notebook:
jupyter notebook
这将在你的默认浏览器中打开Jupyter Notebook界面。
2. PyTorch基础
2.1 张量(Tensor)简介
在PyTorch中,张量(Tensor)是最基本的数据结构,类似于NumPy的ndarray,但可以在GPU上加速计算。
让我们创建一些基本的张量:
import torch # 创建一个未初始化的5x3矩阵 x = torch.empty(5, 3) print(x) # 创建一个随机初始化的5x3矩阵 x = torch.rand(5, 3) print(x) # 创建一个全零矩阵,数据类型为long x = torch.zeros(5, 3, dtype=torch.long) print(x) # 直接从数据创建张量 x = torch.tensor([5.5, 3]) print(x) # 基于现有张量创建新张量 x = x.new_ones(5, 3, dtype=torch.double) # new_* 方法会复用输入张量的属性,如dtype print(x) x = torch.randn_like(x, dtype=torch.float) # 覆盖dtype print(x)
2.2 张量操作
张量支持多种操作,包括加法、切片、调整形状等:
# 加法操作 y = torch.rand(5, 3) print(x + y) # 或者 print(torch.add(x, y)) # 切片操作 print(x[:, 1]) # 所有行的第二列 # 调整形状 x = torch.randn(4, 4) y = x.view(16) # 转换为1D张量 z = x.view(-1, 8) # 转换为2x8的张量,-1表示自动计算该维度 print(x.size(), y.size(), z.size())
2.3 自动梯度(Autograd)
PyTorch的核心特性之一是自动梯度,它为张量操作提供了自动微分功能。这对训练神经网络至关重要。
# 创建一个张量并设置requires_grad=True来跟踪其计算历史 x = torch.ones(2, 2, requires_grad=True) print(x) # 对张量进行操作 y = x + 2 print(y) # y是操作的结果,所以它有grad_fn属性 print(y.grad_fn) # 对y进行更多操作 z = y * y * 3 out = z.mean() print(z, out) # 反向传播 out.backward() # 计算梯度 d(out)/dx print(x.grad)
3. 数据准备
3.1 加载数据
在深度学习中,数据准备是至关重要的一步。PyTorch提供了torch.utils.data.DataLoader
和torch.utils.data.Dataset
来简化数据加载过程。
让我们以MNIST数据集为例,展示如何加载数据:
import torch from torchvision import datasets, transforms # 定义数据转换 transform = transforms.Compose([ transforms.ToTensor(), # 将图像转换为PyTorch张量 transforms.Normalize((0.5,), (0.5,)) # 标准化,均值和标准差都为0.5 ]) # 下载并加载训练数据 train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform) # 下载并加载测试数据 test_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform) # 创建数据加载器 batch_size = 64 train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True) test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
3.2 自定义数据集
如果你想使用自己的数据,可以创建一个自定义的Dataset
类:
from torch.utils.data import Dataset, DataLoader import pandas as pd from PIL import Image import os class CustomDataset(Dataset): def __init__(self, csv_file, root_dir, transform=None): """ 参数: csv_file (string): 带注释的csv文件的路径 root_dir (string): 包含所有图像的目录 transform (callable, optional): 应用于样本的可选转换 """ self.annotations = pd.read_csv(csv_file) self.root_dir = root_dir self.transform = transform def __len__(self): return len(self.annotations) def __getitem__(self, idx): if torch.is_tensor(idx): idx = idx.tolist() img_name = os.path.join(self.root_dir, self.annotations.iloc[idx, 0]) image = Image.open(img_name) label = self.annotations.iloc[idx, 1] if self.transform: image = self.transform(image) return image, label # 使用自定义数据集 transform = transforms.Compose([ transforms.Resize((256, 256)), transforms.ToTensor(), ]) custom_dataset = CustomDataset(csv_file='labels.csv', root_dir='images/', transform=transform) data_loader = DataLoader(custom_dataset, batch_size=4, shuffle=True)
3.3 数据预处理与增强
数据预处理和增强可以提高模型的泛化能力。PyTorch的torchvision.transforms
模块提供了多种图像转换方法:
from torchvision import transforms # 定义数据增强和预处理 transform_train = transforms.Compose([ transforms.RandomCrop(32, padding=4), # 随机裁剪 transforms.RandomHorizontalFlip(), # 随机水平翻转 transforms.ToTensor(), # 转换为张量 transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) # 标准化 ]) transform_test = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) ]) # 应用到数据集 train_dataset = datasets.CIFAR10(root='./data', train=True, download=True, transform=transform_train) test_dataset = datasets.CIFAR10(root='./data', train=False, download=True, transform=transform_test)
4. 构建模型
4.1 定义神经网络
在PyTorch中,我们通常通过继承torch.nn.Module
来定义神经网络:
import torch.nn as nn import torch.nn.functional as F class SimpleNet(nn.Module): def __init__(self): super(SimpleNet, self).__init__() # 定义卷积层 self.conv1 = nn.Conv2d(1, 32, 3, 1) # 输入通道1,输出通道32,卷积核大小3x3,步长1 self.conv2 = nn.Conv2d(32, 64, 3, 1) # 定义全连接层 self.fc1 = nn.Linear(9216, 128) # 输入特征9216,输出特征128 self.fc2 = nn.Linear(128, 10) # 输出10个类别 def forward(self, x): # 前向传播过程 x = F.relu(self.conv1(x)) # 卷积 -> ReLU激活 x = F.max_pool2d(x, 2) # 最大池化 x = F.relu(self.conv2(x)) # 卷积 -> ReLU激活 x = F.max_pool2d(x, 2) # 最大池化 x = torch.flatten(x, 1) # 展平张量 x = F.relu(self.fc1(x)) # 全连接 -> ReLU激活 x = self.fc2(x) # 全连接 output = F.log_softmax(x, dim=1) # 输出层使用log_softmax return output # 创建模型实例 model = SimpleNet() print(model)
4.2 使用预训练模型
PyTorch提供了许多预训练模型,可以用于迁移学习:
import torchvision.models as models # 加载预训练的ResNet模型 resnet = models.resnet18(pretrained=True) # 如果需要修改最后一层以适应你的任务 num_ftrs = resnet.fc.in_features resnet.fc = nn.Linear(num_ftrs, 10) # 假设有10个类别 print(resnet)
4.3 模型参数管理
我们可以查看和操作模型的参数:
# 打印模型参数 for name, param in model.named_parameters(): print(f"Layer: {name} | Size: {param.size()} | Values: {param[:2]} n") # 冻结某些层的参数(在迁移学习中常用) for param in resnet.parameters(): param.requires_grad = False # 只解冻最后一层 for param in resnet.fc.parameters(): param.requires_grad = True
5. 训练模型
5.1 定义损失函数和优化器
在训练模型之前,我们需要定义损失函数和优化器:
import torch.optim as optim # 定义损失函数 criterion = nn.CrossEntropyLoss() # 定义优化器 optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9) # 或者使用Adam优化器 optimizer = optim.Adam(model.parameters(), lr=0.001)
5.2 训练循环
下面是一个完整的训练循环示例:
def train(model, device, train_loader, optimizer, epoch, log_interval=100): model.train() for batch_idx, (data, target) in enumerate(train_loader): # 将数据移至设备(CPU或GPU) data, target = data.to(device), target.to(device) # 清零梯度 optimizer.zero_grad() # 前向传播 output = model(data) # 计算损失 loss = criterion(output, target) # 反向传播 loss.backward() # 更新参数 optimizer.step() # 打印训练信息 if batch_idx % log_interval == 0: print(f'Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)} ' f'({100. * batch_idx / len(train_loader):.0f}%)]tLoss: {loss.item():.6f}') # 设置设备 device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model = model.to(device) # 训练模型 num_epochs = 10 for epoch in range(1, num_epochs + 1): train(model, device, train_loader, optimizer, epoch)
5.3 保存和加载模型
在训练过程中,我们可能需要保存模型以便以后使用:
# 保存整个模型 torch.save(model, 'model.pth') # 或者只保存模型参数(推荐) torch.save(model.state_dict(), 'model_params.pth') # 加载模型 model = SimpleNet() # 首先创建模型实例 model.load_state_dict(torch.load('model_params.pth')) model = model.to(device) # 如果保存了整个模型 model = torch.load('model.pth') model = model.to(device)
5.4 训练过程中的验证
为了监控训练过程并防止过拟合,我们可以在训练过程中进行验证:
def validate(model, device, test_loader): model.eval() test_loss = 0 correct = 0 # 不计算梯度 with torch.no_grad(): for data, target in test_loader: data, target = data.to(device), target.to(device) output = model(data) test_loss += criterion(output, target).item() # 累加批次损失 pred = output.argmax(dim=1, keepdim=True) # 获取最大概率的索引 correct += pred.eq(target.view_as(pred)).sum().item() # 计算正确预测的数量 test_loss /= len(test_loader.dataset) accuracy = 100. * correct / len(test_loader.dataset) print(f'Test set: Average loss: {test_loss:.4f}, ' f'Accuracy: {correct}/{len(test_loader.dataset)} ({accuracy:.2f}%)') return test_loss, accuracy # 修改训练循环,加入验证 best_accuracy = 0.0 for epoch in range(1, num_epochs + 1): train(model, device, train_loader, optimizer, epoch) test_loss, accuracy = validate(model, device, test_loader) # 保存最佳模型 if accuracy > best_accuracy: best_accuracy = accuracy torch.save(model.state_dict(), 'best_model.pth') print(f"Model saved with accuracy: {best_accuracy:.2f}%")
6. 评估模型
6.1 模型评估指标
除了准确率,我们还可以使用其他指标来评估模型:
from sklearn.metrics import confusion_matrix, classification_report import numpy as np def evaluate_model(model, device, test_loader): model.eval() all_preds = [] all_targets = [] with torch.no_grad(): for data, target in test_loader: data, target = data.to(device), target.to(device) output = model(data) pred = output.argmax(dim=1) all_preds.extend(pred.cpu().numpy()) all_targets.extend(target.cpu().numpy()) # 计算混淆矩阵 cm = confusion_matrix(all_targets, all_preds) print("Confusion Matrix:") print(cm) # 计算分类报告 print("nClassification Report:") print(classification_report(all_targets, all_preds)) return all_preds, all_targets # 评估模型 predictions, targets = evaluate_model(model, device, test_loader)
6.2 可视化结果
可视化可以帮助我们更好地理解模型的性能:
import matplotlib.pyplot as plt import seaborn as sns def plot_confusion_matrix(cm, classes): plt.figure(figsize=(10, 8)) sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=classes, yticklabels=classes) plt.xlabel('Predicted') plt.ylabel('Actual') plt.title('Confusion Matrix') plt.show() def plot_predictions(model, device, test_loader, classes, num_samples=10): model.eval() # 获取一批数据 dataiter = iter(test_loader) images, labels = next(dataiter) # 选择指定数量的样本 images = images[:num_samples] labels = labels[:num_samples] # 预测 with torch.no_grad(): images_gpu = images.to(device) outputs = model(images_gpu) _, predicted = torch.max(outputs, 1) # 显示图像和预测 fig = plt.figure(figsize=(15, 4)) for idx in range(num_samples): ax = fig.add_subplot(1, num_samples, idx+1, xticks=[], yticks=[]) # 将张量转换为图像 img = images[idx].squeeze().numpy() if img.shape[0] == 1: # 灰度图像 plt.imshow(img, cmap='gray') else: # 彩色图像 plt.imshow(np.transpose(img, (1, 2, 0))) # 设置标题为预测和真实标签 title = f"Pred: {classes[predicted[idx]]}nTrue: {classes[labels[idx]]}" ax.set_title(title, color=("green" if predicted[idx]==labels[idx] else "red")) plt.tight_layout() plt.show() # 假设我们有类别名称 classes = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] # 计算并绘制混淆矩阵 cm = confusion_matrix(targets, predictions) plot_confusion_matrix(cm, classes) # 绘制预测结果 plot_predictions(model, device, test_loader, classes)
7. 完整项目示例
让我们将前面学到的所有内容整合起来,创建一个完整的MNIST手写数字识别项目:
import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pyplot as plt import numpy as np from sklearn.metrics import confusion_matrix, classification_report import seaborn as sns # 1. 设置随机种子以确保可重复性 torch.manual_seed(42) # 2. 定义超参数 batch_size = 64 test_batch_size = 1000 epochs = 10 lr = 0.01 momentum = 0.5 log_interval = 100 # 3. 检查是否有可用的GPU device = torch.device("cuda" if torch.cuda.is_available() else "cpu") print(f"Using device: {device}") # 4. 定义数据转换 transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) # 5. 加载数据集 train_dataset = datasets.MNIST('./data', train=True, download=True, transform=transform) test_dataset = datasets.MNIST('./data', train=False, transform=transform) train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True) test_loader = DataLoader(test_dataset, batch_size=test_batch_size, shuffle=False) # 6. 定义神经网络 class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1) self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1) self.dropout1 = nn.Dropout2d(0.25) self.dropout2 = nn.Dropout2d(0.5) self.fc1 = nn.Linear(9216, 128) self.fc2 = nn.Linear(128, 10) def forward(self, x): x = F.relu(self.conv1(x)) x = F.relu(self.conv2(x)) x = F.max_pool2d(x, 2) x = self.dropout1(x) x = torch.flatten(x, 1) x = F.relu(self.fc1(x)) x = self.dropout2(x) x = self.fc2(x) output = F.log_softmax(x, dim=1) return output # 7. 初始化模型和优化器 model = Net().to(device) optimizer = optim.SGD(model.parameters(), lr=lr, momentum=momentum) # 8. 定义训练函数 def train(model, device, train_loader, optimizer, epoch): model.train() train_loss = 0 correct = 0 for batch_idx, (data, target) in enumerate(train_loader): data, target = data.to(device), target.to(device) optimizer.zero_grad() output = model(data) loss = F.nll_loss(output, target) train_loss += loss.item() loss.backward() optimizer.step() # 计算准确率 pred = output.argmax(dim=1, keepdim=True) correct += pred.eq(target.view_as(pred)).sum().item() if batch_idx % log_interval == 0: print(f'Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)} ' f'({100. * batch_idx / len(train_loader):.0f}%)]tLoss: {loss.item():.6f}') train_loss /= len(train_loader) accuracy = 100. * correct / len(train_loader.dataset) print(f'nTrain set: Average loss: {train_loss:.4f}, Accuracy: {correct}/{len(train_loader.dataset)} ({accuracy:.2f}%)') return train_loss, accuracy # 9. 定义测试函数 def test(model, device, test_loader): model.eval() test_loss = 0 correct = 0 all_preds = [] all_targets = [] with torch.no_grad(): for data, target in test_loader: data, target = data.to(device), target.to(device) output = model(data) test_loss += F.nll_loss(output, target, reduction='sum').item() pred = output.argmax(dim=1, keepdim=True) correct += pred.eq(target.view_as(pred)).sum().item() # 收集预测和真实标签 all_preds.extend(pred.cpu().numpy()) all_targets.extend(target.cpu().numpy()) test_loss /= len(test_loader.dataset) accuracy = 100. * correct / len(test_loader.dataset) print(f'Test set: Average loss: {test_loss:.4f}, Accuracy: {correct}/{len(test_loader.dataset)} ({accuracy:.2f}%)') return test_loss, accuracy, all_preds, all_targets # 10. 训练和测试模型 train_losses = [] train_accuracies = [] test_losses = [] test_accuracies = [] best_accuracy = 0.0 for epoch in range(1, epochs + 1): print(f"nEpoch {epoch}/{epochs}") print("-" * 50) train_loss, train_accuracy = train(model, device, train_loader, optimizer, epoch) test_loss, test_accuracy, _, _ = test(model, device, test_loader) train_losses.append(train_loss) train_accuracies.append(train_accuracy) test_losses.append(test_loss) test_accuracies.append(test_accuracy) # 保存最佳模型 if test_accuracy > best_accuracy: best_accuracy = test_accuracy torch.save(model.state_dict(), "mnist_cnn.pt") print(f"Model saved with accuracy: {best_accuracy:.2f}%") # 11. 加载最佳模型进行最终评估 model.load_state_dict(torch.load("mnist_cnn.pt")) test_loss, test_accuracy, all_preds, all_targets = test(model, device, test_loader) # 12. 可视化训练过程 plt.figure(figsize=(12, 4)) plt.subplot(1, 2, 1) plt.plot(range(1, epochs + 1), train_losses, label='Train Loss') plt.plot(range(1, epochs + 1), test_losses, label='Test Loss') plt.xlabel('Epochs') plt.ylabel('Loss') plt.title('Training and Test Loss') plt.legend() plt.subplot(1, 2, 2) plt.plot(range(1, epochs + 1), train_accuracies, label='Train Accuracy') plt.plot(range(1, epochs + 1), test_accuracies, label='Test Accuracy') plt.xlabel('Epochs') plt.ylabel('Accuracy (%)') plt.title('Training and Test Accuracy') plt.legend() plt.tight_layout() plt.show() # 13. 绘制混淆矩阵 cm = confusion_matrix(all_targets, all_preds) plt.figure(figsize=(10, 8)) sns.heatmap(cm, annot=True, fmt='d', cmap='Blues') plt.xlabel('Predicted') plt.ylabel('Actual') plt.title('Confusion Matrix') plt.show() # 14. 打印分类报告 print("nClassification Report:") print(classification_report(all_targets, all_preds)) # 15. 可视化一些预测结果 def plot_predictions(model, device, test_loader, num_samples=10): model.eval() # 获取一批数据 dataiter = iter(test_loader) images, labels = next(dataiter) # 选择指定数量的样本 images = images[:num_samples] labels = labels[:num_samples] # 预测 with torch.no_grad(): images_gpu = images.to(device) outputs = model(images_gpu) _, predicted = torch.max(outputs, 1) # 显示图像和预测 fig = plt.figure(figsize=(15, 4)) for idx in range(num_samples): ax = fig.add_subplot(1, num_samples, idx+1, xticks=[], yticks=[]) # 将张量转换为图像 img = images[idx].squeeze().numpy() plt.imshow(img, cmap='gray') # 设置标题为预测和真实标签 title = f"Pred: {predicted[idx]}nTrue: {labels[idx]}" ax.set_title(title, color=("green" if predicted[idx]==labels[idx] else "red")) plt.tight_layout() plt.show() # 绘制预测结果 plot_predictions(model, device, test_loader)
8. 总结与下一步
8.1 教程总结
在本教程中,我们从零开始学习了如何构建一个完整的PyTorch项目,包括:
- 环境配置:安装Python、PyTorch和其他必要的库
- PyTorch基础:学习张量操作和自动梯度
- 数据准备:加载和预处理数据,包括使用自定义数据集
- 模型构建:定义神经网络,包括使用预训练模型
- 模型训练:实现训练循环,包括验证和模型保存
- 模型评估:使用各种指标评估模型性能,并可视化结果
- 完整项目:整合所有知识,构建一个MNIST手写数字识别系统
8.2 下一步学习方向
如果你已经掌握了本教程的内容,以下是一些进一步学习的方向:
- 更复杂的模型架构:尝试实现更复杂的模型,如ResNet、Inception、Transformer等
- 不同的任务类型:探索目标检测、语义分割、生成模型等任务
- 高级优化技术:学习学习率调度、梯度裁剪、正则化等技术
- 分布式训练:了解如何在多GPU或多台机器上训练模型
- 模型部署:学习如何将训练好的模型部署到生产环境
- PyTorch生态系统:探索PyTorch Lightning、FastAI等高级库
8.3 学习资源推荐
以下是一些推荐的学习资源:
- PyTorch官方教程
- Deep Learning with PyTorch: A 60 Minute Blitz
- PyTorch官方文档
- PyTorch示例代码
- Fast.ai课程
通过本教程的学习,你已经掌握了PyTorch的核心技能,并能够独立构建深度学习项目。继续实践和探索,你将在深度学习领域取得更大的进步!