当前位置: 首页 > news >正文

深度学习----由手写数字识别案例来认识PyTorch框架

一、深度学习常用的框架

常用的有:Caffe,TensorFlow,Keras,PyTorc

  • Caffe:配置文件搭模型方便, 但安装麻烦、缺新模型且更新停滞。
  • TensorFlow:Google 开发,1.x 版本代码冗余难上手,2.x 版本因收购 Keras 不兼容 1.x 。
  • Keras:基于 TensorFlow 封装,能简化代码难度 。
  • PyTorch:Facebook(现 Meta )开发,上手易,可直接套用模板 。

二、pytorch及cuda的配置

注意:自己需要下载的版本,还有要注意是cpu还是gpu

pytorch安装及配置-CSDN博客

CUDA安装教程(包括cuDNN的教程)一个博客带你了解所有问题-CSDN博客

三、PyTorch框架认识

现在主流的框架是PyTorch框架,所以这里详细介绍一下

PyTorch 是由 Meta(原 Facebook)开发并维护的开源深度学习框架,以易用性和灵活性著称,在深度学习领域应用广泛,尤其受研究人员和开发者青睐,以下是对它的详细介绍:

核心特性

  • 动态计算图:PyTorch 采用动态计算图机制,计算图在运行时根据前向传播的实际需求即时构建 ,允许用户随时修改计算图结构。例如在处理循环神经网络中不同序列长度的数据时,能灵活调整模型。相比之下,传统框架(如早期版本的 TensorFlow )使用静态计算图,需预先定义完整模型结构,调试难度较大。
  • 自动微分:通过 torch.autograd 模块实现,自动跟踪张量运算,自动计算梯度,无需开发者手动推导复杂导数公式,这使得反向传播算法的实现更为高效,让开发者更专注于模型设计本身。
  • Pythonic 设计:语法与 NumPy 高度兼容,熟悉 NumPy 的用户能快速上手。并且天然支持 Python 生态工具,比如可以使用 Pandas 加载数据再转为 PyTorch 张量进行处理,利用 Matplotlib 可视化训练过程等,降低了学习门槛。
  • 灵活性和扩展性:支持用户轻松自定义模型、损失函数和优化器,比如开发者可以使用 Python 强大的功能实现自定义层、自定义数据加载器等复杂逻辑。同时,它支持 GPU 和多 GPU 训练,通过简单 API 调用就能将计算任务分配到 GPU 上,大幅提升训练速度 。

主要模块

  • torch:PyTorch 的核心模块,提供张量运算、随机数生成等基本功能,是其他模块的基础。张量是 PyTorch 中数据的基本结构,类似于 NumPy 数组,但支持 GPU 计算。
  • torch.autograd:自动微分模块,用于自动计算梯度,在神经网络反向传播中发挥关键作用。
  • torch.nn:神经网络模块,将神经网络抽象为可堆叠的组件,提供各种神经网络层(如全连接层 nn.Linear、卷积层 nn.Conv2d、循环层 nn.LSTM )、激活函数、损失函数等,方便开发者构建神经网络模型。
  • torch.optim:优化模块,提供多种优化算法,如随机梯度下降(SGD)、Adam 等,用于神经网络训练过程中对模型参数进行优化更新。
  • torch.utils.data:数据处理模块,包含 Dataset 和 DataLoader 等类,用于处理数据集并进行批量加载,方便数据的读取和预处理。
  • torch.cuda:GPU 支持模块,提供与 GPU 相关的操作,能将张量移动到 GPU 上以加速计算。
  • torchvision:图像处理扩展库,专为计算机视觉任务提供数据集(如 MNIST、CIFAR - 10 等)、预训练模型(如 ResNet、VGG 等)和图像预处理工具。
  • torchtext:自然语言处理扩展库,提供文本数据集、词向量和常见的文本处理工具,助力自然语言处理任务的开发。
  • torchaudio:音频处理扩展库,用于音频数据的加载、转换和特征提取。
  • torch.jit:模型序列化与部署模块,有助于将 PyTorch 模型部署到不同环境中。

三、优化器的介绍

优化器用于调整深度学习模型参数,以最小化损失函数,以下是常见优化器分类及特点:

一、基础梯度下降类

1. 批量梯度下降法(Batch Gradient Descent, BGD)

  • 计算方式:用全样本数据计算梯度,如batch_size=64时,基于 64 个样本算梯度。
  • 优点:收敛次数少,模型训练相对稳定,易趋近全局最优。
  • 缺点:每次迭代需加载所有数据,内存占用大、计算耗时,不适大数据集。

2. 随机梯度下降法(Stochastic Gradient Descent, SGD)

  • 计算方式:从样本(如 64 个)中随机选一组训练,依此梯度更新参数。
  • 优点:训练速度快,无需加载全量数据,适合在线学习场景。
  • 缺点:梯度更新波动大,可能陷入局部最优,收敛路径不稳定。

3. 小批量梯度下降法(Mini-batch Gradient Descent)

  • 计算方式:将训练集分成小批量(如每次选 32/64 个样本 ),用小批量数据算误差、更新参数。
  • 特点:结合 BGD 和 SGD 优势,平衡计算效率与收敛稳定性,是深度学习常用策略。

二、改进优化算法

1. 自适应矩估计(Adaptive Moment Estimation, Adam)

  • 特点:自适应调整每个参数的学习率,结合一阶动量(梯度均值)和二阶动量(梯度平方均值 ),收敛快、鲁棒性好,适多种场景,尤其非凸优化任务。

2. 动量梯度下降(Momentum Gradient Descent)

  • 特点:引入 “动量” 概念,累加历史梯度方向,加速梯度下降。像物理中物体惯性,能冲过局部极小值,加快收敛,适合处理含噪声、平坦区域的损失函数。

3. AdaGrad

  • 特点:为不同参数自适应调整学习率,频繁更新参数的学习率衰减快,适合稀疏数据场景(如文本分类 ),但易因学习率过早衰减导致训练停滞。

4. RMSprop

  • 特点:改进 AdaGrad,只累加近期梯度平方,避免学习率过快下降,更适合非凸优化,在深度学习中常作自适应学习率优化器基础。

5. AdamW

  • 特点:对 Adam 增加权重衰减正则化,优化模型泛化能力,在大模型训练(如 Transformer )中,能有效缓解过拟合。

6. Adadelta

  • 特点:无需手动设置初始学习率,基于梯度的二阶矩动态调整,鲁棒性强,减少对学习率敏感问题,适合不同规模数据集训练。

四、激活函数

一、核心问题:梯度消失 & 梯度爆炸

深度神经网络训练时,反向传播需计算梯度(参数更新的依据),但会因激活函数导数连乘产生问题:

  • 梯度消失:若激活函数导数大多 <1(如 sigmoid 导数在 0~0.25),连乘后梯度→0,导致深层网络参数几乎不更新,模型难以训练。
  • 梯度爆炸:若激活函数导数大多 >1,连乘后梯度→无穷大,导致参数更新幅度过大,模型训练不稳定

二、根源:激活函数的「导数特性」

以 sigmoid 激活函数为例,其导数 σ’(z) 最大值仅 0.25(区间 0~0.25)。深层网络反向传播时,梯度需通过 σ’(z₁)×w₂×σ’(z₂)×w₃×… 连乘传递,一旦层数多,梯度极易因连乘「指数级衰减 / 膨胀」,引发消失或爆炸。

三、解决方案:换用更优激活函数

为缓解梯度问题,避免用 sigmoid,改用导数更稳定的激活函数,常见替代方案:

  • ReLU:导数为 1(正区间),无梯度消失风险,计算高效,是深度学习标配。
  • tanh:导数比 sigmoid 更接近 1(但仍 <1),效果优于 sigmoid 但不如 ReLU。
  • P-ReLU、R-ReLU:ReLU 变种,解决 ReLU 「死亡神经元」问题,导数更灵活。
  • Maxout:动态选最优激活值,导数稳定,但计算成本略高。

五、深度学习的手写数字识别

1、导入相关库以及获取数据集

import torch
from torch import nn#导入神经网络模块
from torch.utils.data import DataLoader#数据包管理工具,打包数据
from torchvision import datasets#封装了很多与图像相关的模型,和数据集
from torchvision.transforms import ToTensor#将其他数据类型转化为张量train_data=datasets.MNIST(root='data',train=True,#是否读取下载后数据中的训练集download=True,#如果之前下载过则不用下载transform=ToTensor()
)
test_data=datasets.MNIST(root='data',train=False,download=True,transform=ToTensor()
)

2、数据打包与加载

Data_loader用于将数据集分批次打包,需要注意的是数据实际加载发生在for循环遍历Data_loader时(每次循环从硬盘加载指定数量(如64张图片)的数据),而非初始化阶段

train_loader=DataLoader(train_data,batch_size=64)
test_loader=DataLoader(test_data,batch_size=64)

3、判断使用的设备

判断是不是使用cuda进行训练

device='cuda' if torch.cuda.is_available() else 'mps' if torch.backends.mps.is_available() else 'cpu'
print(f'Using {device} device')

4、神经网络构建(通过类的继承)

必须继承 nn.Module 类,并实现 __init__初始化网络层)和 forward定义数据流向)方法即前向传播

nn.Flatten()创建一个展开对象,将输入图片展开为一维数据(如28*28=784)

通过nn.Linear()创建网络层,第一个参数是有多少信息传进来,第二个参数是当前本层神经元个数或有多少信息传出去

这里我们创建了两个隐藏层和一个输出层

class NeuralNetwork(nn.Module):def __init__(self):super().__init__()self.flatten=nn.Flatten()#创建一个展开对象self.hidden1=nn.Linear(28*28,128)#第一个参数是有多少信息传进来,第二个参数是当前本层神经元个数或有多少信息传出去self.hidden2=nn.Linear(128,256)self.out=nn.Linear(256,10)def forward(self,x):#前向传播x=self.flatten(x)x=self.hidden1(x)x = torch.sigmoid(x)x=self.hidden2(x)x = torch.sigmoid(x)x=self.out(x)return x
model=NeuralNetwork().to(device)#把刚刚创建的模型传入device中
print(model)

forward()方法不可改名,我们要明确数据的流动顺序(如 Flatten → Linear → 激活 → Linear → 输出)。最后一层通常不设激活函数,直接输出原始值

最后通过model=NeuralNetwork().to(device)把刚刚创建的模型传入device(gpu/cpu)中

注意:

  • self.flatten 等为类属性,存储网络层对象(如 nn.Flattennn.Linear 的实例)。
  • 调用 forward 时,数据依次通过各层对象执行计算。
  • 网络结构可调整,但需确保参数一致性(如神经元凸起数量匹配即前后输入输出个数要匹配)。

5、损失函数和优化器        

我们使用交叉熵损失函数来计算多分类问题的损失

使用SGD随机梯度下降来作为我们的优化器,传入模型参数和步长即学习率

loss_fn=nn.CrossEntropyLoss()
optimizer=torch.optim.SGD(model.parameters(),lr=0.01)#lr是学习率即步长,第一个参数是我们要训练的参数

6、数据的加载与训练

自已定义一个train()方法

首先我们在训练前使用model.train()方法告诉模型我们要准备开始训练,赋予模型修改参数w的权限(测试时则需调用model.eval(),固定模型参数(ω)仅允许读取不允许修改,防止意外修改

遍历data_loader循环读取数据,每次取出64张图片,由于数据与模型需要在同一个设备(CPU/GPU)上,所以我们通过X.to(device),y.to(device)实现数据迁移(如GPU训练时需将数据移至GPU)。

调用model的forward()方法完成前向传播,再将前向传播的结果和Y标签传入损失函数得到损失值

损失值为tensor类型

最后进行反向传播更新w值

  1. 优化器梯度清零(zero_grad)。

  2. 反向更新计算出更新后的w值(loss.backward)。

  3. 更新模型参数(optimizer.step)。

通过item()方法将损失值转化为可读的float类型,我们每一百个批次打印一次损失值

def train(dataloader,model,loss_fn,optimizer):model.train()#告诉模型,准备开始训练batch_size_num=1for X,y in dataloader:X,y=X.to(device),y.to(device)#把训练数据和标签也传入cpu或gpu中pred=model.forward(X)loss=loss_fn(pred,y)optimizer.zero_grad()loss.backward()optimizer.step()loss_value=loss.item()if batch_size_num%100==0:print(f'loss:{loss_value:>7f} [number:{batch_size_num}]')batch_size_num+=1

补充:

  • 模型传入64张图片,每张图片维度为28x28(单通道)。训练时采用逐张图片处理,但计算机通过并行计算实现高效运行。
  • 数据以矩阵形式处理,64张图片对应64个并行网络实例,GPU同时运行64个相同模型进行前向传播。
  • Batch size的优势在于并行化计算,64个模型的输出结果通过累加平均计算损失值,并统一更新模型参数。

7、模型测试

自己定义一个test()方法

首先调用model.eval()方法告诉模型我们要开始测试了,停止对w的更新

通过len(dataloader.dataset)获取数据集的长度便于后面计算准确率

使用torch.no_grad()禁用梯度更新,减少内存占用

X,y还是需要传入到模型的设备中,继续前向传播得到结果和损失值,将损失值累加到一起最后计算平均损失

通过pred.argma(1)找出64组训练结果中每一组的结果(最大概率的)然后通过==y判断与测试集的标签是否相同,相同返回True,不同返回False,共返回64个,

再通过.type(torch.float)将结果转化为0,1,True则转化为1然后sum()计算总合,将每个correct累加起来这样我们就能知道我们总共对了多少,通过item()转化为可读的浮点类型

最后通过累加后的correct计算正确率

def test(dataloader,model,loss_fn):model.eval()#开始测试,停止更新wlen_data=len(dataloader.dataset)correct,num_batch=0,0loss_sum=0with torch.no_grad():for X,y in dataloader:X,y=X.to(device),y.to(device)pred=model.forward(X)loss_sum+=loss_fn(pred,y)correct+=(pred.argmax(1)==y).type(torch.float).sum().item()num_batch+=1correct/=len_dataloss_avg=loss_sum/num_batchprint(f'Accuracy:{100*correct}%\nLoss Avg:{loss_avg}')

8、结果

调用train()和test()方法查看结果      


train(train_loader, model, loss_fn, optimizer)
test(test_loader,model,loss_fn)

准确率太低,需要优化

9、模型优化

1.设置训练的次数epoch

如果我们不设置训练次数,那我们就相当于只将训练集的内容只训练了一次明显不够

可以通过for循环实现多次训练

for i in range(epochs):print(f'==========第{i+1}轮训练==============')train(train_loader, model, loss_fn, optimizer)print(f'第{i+1}轮训练结束')
​​​​​​2.优化器改进

有以下的优化器

  • 原使用随机梯度下降(SGD),建议改用自适应优化器(如Adam)。
  • Adam优化器优势:
    • 更快收敛(首轮损失值从2.0降至0.2)。
    • 训练10轮后正确率从19%提升至96.81%。
  • 学习率调整:
    • 初始学习率0.01导致后期损失值震荡,改为0.005后正确率微升至97.5%。
    • 学习率过大会阻碍模型到达极小值点。
optimizer=torch.optim.Adam(model.parameters(),lr=0.005)
3.激活函数改进
  • Sigmoid函数缺陷:
    • 偏导数范围(0~0.25)导致深层网络梯度消失(连乘趋近于0)。
    • 影响参数更新,造成训练停滞或震荡。

替代方案:ReLU(Rectified Linear Unit)函数。

  • 优势:
    • 计算简单(偏导数为0或1,避免梯度消失)。
    • 非线性映射能力(小于零输出0,大于零线性输出)。
  • 当前实验效果:
    • 对浅层网络(如2层)影响有限,正确率未显著提升。
    • 深层网络中推荐替换Sigmoid以避免梯度问题。
    def forward(self,x):#前向传播x=self.flatten(x)x=self.hidden1(x)x=torch.relu(x)x=self.hidden2(x)x=torch.relu(x)x=self.out(x)return x

10、优化后的代码

import torch
print(torch.__version__)import torch
from torch import nn#导入神经网络模块
from torch.utils.data import DataLoader#数据包管理工具,打包数据
from torchvision import datasets#封装了很多与图像相关的模型,和数据集
from torchvision.transforms import ToTensor#将其他数据类型转化为张量train_data=datasets.MNIST(root='data',train=True,#是否读取下载后数据中的训练集download=True,#如果之前下载过则不用下载transform=ToTensor()
)
test_data=datasets.MNIST(root='data',train=False,download=True,transform=ToTensor()
)train_loader=DataLoader(train_data,batch_size=64)
test_loader=DataLoader(test_data,batch_size=64)device='cuda' if torch.cuda.is_available() else 'mps' if torch.backends.mps.is_available() else 'cpu'
print(f'Using {device} device')
#定义神经网络,通过类的继承
class NeuralNetwork(nn.Module):def __init__(self):super().__init__()self.flatten=nn.Flatten()#创建一个展开对象self.hidden1=nn.Linear(28*28,128)#第一个参数是有多少信息传进来,第二个参数是当前本层神经元个数或有多少信息传出去self.hidden2=nn.Linear(128,256)self.out=nn.Linear(256,10)def forward(self,x):#前向传播x=self.flatten(x)x=self.hidden1(x)# x=torch.sigmoid(x)#激活函数x=torch.relu(x)x=self.hidden2(x)x=torch.relu(x)x=self.out(x)return x
model=NeuralNetwork().to(device)#把刚刚创建的模型传入device中
print(model)def train(dataloader,model,loss_fn,optimizer):model.train()#告诉模型,准备开始训练batch_size_num=1for X,y in dataloader:X,y=X.to(device),y.to(device)#把训练数据和标签也传入cpu或gpu中pred=model.forward(X)loss=loss_fn(pred,y)optimizer.zero_grad()loss.backward()optimizer.step()loss_value=loss.item()if batch_size_num%100==0:print(f'loss:{loss_value:>7f} [number:{batch_size_num}]')batch_size_num+=1def test(dataloader,model,loss_fn):model.eval()#开始测试,停止更新wlen_data=len(dataloader.dataset)correct,num_batch=0,0loss_sum=0with torch.no_grad():for X,y in dataloader:X,y=X.to(device),y.to(device)pred=model.forward(X)loss_sum+=loss_fn(pred,y)correct+=(pred.argmax(1)==y).type(torch.float).sum().item()num_batch+=1correct/=len_dataloss_avg=loss_sum/num_batchprint(f'Accuracy:{100*correct}%\nLoss Avg:{loss_avg}')
loss_fn=nn.CrossEntropyLoss()
# optimizer=torch.optim.SGD(model.parameters(),lr=0.01)#lr是学习率即步长,第一个参数是我们要训练的参数
optimizer=torch.optim.Adam(model.parameters(),lr=0.005)epochs=10
for i in range(epochs):print(f'==========第{i+1}轮训练==============')train(train_loader, model, loss_fn, optimizer)print(f'第{i+1}轮训练结束')
test(test_loader,model,loss_fn)

六、x=self.hidden1(x)对象后直接传参说明

在上面代码中有一些代码如

x=self.flatten(x)
x=self.hidden1(x)

x=self.hidden2(x)

x=self.out(x)

loss=loss_fn(pred,y)

中都是直接对象后直接加参数这是因为继承自 nn.Module 的类可以省略 forward,直接通过对象调用(如 model(x) 等价于 model.forward(x))。

  • nn.Flattennn.Linear 等层均继承自 nn.Module,因此它们的 forward 方法可省略调用。
  • 交叉熵损失函数(nn.CrossEntropyLoss)同样继承自 nn.Module,支持直接调用(如 loss(pred, target))。

http://www.dtcms.com/a/351696.html

相关文章:

  • 构建AI智能体:十四、从“计算”到“洞察”:AI大模型如何让时间序列数据“开口说话”
  • version GLIBCXX_3.4.30‘ not found (required by cmake)
  • JVM线上调优参数配置指南
  • 今日分享:C++ string 类模拟实现
  • 深度学习之第四课卷积神经网络CNN(一)
  • 不卡顿、不掉线!稳定可靠的体育赛事直播系统源码解析
  • 【Chrome】更新后白屏无法显示问题
  • 【力扣】面试经典150题总结04-区间/栈
  • python 自学笔记13 numpy数组规整
  • 智能驾驶机器学习知识总结
  • 越过千万生死线,鸿蒙直面商业化考验
  • ME_INFORECORD_MAINTAIN_MULTI,创建采购单信息记录,报错ME 816 系统错误(方法PROCESS_CONDITION中错误)
  • Feign 调用为服务报 `HardCodedTarget(type=xxxClient, name=xxxfile, url=http://file)`异常
  • 关于C#中运算符的简单说明
  • 为什么的中小企业很难承受“大型系统”的成本
  • 【RAGFlow代码详解-10】文本处理和查询处理
  • 深度学习(五):正则化:约束模型的复杂度
  • 什么样的 IP 能穿越周期,持续被用户买单?​
  • 深入解析交换机端口安全:Sticky MAC的工作原理与应用实践
  • 自动化测试概念与 Web 自动化实战(基于 Selenium)
  • 第一篇:MySQL安装部署全攻略
  • 计算机毕业设计 java 养老院管理系统 基于 Java 的养老院管理平台 Java 开发的养老服务系统
  • Linux云计算运维简明教程02 应用运维
  • 视频合成素材视频-多合一功能-青柠剪吧
  • 智能手机使用(2015-2019)
  • 基于MATLAB的运动模糊图像修复方法研究(LW+源码+讲解+部署)
  • vue2+elementui 表格单元格增加背景色,根据每列数据的大小 颜色依次变浅显示
  • 科研笔记:SCI论文中的功能性图表
  • 【技术教程】如何将文档编辑器集成到用 .Net 编写的网络应用程序中
  • VScode,设置自动保存