深度学习入门:PyTorch 环境搭建与 MNIST 手写数字识别
前言:本文是“深度学习入门”系列的第二篇。上一篇我们学习了深度学习的基本概念,包括神经网络结构、激活函数、损失函数、梯度下降和反向传播。本篇我们将正式进入代码实战,从 PyTorch 的环境搭建开始,通过 MNIST 手写数字识别任务,完整实现一个多层感知机(MLP)的神经网络,将理论知识付诸实践。
目录
- 一、PyTorch 简介
- 二、PyTorch 环境搭建
- 三、MNIST 数据集
- 四、PyTorch 基础操作
- 五、构建神经网络模型
- 六、训练与测试
- 七、运行结果
- 八、总结
一、PyTorch 简介
1.1 什么是 PyTorch?
PyTorch 是一个开源的深度学习框架,由 Meta(原 Facebook)的人工智能研究团队开发。它基于 Torch 库,使用 Python 作为前端语言,提供了灵活、高效的深度学习开发环境。
1.2 为什么选择 PyTorch?
| 优点 | 说明 |
|---|---|
| 动态计算图 | 代码即模型,调试方便,易于理解 |
| Python 风格 | 与 NumPy 类似,上手容易 |
| 强大的 GPU 加速 | 支持 CUDA 和 Apple Silicon(MPS) |
| 丰富的生态系统 | Torchvision、Torchaudio、Hugging Face 等 |
| 学术与研究首选 | 大多数顶会论文使用 PyTorch 实现 |
二、PyTorch 环境搭建
2.1 安装 Anaconda
Anaconda 是一个开源的 Python 发行版,用于科学计算和深度学习,建议使用它来管理 Python 环境。
2.2 创建虚拟环境
# 创建名为 dl (自己取名)的虚拟环境,指定 Python 版本为 3.11conda create-ndlpython=3.11# 激活虚拟环境conda activate dl2.3 安装依赖包
安装 PyTorch(指定版本):
# 安装 PyTorch 2.1.0 CPU 版本pipinstalltorch==2.1.0torchvision==0.16.0torchaudio==2.1.0# 或指定 CUDA 版本(如 CUDA 11.8)pipinstalltorch==2.1.0torchvision==0.16.0torchaudio==2.1.0 --index-url https://download.pytorch.org/whl/cu118说明:本系列统一使用 PyTorch 2.1.0 版本,配套关系如下:
| PyTorch | Torchvision | Torchaudio |
|---|---|---|
| 2.1.0 | 0.16.0 | 2.1.0 |
安装 NumPy(保持 1.x 版本):
pipinstall"numpy<2.0"注意:NumPy 2.0 版本引入了大量 API 变更,与 PyTorch 2.1.0 存在兼容性问题,2.3版本以上支持,但audio在Windows没有2.3以上的。必须安装 NumPy 1.x 版本,否则运行时会报错。使用
"numpy<2.0"可锁定最新 1.x 版本。
2.4 验证安装
pythonimporttorchimporttorchvisionimporttorchaudioimportnumpyasnpprint(f"PyTorch:{torch.__version__}")print(f"Torchvision:{torchvision.__version__}")print(f"Torchaudio:{torchaudio.__version__}")print(f"NumPy:{np.__version__}")print(f"CUDA 可用:{torch.cuda.is_available()}")输出示例: PyTorch: 2.1.0+cpu Torchvision: 0.16.0+cpu Torchaudio: 2.1.0+cpu NumPy: 1.26.4 CUDA 可用: False三、MNIST 数据集
3.1 什么是 MNIST?
MNIST(Modified National Institute of Standards and Technology)是机器学习领域最经典的手写数字数据集。它包含 70,000 张手写数字图像:
- 60,000 张用于训练
- 10,000 张用于测试
数据集特点:
| 特点 | 说明 |
|---|---|
| 图像尺寸 | 28 × 28 像素 |
| 颜色通道 | 灰度(单通道) |
| 像素范围 | 0 ~ 255(归一化后为 0 ~ 1) |
| 类别数量 | 10(数字 0-9) |
3.2 下载与加载数据
importtorchimporttorchvisionfromtorchvisionimportdatasetsfromtorchvision.transformsimportToTensorimportmatplotlib.pyplotasplt# 下载训练数据集training_data=datasets.MNIST(root='data',# 数据存储目录train=True,# 加载训练集download=True,# 自动下载transform=ToTensor()# 转换为张量并归一化到 [0,1])# 下载测试数据集test_data=datasets.MNIST(root='data',train=False,# 加载测试集download=True,transform=ToTensor())print(f"训练集大小:{len(training_data)}")print(f"测试集大小:{len(test_data)}")输出示例: 训练集大小: 60000 测试集大小: 100003.3 可视化展示
# 显示训练集中前 9 张图片及其标签figure=plt.figure(figsize=(8,8))foriinrange(9):img,label=training_data[i]figure.add_subplot(3,3,i+1)plt.title(label)plt.axis('off')plt.imshow(img.squeeze(),cmap='gray')plt.show()3.4 DataLoader 数据加载器
DataLoader 用于将数据集分批加载,减少内存压力,提高训练速度。
fromtorch.utils.dataimportDataLoader# 创建数据加载器,每批 64 个样本train_dataloader=DataLoader(training_data,batch_size=64,shuffle=True)test_dataloader=DataLoader(test_data,batch_size=64,shuffle=False)# 检查一批数据的形状forX,yintest_dataloader:print(f"Shape of X [N, C, H, W]:{X.shape}")print(f"Shape of y:{y.shape}{y.dtype}")break输出示例: Shape of X [N, C, H, W]: torch.Size([64, 1, 28, 28]) Shape of y: torch.Size([64]) torch.int64四、PyTorch 基础操作
4.1 张量(Tensor)
张量是 PyTorch 的基本数据结构,类似于 NumPy 的 ndarray,但支持 GPU 加速。
# 创建张量x=torch.tensor([1,2,3])print(x)# 零张量zeros=torch.zeros(2,3)# 随机张量random=torch.rand(2,3)4.2 设备(Device)
PyTorch 支持在不同设备上运行:
# 自动选择设备device="cuda"iftorch.cuda.is_available()else"mps"iftorch.backends.mps.is_available()else"cpu"print(f"Using{device}device")# 将张量移动到设备x=torch.tensor([1,2,3]).to(device)4.3 自动求导(Autograd)
PyTorch 的自动求导功能可以自动计算梯度,是反向传播的基础。
# 创建需要梯度的张量w=torch.tensor([2.0],requires_grad=True)x=torch.tensor([3.0])# 前向计算y=w*x# 反向传播y.backward()# 查看梯度print(w.grad)# 输出: tensor([3.])五、构建神经网络模型
5.1 模型架构
本案例使用一个三层全连接神经网络(多层感知机 MLP):
输入层: 784 个节点 (28x28 展平) ↓ 隐藏层1: 128 个节点 + Sigmoid 激活函数 ↓ 隐藏层2: 256 个节点 + Sigmoid 激活函数 ↓ 输出层: 10 个节点 (对应数字 0-9)5.2 完整代码
importtorchfromtorchimportnn# 定义神经网络模型classNeuralNetwork(nn.Module):def__init__(self):super().__init__()# 展平层: 将 28x28 的图像展平为 784 维向量self.flatten=nn.Flatten()# 全连接层(线性层)self.hidden1=nn.Linear(28*28,128)# 784 -> 128self.hidden2=nn.Linear(128,256)# 128 -> 256self.output=nn.Linear(256,10)# 256 -> 10# 前向传播defforward(self,x):x=self.flatten(x)# 展平x=self.hidden1(x)# 第一层全连接x=torch.sigmoid(x)# 激活函数x=self.hidden2(x)# 第二层全连接x=torch.sigmoid(x)# 激活函数x=self.output(x)# 输出层returnx# 创建模型实例并移动到设备model=NeuralNetwork().to(device)print(model)输出示例: NeuralNetwork( (flatten): Flatten(start_dim=1, end_dim=-1) (hidden1): Linear(in_features=784, out_features=128, bias=True) (hidden2): Linear(in_features=128, out_features=256, bias=True) (output): Linear(in_features=256, out_features=10, bias=True) )六、训练与测试
6.1 定义损失函数和优化器
# 损失函数: 交叉熵(适用于多分类问题)loss_fn=nn.CrossEntropyLoss()# 优化器: 随机梯度下降(SGD),学习率 lr=0.01optimizer=torch.optim.SGD(model.parameters(),lr=0.01)6.2 训练函数
deftrain(dataloader,model,loss_fn,optimizer):model.train()# 切换到训练模式batch_count=1forX,yindataloader:# 将数据移到设备X,y=X.to(device),y.to(device)# 前向传播: 计算预测值pred=model(X)# 计算损失loss=loss_fn(pred,y)# 梯度清零optimizer.zero_grad()# 反向传播loss.backward()# 更新参数optimizer.step()# 每 100 批打印一次损失值ifbatch_count%100==0:print(f"loss:{loss.item():>7f}[批次:{batch_count}]")batch_count+=16.3 测试函数
deftest(dataloader,model,loss_fn):size=len(dataloader.dataset)num_batches=len(dataloader)model.eval()# 切换到评估模式test_loss,correct=0,0withtorch.no_grad():# 禁用梯度计算(节省内存)forX,yindataloader:X,y=X.to(device),y.to(device)pred=model(X)test_loss+=loss_fn(pred,y).item()correct+=(pred.argmax(1)==y).type(torch.float).sum().item()test_loss/=num_batches correct/=sizeprint(f"测试结果: 准确率:{(100*correct):.2f}%, 平均损失:{test_loss:.4f}")6.4 开始训练
epochs=10# 训练轮数fortinrange(epochs):print(f"\n第{t+1}轮训练")print("="*40)train(train_dataloader,model,loss_fn,optimizer)print("\n训练完成!")test(test_dataloader,model,loss_fn)七、运行结果
7.1 训练过程输出
第 1 轮训练 ======================================== loss: 2.300284 [批次: 100] loss: 2.292170 [批次: 200] loss: 2.303720 [批次: 300] loss: 2.307772 [批次: 400] ... loss: 1.222957 [批次: 900] 第 10 轮训练 ======================================== loss: 1.221065 [批次: 100] ... loss: 1.083294 [批次: 600] loss: 1.182948 [批次: 700] loss: 0.891334 [批次: 800] loss: 1.086440 [批次: 900] 训练完成! 测试结果: 准确率: 72.19%, 平均损失: 0.9466八、总结
核心知识点速查
| 知识点 | 关键概念 |
|---|---|
| PyTorch | 开源的深度学习框架,支持动态计算图 |
| Conda | Python 虚拟环境管理工具 |
| 张量 | PyTorch 的基本数据结构,支持 GPU 加速 |
| Device | GPU/CUDA、MPS、CPU 设备管理 |
| MNIST | 手写数字数据集,共 70,000 张图片 |
| DataLoader | 数据分批加载工具 |
| nn.Module | 所有神经网络模型的基类 |
| 前向传播 | forward()定义数据流向 |
| 反向传播 | loss.backward()自动计算梯度 |
| 交叉熵损失 | nn.CrossEntropyLoss()多分类标准损失 |
注意事项
| 要点 | 说明 |
|---|---|
| 设备一致性 | 模型和数据必须在同一设备上 |
model.train()vsmodel.eval() | 训练时用train(),测试时用eval() |
torch.no_grad() | 测试时禁用梯度计算,节省内存 |
optimizer.zero_grad() | 每次更新前必须清零梯度 |
| 数据归一化 | ToTensor()自动将像素值归一化到 [0,1] |
| NumPy 版本 | 必须使用 1.x 版本(pip install "numpy<2.0"),NumPy 2.0 与 PyTorch 2.1.0 不兼容 |
| 显存管理 | 如果显存不足,减小batch_size |
系列直达
- 上篇:深度学习入门:初识深度学习
- 本篇:深度学习入门:PyTorch 环境搭建与 MNIST 手写数字识别(本文)
- 下篇:敬请期待