PyTorch实战:从零构建CNN模型,精准识别MNIST手写数字
1. 环境准备与数据加载第一次用PyTorch构建CNN模型时我对着空白的代码编辑器发呆了半小时——到底该从哪里开始后来发现其实只需要三行代码就能搭建好基础环境。先安装PyTorch建议直接用conda安装然后导入这几个核心模块import torch import torch.nn as nn from torchvision import datasets, transformsMNIST数据集就像深度学习界的Hello World包含6万张28x28像素的手写数字图片。PyTorch很贴心地内置了自动下载功能transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,)) # 将像素值归一化到[-1,1]范围 ]) train_data datasets.MNIST(../data, trainTrue, downloadTrue, transformtransform) test_data datasets.MNIST(../data, trainFalse, transformtransform)这里有个新手容易踩的坑忘记做数据归一化。我最初训练时准确率卡在90%上不去后来发现是因为像素值范围在0-255之间导致梯度更新不稳定。加上Normalize后效果立竿见影数据加载器(DataLoader)是另一个神器它能自动处理分批和打乱顺序train_loader torch.utils.data.DataLoader(train_data, batch_size128, shuffleTrue) test_loader torch.utils.data.DataLoader(test_data, batch_size128)提示batch_size设置很有讲究太小会导致训练不稳定太大又影响梯度更新效果。经过多次测试128是个比较稳妥的值2. 构建CNN模型架构CNN模型就像搭积木PyTorch提供了各种现成的模块。我们先定义一个包含两个卷积层的简单网络class CNN(nn.Module): def __init__(self): super(CNN, self).__init__() self.conv1 nn.Sequential( nn.Conv2d(1, 16, 3, 1, 1), # 输入通道1输出通道163x3卷积核 nn.ReLU(), nn.MaxPool2d(2) # 2x2最大池化 ) self.conv2 nn.Sequential( nn.Conv2d(16, 32, 3, 1, 1), nn.ReLU(), nn.MaxPool2d(2) ) self.fc nn.Linear(32*7*7, 10) # 全连接层输出10类这里有几个关键参数需要理解卷积核大小3x3是最常用的尺寸能捕捉局部特征又不会太耗计算资源Padding1保证输出尺寸不变计算公式(W-F2P)/S 1池化层用2x2窗口相当于降采样保留主要特征前向传播就像组装流水线def forward(self, x): x self.conv1(x) # [128,1,28,28] - [128,16,14,14] x self.conv2(x) # - [128,32,7,7] x x.view(x.size(0), -1) # 展平 return self.fc(x)第一次写forward函数时我经常搞错张量维度。后来养成了用print(x.shape)调试的好习惯建议你也试试3. 模型训练与调优训练循环是深度学习的核心引擎这里我总结了一个万能模板model CNN() criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.parameters(), lr0.001) for epoch in range(10): for images, labels in train_loader: outputs model(images) loss criterion(outputs, labels) optimizer.zero_grad() loss.backward() optimizer.step()Adam优化器对新手特别友好不像SGD那样需要手动调整学习率。但要注意如果训练集准确率上不去但测试集准确率很高可能是学习率设太大了。我在训练过程中发现几个提升准确率的关键点加入Dropout层防止过拟合在fc层前加nn.Dropout(0.5)使用学习率衰减scheduler torch.optim.lr_scheduler.StepLR(optimizer, step_size5, gamma0.1)增加epoch到20轮时测试准确率能达到98.5%可视化训练过程也很重要可以及时发现异常plt.plot(train_losses, labelTraining loss) plt.plot(val_losses, labelValidation loss) plt.legend()4. 模型评估与实战技巧训练完成后我们需要全面评估模型表现correct 0 total 0 with torch.no_grad(): for images, labels in test_loader: outputs model(images) _, predicted torch.max(outputs.data, 1) total labels.size(0) correct (predicted labels).sum().item() print(f测试准确率: {100 * correct / total}%)在实际项目中我还总结出这些实用技巧使用torch.save(model.state_dict(), model.pth)保存训练好的模型加载模型时先实例化网络结构再model.load_state_dict(torch.load(model.pth))对于GPU训练记得用model.to(cuda)和data.to(cuda)使用torchsummary库可以打印网络结构和参数数量最后分享一个调试秘籍当模型表现异常时先检查数据加载是否正确。我遇到过因为transform顺序错误导致准确率只有10%的情况后来用matplotlib可视化输入数据才发现问题。