资讯动态

深度学习入门实战:用PyTorch实现手写数字识别全流程

发布时间:2026/10/18 14:26:50 来源:尧图企业网站定制
1. 从零开始的深度学习为什么我劝你先选Python两年前有个朋友问我说想学深度学习但是被各种概念劝退了。我给他的建议很简单先把Python基础过一遍然后直接上手PyTorch跑一个小项目边跑边补理论。事实证明这条路是通的。现在回头看深度学习入门最大的障碍根本不是数学太难、模型太复杂而是很多人卡在了“工具链不会搭、概念太抽象、跑通第一个模型耗时太久”这三个坎上。Python在深度学习领域的位置基本等同于“普通话”在国内的普及度。不管是研究机构还是工业界主流框架PyTorch、TensorFlow、Keras、PaddlePaddle都提供了完整的Python接口社区里90%以上的开源代码、教程、预训练模型都是用Python写的。如果你选了Python意味着你能直接站在巨人的肩膀上遇到问题随手一搜就有解决方案缺什么功能大概率已经有人封装好了。这比你用C或者Java从零搭一个神经网络要省下好几个星期的折腾时间。这篇文章适合谁看如果你已经会一点Python基础语法变量、循环、函数、类但还没跑通过任何一个深度学习模型或者你跑过一些现成代码但对背后的原理一知半解那这篇文章就是给你准备的。我会尽量用白话把核心概念讲明白然后带你完整走一遍从数据准备到模型训练再到结果评估的全流程中间穿插我在实际项目里踩过的坑和总结出来的经验帮你少走弯路。需要说明的是深度学习领域发展实在太快工具版本、API接口都在不停迭代我下面提到的很多做法是基于我日常实践中的常用方案具体到你手里的环境可能需要在细节上做一点微调但整体思路和流程是通用的。2. 核心概念梳理把抽象名词翻译成人话2.1 张量就是装了数字的多维数组深度学习里你会反复听到“张量”Tensor这个词听着很高大上其实你可以把它理解成“装了数字的多维数组”。标量是0维张量向量是1维张量矩阵是2维张量再往上堆就是三维、四维甚至更高维。举个具体例子。一张256x256的彩色图片在计算机里就是一个三维张量形状是[3, 256, 256]3代表RGB三个颜色通道。一批32张这样的图片摞在一起就成了[32, 3, 256, 256]的四维张量。深度学习模型做的所有计算本质上就是对这种多维数组做各种变换。把这个概念想通了后面看任何代码里的shape都不会发怵。我刚开始学的时候最大的困惑是“为什么要搞这么多维度”。后来做项目多了才明白维度其实就是数据的组织结构。把数据组织成张量才能利用GPU做大规模并行计算才能让模型一次性处理一批数据大幅提升训练速度。2.2 自动求导深度学习最性感的设计传统编程里你写一个函数传入参数得到输出。深度学习框架里多了一个核心能力——自动求导Autograd它会自动追踪你的所有计算过程然后在你需要的时候自动算出损失函数对每一个参数的梯度。为什么要算梯度因为神经网络的学习本质上是一个“猜答案-算误差-调整参数”的循环而调整参数的方向和幅度就是由梯度决定的。梯度告诉你“参数往哪个方向调损失下降得最快”这就像你在山上要下山梯度就是告诉你哪边坡最陡的那个方向。没有自动求导的时候人们得用手推公式或者用数值方法近似计算梯度又慢又容易出错。现在PyTorch里只需要一行loss.backward()所有参数的梯度就自动算好了你只需要拿着梯度去更新参数就行。这个设计让深度学习的工程门槛降低了一个数量级也让初学者能把精力放在模型设计上而不是数学推导上。2.3 神经网络一堆参数和一个函数神经网络说了这么多本质上就是一个带大量可学习参数的复合函数。输入数据进来经过一层层的线性变换和非线性激活最后输出预测结果。我们可以拆开来看。一个最简单的神经元做的事就是把输入向量和权重做点积加上偏置再过一个激活函数。多个神经元组成一层多层堆叠起来就成了深度网络。中间层的神经元越多、层数越深模型的表达能力就越强能拟合的函数就越复杂。激活函数是让你别被“线性”限制死的关键。如果没有激活函数再多层网络叠加起来还是线性的那跟一层没区别。ReLU、Sigmoid、Tanh这些激活函数引入非线性才让神经网络能逼近各种复杂映射。实际使用中ReLU是默认首选计算快、梯度消失问题轻只有在输出层做二分类时才常用Sigmoid。2.4 损失函数和优化器模型的指挥棒和方向盘你拿什么标准来判断模型预测得好不好损失函数就是那把尺子。回归问题常用均方误差MSE分类问题常用交叉熵CrossEntropyLoss。模型训练的目标就一个让损失函数的值尽可能小。优化器则是决定怎么沿着梯度方向迈步子的策略。最简单的梯度下降是“朝着最陡的方向走一大步”但步子迈太大容易错过最低点迈太小又走得太慢。所以有了各种改进版比如SGD加动量、Adam、RMSProp。我个人的经验是刚上手时直接用Adam就行它对学习率的敏感度低默认参数就能跑出不错的效果省心。等到你开始精调模型了再去研究SGD加动量Momentum在某些任务上可能效果更好收敛更稳。这是个经验活不同任务上的最优选择不太一样靠实测说话。3. 环境准备与工具选型装好你的深度学习工作台3.1 框架选择PyTorch还是TensorFlow这是每个入门者都要面对的选择题。我的建议很明确选PyTorch。原因有三个。第一学术界的主流论文大部分用PyTorch实现代码开源比例最高你复现论文、查资料都方便。第二PyTorch的调试体验好它的计算图是动态的你可以随时打印中间结果像写普通Python程序一样检查问题这对初学者极其友好。第三工业界的部署工具链这些年已经跟上来了PyTorch模型转ONNX再部署到各种平台都很成熟不存在“学了没用”的问题。TensorFlow也不是不好它的部署生态在某些场景确实很完善但它的学习曲线更陡API更啰嗦初学者很容易被各种概念绕晕。等你把PyTorch玩熟了如果工作中需要再补TensorFlow也不迟。3.2 环境安装的完整步骤我给你一套经过多次验证的安装流程照着做基本不会出问题。首先是安装Python。我推荐直接用Miniconda它比完整版Anaconda轻量只带一个包管理器和Python解释器需要什么库再自己装避免环境臃肿冲突。# 下载并安装Miniconda # 安装完成后创建一个独立环境 conda create -n dl python3.9 conda activate dl然后是安装PyTorch。这里有个重要细节安装命令要先去PyTorch官网根据自己的操作系统和CUDA版本生成不要随便在网上复制。官网上有可视化选项选好配置后会给出一行命令复制执行即可。# 以CUDA 11.8为例具体以官网生成为准 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118注意如果电脑没有NVIDIA显卡或者用的是Mac就装CPU版。CPU版也能跑通所有代码只是训练速度慢很多。对于纯入门学习CPU版完全够用不用纠结。3.3 开发环境推荐代码编辑器这块我推荐Jupyter Notebook和VS Code搭配使用。Jupyter特别适合入门阶段因为你可以一段一段跑代码每跑一段就能立刻看到结果理解起来非常直观。VS Code则适合写比较正式的项目代码它有插件能直接连服务器跑训练还能可视化查看训练日志。我自己的习惯是探索阶段用Jupyter写正式的训练脚本用VS Code。两边的快捷键和界面稍微熟悉一下效率就能上来。4. 实战项目拆解用PyTorch实现手写数字识别4.1 项目背景与任务定义理论说再多不如动手跑一个项目。我选了深度学习领域最经典的入门项目——手写数字识别MNIST用卷积神经网络CNN把28x28像素的手写数字图片分类成0到9中的一个。MNIST数据集由6万张训练图片和1万张测试图片组成每张图都标好了正确答案。这个项目虽然简单但五脏俱全数据加载、模型定义、训练循环、评估流程、结果可视化所有深度学习项目该有的模块它都有。跑通它你就掌握了深度学习项目的标准流水线以后换任何数据集、任何任务都是同样的套路只是模型结构和数据入口不同而已。为了让它更贴近真实场景我没有用PyTorch自带的数据集下载接口而是直接手动下载数据集文件、自己写数据预处理逻辑。这样你能看到数据从原始文件到训练张量的完整流转过程而不是被框架封装好的黑盒。4.2 数据准备与预处理第一步是下载数据。MNIST数据可以从公开渠道获取包含四个文件训练图片、训练标签、测试图片、测试标签。下载后解压你会得到IDX格式的二进制文件。这个格式的读取逻辑我得详细说一下因为这里最容易把人绕晕。IDX文件开头是魔数magic number和维度信息后面才是真正的数据。读的时候要注意魔数解析和字节序转换我直接把完整的读取代码写出来import struct import numpy as np def load_mnist_images(filename): with open(filename, rb) as f: magic, num, rows, cols struct.unpack(IIII, f.read(16)) data np.fromfile(f, dtypenp.uint8).reshape(num, rows, cols) return data def load_mnist_labels(filename): with open(filename, rb) as f: magic, num struct.unpack(II, f.read(8)) labels np.fromfile(f, dtypenp.uint8) return labels x_train load_mnist_images(train-images.idx3-ubyte) y_train load_mnist_labels(train-labels.idx1-ubyte) x_test load_mnist_images(t10k-images.idx3-ubyte) y_test load_mnist_labels(t10k-labels.idx1-ubyte)然后是预处理。深度学习模型对数据的数值范围很敏感我们需要做归一化把像素值从0到255缩放到0到1之间。另外PyTorch的卷积神经网络期望输入形状是[通道数, 高, 宽]而我们从文件读出来的是[样本数, 高, 宽]所以要增加一个通道维度。x_train x_train.astype(np.float32) / 255.0 x_test x_test.astype(np.float32) / 255.0 x_train np.expand_dims(x_train, axis1) x_test np.expand_dims(x_test, axis1)最后把NumPy数组转成PyTorch张量创建数据加载器。DataLoader会自动把数据分成小批次batch训练时每次送一批进模型既省内存又能用GPU并行加速。import torch from torch.utils.data import TensorDataset, DataLoader train_dataset TensorDataset(torch.from_numpy(x_train), torch.from_numpy(y_train)) test_dataset TensorDataset(torch.from_numpy(x_test), torch.from_numpy(y_test)) train_loader DataLoader(train_dataset, batch_size64, shuffleTrue) test_loader DataLoader(test_dataset, batch_size64, shuffleFalse)4.3 构建卷积神经网络模型接下来是模型定义。我用一个两层卷积加两层全连接的网络结构虽然代码不长但它包含了CNN的几乎所有核心组件卷积层Conv2d、激活函数ReLU、池化层MaxPool2d、全连接层Linear、Dropout防过拟合。import torch.nn as nn import torch.nn.functional as F class SimpleCNN(nn.Module): def __init__(self): super(SimpleCNN, self).__init__() self.conv1 nn.Conv2d(1, 32, kernel_size3, padding1) self.conv2 nn.Conv2d(32, 64, kernel_size3, padding1) self.pool nn.MaxPool2d(2, 2) self.fc1 nn.Linear(64 * 7 * 7, 128) self.fc2 nn.Linear(128, 10) self.dropout nn.Dropout(0.5) def forward(self, x): x self.pool(F.relu(self.conv1(x))) x self.pool(F.relu(self.conv2(x))) x x.view(-1, 64 * 7 * 7) x F.relu(self.fc1(x)) x self.dropout(x) x self.fc2(x) return x这里有个新手很容易懵的地方self.fc1的输入维度64 * 7 * 7是怎么算出来的我们一步步推。输入是28x28的图经过第一个卷积层padding1不改变尺寸还是28x28经过第一个池化层变成14x14。再经过第二个卷积层尺寸不变还是14x14经过第二个池化层变成7x7。此时有64个通道所以展平后就是64 * 7 * 7。这个推导过程你得会因为后面自己设计网络结构时每一层的输入输出维度都需要这样手动推一遍。4.4 训练循环让模型开始学习模型定义好了接下来是训练。训练循环的固定模式是取数据、算预测、算损失、清梯度、回传、更新参数。每一轮epoch就把整个训练集跑一遍。import torch.optim as optim model SimpleCNN() criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr0.001) def train_one_epoch(model, train_loader, criterion, optimizer): model.train() running_loss 0.0 correct 0 total 0 for images, labels in train_loader: optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() _, predicted torch.max(outputs.data, 1) total labels.size(0) correct (predicted labels).sum().item() accuracy 100.0 * correct / total avg_loss running_loss / len(train_loader) return avg_loss, accuracy num_epochs 10 for epoch in range(num_epochs): train_loss, train_acc train_one_epoch(model, train_loader, criterion, optimizer) print(fEpoch [{epoch1}/{num_epochs}], Loss: {train_loss:.4f}, Accuracy: {train_acc:.2f}%)有几个点我需要特别强调。optimizer.zero_grad()这一行千万别漏。PyTorch的梯度是累积的如果不每次清零上一轮的梯度会叠加到这一轮上导致参数更新方向错乱模型根本学不进去。这是新手最容易踩的坑之一。model.train()和model.eval()的区别也容易被忽略。model.train()会启用Dropout等训练时特有的行为model.eval()则关闭它们。在评估模型时忘了切换到eval模式是很多“训练时准确率正常、测试时结果不对”的罪魁祸首。损失函数选CrossEntropyLoss时模型最后一层不要加Softmax。这个函数内部已经把Softmax和交叉熵合并计算了如果你在最后一层又加了个Softmax相当于算了两遍不仅数值会出问题训练也大概率不收敛。我见过太多人在这里卡住。4.5 模型评估与结果可视化训练结束后需要在测试集上评估真实表现。MNIST这个任务比较简单即使网络结构不深训练10轮左右也能轻松达到99%以上的准确率。def evaluate(model, test_loader): model.eval() 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() accuracy 100.0 * correct / total return accuracy test_acc evaluate(model, test_loader) print(fTest Accuracy: {test_acc:.2f}%)这里我用torch.no_grad()把梯度计算关掉了因为推理阶段不需要反向传播关了能省内存、加快速度这是评估和预测时的标准做法。只看准确率还不够直观我推荐把预测结果可视化出来。随机挑几张测试图片把图片和模型预测的标签并列打印出来让你亲眼看到模型认出来的是啥比冷冰冰的数字有说服力得多。可视化的方式很多Matplotlib画一下就行算是给这段学习历程留个纪念。5. 进阶扩展从MNIST走向真实项目5.1 CIFAR-10分类检验你的迁移能力MNIST跑通只是第一步我建议你立刻动手做第二个项目——CIFAR-10分类。它的图片是32x32的彩色图一共10个类别飞机、汽车、鸟、猫、鹿、狗、青蛙、马、船、卡车数据量和图像复杂度都比MNIST高一个档位。这个项目的意义在于检验你对整个流程的掌握程度。你可以把前面写的代码几乎原样复用只改两处模型的第一层输入从1通道改成3通道全连接层的维度根据新的特征图尺寸调整一下。如果你能自己完成这些修改并且把准确率从随机猜测的10%提升到80%以上说明你已经真正理解了深度学习项目的核心流程而不是只会抄代码。CIFAR-10也是很多人第一次体会到“调参”乐趣的地方。同一个模型把学习率从0.001改成0.0001训练轮数从10改成30准确率可能就有明显变化。你还可以尝试加BatchNorm层、换不同结构、做数据增强随机翻转、裁剪这些操作对最终效果的影响都是立竿见影的。通过对比实验你会慢慢建立起“什么操作会带来什么效果”的直觉这对后续做任何深度学习项目都极其重要。5.2 用GPU加速训练什么时候值得投入MNIST在CPU上训练也就几分钟但如果你去做CIFAR-10、跑更深的网络或者处理更大的图片CPU的速度就会让人想砸电脑。这时候就该考虑GPU了。PyTorch让CPU和GPU的切换变得异常简单核心就是to(device)操作。先定义设备再把模型和数据都放上去。device torch.device(cuda if torch.cuda.is_available() else cpu) model SimpleCNN().to(device) # 在每个batch循环里 images, labels images.to(device), labels.to(device)需要注意的地方如果显卡显存不够把batch_size调小因为batch_size乘上模型显存占用大致等于单次迭代需要的显存总量。常见的8GB显存在224x224输入下最多跑32左右batch_size的小模型再大就会报CUDA out of memory。5.3 迁移学习站在前人的肩膀上干活等你走完前面的路再遇到真实项目时会发现一个残酷的现实自己从零训练的模型往往效果不够好因为数据量太少或者算力不够。这时候最实用的手段是迁移学习。迁移学习的思路很简单别人已经在大规模数据集上预训练好的模型已经学到了非常通用的视觉特征比如边缘、纹理、形状等。你只需要把这些特征提取出来在自己少量数据上微调一下最后几层就行了。在PyTorch里用torchvision.models模块导入预训练模型只需要几行代码import torchvision.models as models model models.resnet18(pretrainedTrue) # 替换最后一层全连接改成自己任务的类别数 num_classes 10 model.fc nn.Linear(model.fc.in_features, num_classes)在真实项目里迁移学习带来的收益往往超出你的预期。哪怕你只有几千张图用预训练模型微调效果通常也能吊打自己从零训练的模型。这是深度学习实践中性价比最高的操作之一也是学术圈和工业界最主流的做法。6. 常见问题与调试技巧那些年我踩过的坑6.1 问题速查表问题现象可能原因解决方案Loss变成NaN学习率太大导致梯度爆炸降低学习率或加梯度裁剪准确率一直不变训练/评估模式没切换检查是否调用model.eval()测试集效果差过拟合加Dropout、数据增强、降低模型复杂度显存不足batch_size太大或模型太大减小batch_size降低分辨率训练很慢没有用GPU或数据加载瓶颈确认GPU可用num_workers调大Loss下降后反弹学习率偏大或数据有噪声减小学习率检查数据标签6.2 我的调试经验分享第一个经验是“先跑小、再跑全”。拿到一个新任务先用极小的数据子集比如100张图跑一遍完整流程确认代码逻辑没问题、模型能收敛再放开数据跑全量。这样可以省下大量排错时间。我见过太多人一上来就全量数据加复杂模型结果写了半天代码发现前面有个低级bug白白浪费几个小时的训练时间。第二个经验是“一步一个脚印的训练”。先用很小的batch_size跑一步print出loss如果loss能正常下降说明代码基本没问题。然后逐渐把数据量、模型复杂度加上去。这个渐进式验证的思路和写代码时先跑单元测试再集成是一个道理。第三个经验是关于调试的耐心。深度学习项目不像传统软件开发代码不报错只是第一步模型不收敛才是常态。你要学会看训练曲线来判断模型状态Loss一直在降说明在正常学习Loss降但测试准确率不涨说明过拟合了Loss反复横跳说明学习率太大Loss完全不动说明可能在实现上有bug。多看、多跑、多比较这条曲线会告诉你很多信息。6.3 学习资源推荐最后说点资源的事。动手之外理论也得跟得上。我推荐的学习路径是先看PyTorch官方教程的60分钟入门篇把张量、自动求导、基础模型定义过一遍然后读经典教材里关于多层感知机、卷积网络、循环网络的核心章节重点理解它们的设计思想和适用场景遇到具体问题再针对性搜索相关资料现在很多好文章和技术博客比我当年学习时的资料质量高太多了。有一点我想特别提醒不要花大量时间刷视频教程。深度学习是个动手极强的学科看视频感觉都会了一写代码就卡壳这是所有人的通病。最好的节奏是花10%时间看教程90%时间写代码、调参数、看结果。代码跑不通、效果不好这些都是最好的学习机会因为你会在解决它们的过程中真正理解每个组件背后的原理。我在实际项目里最大的体会是深度学习入门最难的从来不是算法原理而是“亲手跑通一个像样的项目”。这个门槛一旦跨过去后面就是水到渠成的事。希望你看完这篇文章能顺手把代码敲起来也去跑一个属于你自己的模型到时候你就会发现原来一切并没有想象中那么难。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑