深度学习入门第一课——神经网络实现手写数字识别

昨天我们讲了深度学习的大致框架,下面我们用深度学习网络来实现一个小项目——手写数字识别。

完整代码

import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensortraining_data=datasets.MNIST(root='./data',train=True,download=True,transform=ToTensor())test_data=datasets.MNIST(root='./data',train=False,download=True,transform=ToTensor())from matplotlib import pyplot as plt, figurefigure = plt.figure()
for i in range(9):image,label = training_data[i+59000]figure.add_subplot(3,3,i+1)plt.title(label)plt.axis('off')plt.imshow(image.squeeze(),cmap='gray')a=image.squeeze()
plt.show()train_dataloader=DataLoader(training_data,32)
test_dataloader=DataLoader(test_data,32)device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)for X, y in test_dataloader:print(f"Shape of X [N, C, H, W]: {X.shape}")  # 批次大小、通道、高度、宽度print(f"Shape of y: {y.shape}, Type of y: {y.dtype}")breakclass Net(nn.Module):def __init__(self):super().__init__()self.flatten = nn.Flatten()self.hidden1 = nn.Linear(28*28,128)self.hidden2 = nn.Linear(128,400)self.output = nn.Linear(400,10)def forward(self, x):x = self.flatten(x)x = self.hidden1(x)x = nn.ReLU()(x)# x = nn.functional.sigmoid(x)x = self.hidden2(x)x = nn.ReLU()(x)# x = nn.functional.sigmoid(x)x = self.output(x)return xmodel = Net()
model.to(device)
loss_fn = nn.CrossEntropyLoss()
# optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)def train(train_dataloader,model,loss_fn,optimizer):model.train()batch_size_num=1for X, y in train_dataloader:X,y=X.to(device),y.to(device)output = model.forward(X)loss = loss_fn(output,y)optimizer.zero_grad()loss.backward()optimizer.step()a=loss.item()if batch_size_num %100 ==0:print(f"Batch size: {batch_size_num}, Loss: {a}")batch_size_num+=1# print(model)def test(dataloader,model,loss_fn):size = len(dataloader.dataset)num_batches = len(dataloader)model.eval()batch_size_num=1loss,correct=0,0with torch.no_grad():for X, y in test_dataloader:X,y=X.to(device),y.to(device)pred = model(X)loss = loss_fn(pred,y)+losscorrect += (pred.argmax(1) == y).type(torch.float).sum().item()loss/=num_batchescorrect/=sizeprint(f'Test result: \n Accuracy: {(100*correct)}%,Avg loss: {loss}')# train(train_dataloader,model,loss_fn,optimizer)
# test(test_dataloader,model,loss_fn)epochs=10
for i in range(epochs):print(f"Epoch {i+1}")train(train_dataloader,model,loss_fn,optimizer)test(test_dataloader,model,loss_fn)

结果展示

下面我来进行拆解,

 1 导入库

import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor

前面就是导入库,没什么多说的。

2 导入数据

training_data=datasets.MNIST(root='./data',train=True,download=True,transform=ToTensor())test_data=datasets.MNIST(root='./data',train=False,download=True,transform=ToTensor())

首先我们从datasets.MINST()这个库中导入数据

从上面我们可以看出,这个库中包含一些网址,然后可以从其中下载一些数据。

root='./data',        这个表示把数据存储在这样一个文件夹中
train=False,        这个表示下载的是训练集还是测试集,True为训练集,False为测试集
download=True,        下载
transform=ToTensor())        转化为Tensor数据,将图像数据转换为PyTorch张量(像素值归一化到[0,1])。

3 数据展示

from matplotlib import pyplot as plt, figurefigure = plt.figure()
for i in range(9):image,label = training_data[i+59000]figure.add_subplot(3,3,i+1)plt.title(label)plt.axis('off')plt.imshow(image.squeeze(),cmap='gray')a=image.squeeze()
plt.show()
  • 创建一个3x3的子图,显示训练集中最后9个样本(索引59000到59008)的图像和标签。
  • image.squeeze()将形状从[1,28,28]变为[28,28],因为matplotlib要求单通道图像为二维。

结果展示

4 创建数据加载器

train_dataloader=DataLoader(training_data,32)
test_dataloader=DataLoader(test_data,32)
  • 训练和测试数据加载器,每次迭代返回一个批次(32张图片和对应的标签)。

5 ​设置设备

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)
  • 检查是否有可用的GPU,如果有则使用第一个GPU,否则用CPU。

打印一批测试数据的形状

for X, y in test_dataloader:print(f"Shape of X [N, C, H, W]: {X.shape}")  # 批次大小、通道、高度、宽度print(f"Shape of y: {y.shape}, Type of y: {y.dtype}")break
  • 从测试数据加载器中取一个批次,打印输入图像和标签的形状。
  • X的形状:[32, 1, 28, 28](32个样本,1个通道,28x28像素)
  • y的形状:[32](32个标签)

7 ​定义神经网络模型

class Net(nn.Module):def __init__(self):super().__init__()self.flatten = nn.Flatten()self.hidden1 = nn.Linear(28 * 28, 128)  # 第一层全连接self.hidden2 = nn.Linear(128, 400)     # 第二层全连接self.output = nn.Linear(400, 10)       # 输出层(10个数字)def forward(self, x):x = self.flatten(x)        # 将图像展平为向量:[batch, 28 * 28]x = self.hidden1(x)        # [batch, 128]#x = nn.functional.sigmoid(x)x = nn.ReLU()(x)           # ReLU激活x = self.hidden2(x)        # [batch, 400]#x = nn.functional.sigmoid(x)x = nn.ReLU()(x)           # ReLU激活x = self.output(x)         # [batch, 10]return x
  • 一个简单的多层感知机(MLP)模型:
    • 输入:28x28=784维
    • 两个隐藏层:128和400个神经元,使用ReLU激活。
    • 输出层:10个神经元(对应0-9),无激活(后面用交叉熵损失包含Softmax)。

forward为前向传播

        第一步先用flatten(x)把数据拉成一维的,例如原本28*28的二维数据,变成了28*28的一维数据

        第二步传入到隐含层

        第三步使用激活函数进行激活(这里初开始使用sigmod函数进行激活,但使用ReLU激活比较好)

sigmod和ReLU函数对比

Sigmoid 激活函数的公式为: f(x) = 1 / (1 + e^(-x)) 它将输入值映射到 (0, 1) 区间,常用于二分类问题的输出层。Sigmoid 的优点是可以提供概率分布,但存在以下问题:

  • 梯度消失问题:当输入值过大或过小时,梯度趋近于 0,导致深层网络训练困难。

  • 非零中心输出:输出值始终为正,可能导致权重更新不稳定。

  • 计算复杂度高:涉及指数运算,计算效率较低。

ReLU 激活函数的公式为: f(x) = max(0, x) ReLU 是深度学习中广泛使用的激活函数,具有以下优点:

  • 计算效率高:只需简单的比较操作,计算速度快。

  • 避免梯度消失:在正值范围内梯度为常数,有助于深层网络的训练。

  • 稀疏激活性:部分神经元输出为 0,减少参数依赖,缓解过拟合问题。

然而,ReLU 也存在死亡 ReLU问题,即当神经元进入负值区域时,梯度为 0,可能导致神经元永不激活。为此,改进版本如 Leaky ReLU 和 Parametric ReLU 被提出。

对比总结

  • 计算效率:ReLU 优于 Sigmoid,适合处理大规模数据。

  • 梯度问题:Sigmoid 易出现梯度消失,而 ReLU 在正值范围内表现更优。

  • 适用场景:Sigmoid 常用于二分类问题的输出层,而 ReLU 更适合深度网络,如图像识别和自然语言处理任务

8 创建模型对象

model = Net()
model.to(device)

这里创建了一个模型对象,并把模型传入到我们的device也就是GPU中

9 初始化损失函数

loss_fn = nn.CrossEntropyLoss()

然后定义损失函数,这里为交叉熵损失函数。因为分类算法一般都是用交叉熵损失函数,找最优参数。还有许多损失函数的介绍看我主页。

10 建立优化器

# optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)

这里最初建立的是SGD优化器,这个是梯度下降优化器,lr步长为0.01,后面优化成了Adam优化器,这个比较实用。适用几乎所有哦网络。

11 创建训练函数

def train(train_dataloader,model,loss_fn,optimizer):model.train()batch_size_num=1for X, y in train_dataloader:X,y=X.to(device),y.to(device)output = model.forward(X)loss = loss_fn(output,y)optimizer.zero_grad()loss.backward()optimizer.step()a=loss.item()if batch_size_num %100 ==0:print(f"Batch size: {batch_size_num}, Loss: {a}")batch_size_num+=1# print(model)

第一步model.train(),这里设置为训练模式(影响 Dropout、BatchNorm 等层),也就是说允许读写w。

第二部 分批次对训练集进行导入,前行传播,计算损失值,清空之前的梯度,反向计算梯度,更新梯度,下面就是训练100批次就打印一下损失值。

12 创建测试函数

def test(dataloader, model, loss_fn):size = len(dataloader.dataset)     # 总样本数num_batches = len(dataloader)      # batch 数量model.eval()                       # 设置为评估模式batch_size_num = 1loss, correct = 0, 0with torch.no_grad():  # 不计算梯度,节省内存和计算for X, y in dataloader:X, y = X.to(device), y.to(device)pred = model(X)loss += loss_fn(pred, y)  # 累积损失correct += (pred.argmax(1) == y).type(torch.float).sum().item()  # 统计正确预测的数量loss /= num_batches                # 平均损失correct /= size                    # 正确率print(f'Test result: \n Accuracy: {(100*correct):.2f}%, Avg loss: {loss:.4f}')

我们先把总样本数求出来是因为后面要用预测对的样本数/总样本数就可以得到准确率了

model.eval()就是把w的模式改为仅读模式

初始化损失值和准确率

在不对梯度进行改变的情况下,对训练集中每一个值进行循环,得到预测值,然后用预测值和真实值作对比,然后得出的是一个列表,其中全是True和False.然后转化为float也就是转化为数值型0/1,然后再sum求综合再取item()。

然后计算平均损失和正确率(其实这里损失值没啥用了)

13 训练和测试

train(train_dataloader,model,loss_fn,optimizer)
test(test_dataloader,model,loss_fn)

这里进行了训练和测试,结果并不是很理想,但是我们可以叠加训练,也就是说,在优化的时候次数太少,w权重并没有更新到最优状态。然后我们叠加train,这也是深度学习的一个优点,可以在前一层的基础上继续训练。

epochs=10
for i in range(epochs):print(f"Epoch {i+1}")train(train_dataloader,model,loss_fn,optimizer)test(test_dataloader,model,loss_fn)

如上,我们进行了十次训练,准确率从0.87训练到了95

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/94410.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/94410.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue中的scoped属性

理解&#xff1a; 在 .vue 文件中&#xff0c;scoped 是 <style> 标签的一个属性&#xff0c;作用是让样式只作用于当前组件&#xff0c;避免样式污染其他组件 scoped 让样式只在自己的组件内生效&#xff0c;不会影响到其他组件的同名元素 举例 没有 scoped 的情况&…

S2B2B系统哪个好,商淘云、数商云、金蝶云苍穹供应链批发哪个比较靠谱

在数字化商业浪潮汹涌的当下&#xff0c;S2B2B系统已成为众多企业优化供应链、提升运营效率、拓展业务版图的关键利器。从大型企业的复杂产业链协同&#xff0c;到中小企业对上下游资源的整合与把控&#xff0c;S2B2B系统的身影无处不在。但面对市场上层出不穷的各类S2B2B系统&…

利用3台机器搭建Kubernetes集群

文章目录1 机器准备修改主机名(可选)配置IP地址和主机名映射关闭防火墙关闭swap2 安装docker修改并更新软件源安装docker设置路由转发规则&#xff0c;并使配置生效安装 kubelet&#xff0c;kubeadm&#xff0c;kubectl3 初始化集群4 集群初始化5 work节点加入6 部署网络插件本…

C6.3:发射结交流电阻

当一个小交流电压加在发射结上&#xff0c;会产生发射极交流电流&#xff0c;该交流电流的大小取决于Q点位置&#xff0c;而曲线是弯曲的&#xff0c;所以Q点在曲线上的位置越高&#xff0c;发射极交流电流的峰值越大。发射极总电流是直流分量和交流分量叠加而成&#xff0c;即…

python中生成器

生成器是根据算法生成数据的一种机制&#xff0c;每次调用生成器只生成一个值&#xff0c;可以节省大量内存生成器推导式gg ( i * 2 for i in range(5))print(gg)print(next(gg)) # 0 print(next(gg)) # 2 print(next(gg)) # 4 print(next(gg)) # 6 print(next(gg)) # 8yield…

【RAGFlow代码详解-28】部署和基础设施

Docker 部署架构 RAGFlow 使用多阶段 Docker 构建过程和 Docker Compose 编排进行部署。该系统支持完整和轻量级部署变体。 多阶段 Docker 构建构建过程由 Dockerfile 2-214中定义的三个阶段组成&#xff1a; 基础阶段 &#xff1a;使用系统依赖项、ML 模型和运行时环境设置 Ub…

【prism】Prism 弹窗在 ViewModel 中控制大小的实践总结

好 &#x1f44d; 这个思路非常适合写成一篇 实用型博客&#xff0c;主题就是 “Prism 弹窗如何在 ViewModel 中控制窗口大小”。我帮你整理了一篇文章草稿&#xff0c;你可以直接拿去修改/发布。Prism 弹窗在 ViewModel 中控制大小的实践总结 在 WPF 中使用 Prism 的 IDialogS…

项目管理进阶——研发项目立项管理制度

为使公司研究开发(以下简称研发)项目的管理工作规范化、程序化、充分调度研发人员的积极性,提高研发成果的产出率和成果转化率,特制定管理办法。 一、 研发项目的立项: 原则上公司部设立基础研究项目。研发项目的重点放在符合市场需要。能很快转化成产品,或对现有生产工…

CMake构建学习笔记20-iconv库的构建

1. 构建 iconv是一个用于在不同字符编码&#xff08;如 UTF-8、GBK、ISO-8859-1 等&#xff09;之间进行转换的开源库。笔者在《c中utf8字符串和gbk字符串的转换》这篇文章中介绍过如何在Windows下实现utf8字符串和gbk字符串的转换&#xff0c;不过该实现是基于Win32 API的&am…

STM32的Sg90舵机

1.舵机到底要的是什么信号&#xff1f;想象舵机就像一个“听秒表的工人”&#xff1a;这个工人每隔 20ms 就抬头看看秒表一次。秒表上的 高电平持续多久&#xff0c;他就把这个时间当成“指令角度”。高 1ms → 转到最左&#xff08;0&#xff09; 高 1.5ms → 转到中间&#x…

动态带宽扩展(DBE):下一代Wi-Fi性能提升的关键技术

动态带宽扩展(DBE):下一代Wi-Fi性能提升的关键技术 引言 在无线通信技术快速发展的今天,用户对网络带宽和传输速率的需求呈指数级增长。为了满足这种需求,IEEE 802.11标准不断演进,引入了多项创新技术。其中,动态带宽扩展(Dynamic Bandwidth Expansion, DBE) 作为80…

Seaborn数据可视化实战:Seaborn数据可视化基础-从内置数据集到外部数据集的应用

Seaborn数据集探索与图表绘制实践 学习目标 通过本课程&#xff0c;你将学习如何使用Seaborn库中的内置数据集&#xff0c;了解如何加载这些数据集&#xff0c;并掌握使用这些数据集绘制图表的基本方法。此外&#xff0c;你还将学习如何导入外部数据集&#xff0c;并在Seaborn中…

漫谈《数字图像处理》之经典空域边缘检测Canny与LOG

在《数字图像处理》的图像分割领域&#xff0c;Canny 边缘检测与 LOG&#xff08;高斯拉普拉斯&#xff09;边缘检测是两款极具代表性的先进空域算法。不同于深度学习驱动的方法&#xff0c;它们通过对图像像素的直接计算提取边缘&#xff0c;下面用更贴近日常认知的语言&#…

抢红包案例加强版

加join的功能是保证线程全部运行完毕&#xff0c;之后好统计构造器刚开始为空列表&#xff0c;利用这个方法返回每个成员列表&#xff08;把每个员工弄成一个列表里面写他们抢到的红包大小&#xff0c;索引代表抢到的个数。&#xff09;

曲面方程的三维可视化:从数学解析到Python实现

在三维几何建模中,我们经常遇到需要将隐式方程可视化的需求。本文将深入探讨一个特定的曲面方程: XH−YH2+ZH2tan⁡(θ)−H2πarcsin⁡(YHYH2+ZH2)=0 X_H - \frac{\sqrt{Y_H^2 + Z_H^2}}{\tan(\theta)} - \frac{H}{2\pi} \arcsin\left( \frac{Y_H}{\sqrt{Y_H^2 + Z_H^2}} \r…

当GitHub宕机时,我们如何协作

引言简述GitHub在全球开发协作中的重要性提出假设性问题&#xff1a;当GitHub不可用时&#xff0c;如何确保团队协作不中断常见的GitHub宕机场景服务完全不可用&#xff08;如DNS问题、全球性故障&#xff09;部分功能受限&#xff08;如API速率限制、仓库访问失败&#xff09;…

如何确定哪些层应添加适配器(Adapter)?(58)

“它如何确定哪些层应添加适配器(Adapter)?是否只有量化层符合条件?我能否也将适配器添加到常规(非量化)线性层上?” 这个问题提得很好,我会逐一为你解答。首先,先给出简洁结论: • 主流模型架构会预配置目标层列表,适配器将应用于这些列表中的层。 • 无论目标层…

【内网渗透】CVE-2025-21420 利用cleanmgr本地提权

目录 原理 POC 复现 一个windows本地提权漏洞 这是一个存在于Windows磁盘清理工具&#xff08;cleanmgr.exe&#xff09;中的权限提升漏洞。攻击者可以利用该系统组件在处理特定目录时的逻辑缺陷&#xff0c;通过精心构造的符号链接&#xff08;Symbolic Link&#xff09;&a…

什么是JSON-RPC 2.0,在项目中应该怎么使用

它是什么 JSON-RPC 2.0 是一种超轻量、与传输无关的远程调用协议&#xff1a;用 JSON 表达“方法名 参数 → 结果/错误”。可跑在 HTTP、WebSocket、Unix 管道&#xff0c;甚至 stdio 上&#xff08;很多开发协议如 LSP 就用它&#xff09;。 报文长这样 • 请求&#xff1a; …

基于CentOS7:Linux服务器的初始化流程

文章目录前言一、服务器初始化1.1 配置国内 Yum 源&#xff08;加速软件安装&#xff09;1.1.1 使用阿里云源1.1.2 使用清华源&#xff08;可选&#xff09;1.2 更新系统与安装必备工具1.3 网络连接验证1.4 配置主机名1.5 同步时间1.6 配置iptables防火墙1.6.1 手动配置iptable…