使用 PyTorch 的计算机视觉简介 (2/6)

一、说明

        在本单元中,我们从最简单的图像分类方法开始——一个全连接的神经网络,也称为感知器。我们将回顾一下 PyTorch 中定义神经网络的方式,以及训练算法的工作原理。

二、数据加载的实践

        首先,我们使用 pytorchcv 助手来加载所有数据。

!wget https://raw.githubusercontent.com/MicrosoftDocs/pytorchfundamentals/main/computer-vision-pytorch/pytorchcv.py
import torch
import torch.nn as nn
import torchvision
import matplotlib.pyplot as plt
from torchinfo import summary
import numpy as npfrom pytorchcv import load_mnist, train, plot_results, plot_convolution, display_dataset
load_mnist(batch_size=128)

三、全连接密集神经网络

        PyTorch 中的基本神经网络由许多层组成。最简单的网络将只包含一个完全连接的层,称为线性层,具有 784 个输入(输入图像的每个像素一个输入)和 10 个输出(每个类一个输出)。

        正如我们上面所讨论的,我们的数字图像的尺寸是 1 × 28 × 28,即每个图像包含 28 × 28 = 784 个不同的像素。由于线性层期望其输入为一维向量,因此我们需要在网络中插入另一层,称为 Flatten,以将输入张量形状从
        1 × 28 × 28 更改为 784。在 Flatten 之后,有一个主要的线性层(在 PyTorch 中称为 Dense ),它将 784 个输入转换为 10 个输出——每个类一个。我们希望
网络的 n 个输出返回输入数字等于 n 的概率。

        由于全连接层的输出未归一化为介于 0 和 1 之间,因此不能将其视为概率。此外,如果希望输出是不同数字的概率,它们都需要加起来为 1。为了将输出向量转换为概率向量,称为 Softmax 的函数通常用作分类神经网络中的最后一个激活函数。例如,softmax([−1, 1, 2]) = [0.035, 0.25, 0.705]。

        在 PyTorch 中,我们通常更喜欢使用 LogSoftmax 函数,该函数还将计算输出概率的对数。为了将输出向量转换为实际概率,我们需要获取输出的torch.exp

        因此,我们的网络架构可以在 PyTorch 中使用顺序函数定义:

net = nn.Sequential(nn.Flatten(), nn.Linear(784,10), # 784 inputs, 10 outputsnn.LogSoftmax())

四、如何训练网络

        以这种方式定义的网络可以将任何数字作为输入,并生成概率向量作为输出。让我们看看这个网络是如何表现的,从我们的数据集中给它一个数字:

print('Digit to be predicted: ',data_train[0][1])
torch.exp(net(data_train[0][0]))
Digit to be predicted:  5
tensor([[0.1174, 0.1727, 0.0804, 0.1333, 0.0790, 0.0902, 0.0657, 0.0871, 0.0807,0.0933]], grad_fn=<ExpBackward>)

        因为我们使用 LogSoftmax 作为我们网络的最终激活,所以我们通过 torch.exp 传递网络输出以获得概率。如您所见,网络预测每个数字的相似概率。这是因为它没有接受过如何识别数字的培训。我们需要给它我们的训练数据,以便在我们的数据集上训练它。

        为了训练模型,我们需要从一定大小的数据集创建批次,比如说 64 个。PyTorch 有一个名为 DataLoader 的对象,它可以自动为我们创建成批的数据:

train_loader = torch.utils.data.DataLoader(data_train,batch_size=64)
test_loader = torch.utils.data.DataLoader(data_test,batch_size=64) # we can use larger batch size for testing

        训练过程步骤如下:

  1. 我们从输入数据集中获取一个小批量,该数据集由输入数据(特征)和预期结果(标签)组成。
  2. 我们计算此小批量的预测结果。
  3. 此结果与预期结果之间的差异是使用损失函数计算的。损失函数显示网络的输出与预期输出的差异。我们培训的目标是尽量减少损失。
  4. 我们计算这个损失函数相对于模型权重(参数)的梯度,然后用于调整权重以优化网络的性能。调整量由称为学习率的参数控制,优化算法的详细信息在优化器对象中定义。
  5. 我们重复这些步骤,直到处理整个数据集。通过数据集的一次完整传递称为纪元

        下面是一个执行一个纪元训练的函数:

def train_epoch(net,dataloader,lr=0.01,optimizer=None,loss_fn = nn.NLLLoss()):optimizer = optimizer or torch.optim.Adam(net.parameters(),lr=lr)net.train()total_loss,acc,count = 0,0,0for features,labels in dataloader:optimizer.zero_grad()out = net(features)loss = loss_fn(out,labels) #cross_entropy(out,labels)loss.backward()optimizer.step()total_loss+=loss_,predicted = torch.max(out,1)acc+=(predicted==labels).sum()count+=len(labels)return total_loss.item()/count, acc.item()/counttrain_epoch(net,train_loader)
(0.0059344619750976565, 0.8926833333333334)

        以下是我们在训练时所做的:

  • 将网络切换到训练模式(net.train())
  • 遍历数据集中的所有批次,并为每个批次执行以下操作:
    - 计算网络在此批次上所做的预测(输出)- 计算损失,即预测值和预期值
    之间的差异- 通过调整网络权重来最小化损失 (optimizer.step())- 计算正确预测的事例数(准确性)
     

        该函数计算并返回每个数据项的平均损失和训练准确性(正确猜测的案例百分比)。通过在训练期间观察这种损失,我们可以看到网络是否在改进并从提供的数据中学习。

        控制测试数据集的准确性也很重要,这也称为验证准确性。具有大量参数的良好神经网络可以在任何训练数据集上以相当的准确性进行预测,但它可能很难推广到其他数据。这就是为什么在大多数情况下,我们会留出部分数据,然后定期检查模型在数据上的表现。以下是在测试数据集上评估网络的函数:

def validate(net, dataloader,loss_fn=nn.NLLLoss()):net.eval()count,acc,loss = 0,0,0with torch.no_grad():for features,labels in dataloader:out = net(features)loss += loss_fn(out,labels) pred = torch.max(out,1)[1]acc += (pred==labels).sum()count += len(labels)return loss.item()/count, acc.item()/countvalidate(net,test_loader)
(0.033262069702148435, 0.9496)

        与训练函数类似,该函数计算并返回测试数据集的平均损失和准确性。

五、过拟合

        通常,在训练神经网络时,我们会训练模型几个时期,观察训练和验证的准确性。一开始,训练和验证的准确性都应该提高,因为网络会拾取数据集中的模式。但是,在某些时候,可能会发生训练准确性增加而验证准确性开始降低的情况。这将表明过度拟合,即模型在训练数据集上表现良好,但在新数据上表现不佳。

        下面是可用于执行训练和验证的训练函数。它打印每个纪元的训练和验证精度,并返回可用于在图形上绘制损失和精度的历史记录。

def train(net,train_loader,test_loader,optimizer=None,lr=0.01,epochs=10,loss_fn=nn.NLLLoss()):optimizer = optimizer or torch.optim.Adam(net.parameters(),lr=lr)res = { 'train_loss' : [], 'train_acc': [], 'val_loss': [], 'val_acc': []}for ep in range(epochs):tl,ta = train_epoch(net,train_loader,optimizer=optimizer,lr=lr,loss_fn=loss_fn)vl,va = validate(net,test_loader,loss_fn=loss_fn)print(f"Epoch {ep:2}, Train acc={ta:.3f}, Val acc={va:.3f}, Train loss={tl:.3f}, Val loss={vl:.3f}")res['train_loss'].append(tl)res['train_acc'].append(ta)res['val_loss'].append(vl)res['val_acc'].append(va)return res# Re-initialize the network to start from scratch
net = nn.Sequential(nn.Flatten(), nn.Linear(784,10), # 784 inputs, 10 outputsnn.LogSoftmax())hist = train(net,train_loader,test_loader,epochs=5)
Epoch  0, Train acc=0.892, Val acc=0.893, Train loss=0.006, Val loss=0.006
Epoch  1, Train acc=0.910, Val acc=0.899, Train loss=0.005, Val loss=0.006
Epoch  2, Train acc=0.913, Val acc=0.898, Train loss=0.005, Val loss=0.006
Epoch  3, Train acc=0.915, Val acc=0.897, Train loss=0.005, Val loss=0.006
Epoch  4, Train acc=0.916, Val acc=0.897, Train loss=0.005, Val loss=0.006

        此函数记录消息的准确性来自每个纪元的训练和验证数据。它还将此数据作为字典(称为历史记录)返回。然后,我们可以可视化这些数据,以更好地了解我们的模型训练。

plt.figure(figsize=(15,5))
plt.subplot(121)
plt.plot(hist['train_acc'], label='Training acc')
plt.plot(hist['val_acc'], label='Validation acc')
plt.legend()
plt.subplot(122)
plt.plot(hist['train_loss'], label='Training loss')
plt.plot(hist['val_loss'], label='Validation loss')
plt.legend()

        左图显示训练精度增加(对应于网络学习,以越来越好地分类我们的训练数据),而验证精度开始降低。
        右图显示了训练损失减少(对应于网络性能越来越好),而验证损失增加(对应于网络性能越来越差)。这些图形将指示模型过度拟合。

六、可视化网络权重

        我们网络中的密集层也称为线性,因为它对其输入执行线性变换,可以定义为 y = Wx + b,其中 W 是权重矩阵,b 是偏差。权重矩阵 实际上负责我们的网络可以做什么,即识别数字。
在我们的例子中,它的大小为 784 × 10,因为它为输入图像生成 10 个输出(每个数字一个输出)。

        让我们可视化神经网络的权重,看看它们是什么样子的。当网络比仅一层更复杂时,可能很难像这样可视化结果,因为在复杂的网络中,权重在可视化时没有多大意义。然而,在我们的例子中,权重矩阵W的10个维度中的每一个都对应于单个数字,因此可以可视化以查看数字识别是如何发生的。例如,如果我们想看看我们的数字是否为 0,我们将输入数字乘以 W[0] 并通过 softmax 归一化传递结果以获得答案。

        在下面的代码中,我们将首先将矩阵 W 放入变量weight_tensor。它可以通过调用 net.parameters() 方法(它同时返回 W 和 b)来获取,然后调用 next 以获取两个参数中的第一个。然后我们将遍历每个维度,将其重塑为 28 × 28 大小,然后绘制图。您可以看到 10 个权重张量维度有点类似于它们分类的数字的平均形状:

weight_tensor = next(net.parameters())
fig,ax = plt.subplots(1,10,figsize=(15,4))
for i,x in enumerate(weight_tensor):ax[i].imshow(x.view(28,28).detach())

七、多层感知器

        为了进一步提高准确性,我们可能希望包含一个或多个隐藏层。这里需要注意的重要一点是层之间的非线性激活函数,称为ReLU。深度学习中使用的其他激活函数是sigmoidtanh,但ReLU最常用于计算机视觉,因为它可以快速计算,并且使用其他函数不会带来任何显着的好处。

        这个网络可以在 PyTorch 中使用以下代码定义:

net = nn.Sequential(nn.Flatten(), nn.Linear(784,100),     # 784 inputs, 100 outputsnn.ReLU(),              # Activation Functionnn.Linear(100,10),      # 100 inputs, 10 outputsnn.LogSoftmax(dim=0))summary(net,input_size=(1,28,28))
==========================================================================================
Layer (type:depth-idx)                   Output Shape              Param #
==========================================================================================
├─Flatten: 1-1                           [1, 784]                  --
├─Linear: 1-2                            [1, 100]                  78,500
├─ReLU: 1-3                              [1, 100]                  --
├─Linear: 1-4                            [1, 10]                   1,010
├─LogSoftmax: 1-5                        [1, 10]                   --
==========================================================================================
Total params: 79,510
Trainable params: 79,510
Non-trainable params: 0
Total mult-adds (M): 0.08
==========================================================================================
Input size (MB): 0.00
Forward/backward pass size (MB): 0.00
Params size (MB): 0.32
Estimated Total Size (MB): 0.32
==========================================================================================

        在这里,我们使用 summary() 函数来显示网络的详细逐层结构以及其他一些有用的信息。特别是,我们可以看到:

  • 网络的逐层结构,以及每层的输出大小
  • 每层以及整个网络的参数数量。网络拥有的参数越多,需要训练的数据样本就越多,而不会过度拟合

        让我们看看参数的数量是如何计算的。第一个线性层有 784 个输入和 100 个输出。该层由 W1 × + b 1 定义,其中 W1 的大小为 784 × 100,b 1 - 100。因此,此图层的参数总数为 784 × 100 + 100 = 78500。
同样,第二层的参数数为 100 × 10 + 10 = 1010。激活函数以及展平层没有参数。

        我们可以使用另一种语法来通过使用类来定义相同的网络:

from torch.nn.functional import relu, log_softmaxclass MyNet(nn.Module):def __init__(self):super(MyNet, self).__init__()self.flatten = nn.Flatten()self.hidden = nn.Linear(784,100)self.out = nn.Linear(100,10)def forward(self, x):x = self.flatten(x)x = self.hidden(x)x = relu(x)x = self.out(x)x = log_softmax(x,dim=0)return xnet = MyNet()summary(net,input_size=(1,28,28),device='cpu')
==========================================================================================
Layer (type:depth-idx)                   Output Shape              Param #
==========================================================================================
├─Flatten: 1-1                           [1, 784]                  --
├─Linear: 1-2                            [1, 100]                  78,500
├─Linear: 1-3                            [1, 10]                   1,010
==========================================================================================
Total params: 79,510
Trainable params: 79,510
Non-trainable params: 0
Total mult-adds (M): 0.08
==========================================================================================
Input size (MB): 0.00
Forward/backward pass size (MB): 0.00
Params size (MB): 0.32
Estimated Total Size (MB): 0.32
==========================================================================================

        您可以看到神经网络的结构与顺序网络相同,但定义更明确。我们的自定义神经网络由从 torch.nn.Module 类继承的类表示。

        类定义由两部分组成:

  • 在构造函数 (__init__) 中,我们定义了网络将具有的所有层。这些层存储为类的内部变量,PyTorch 会自动知道这些层的参数在训练时应该优化。在内部,PyTorch 使用 parameters() 方法来查找所有可训练的参数和 nn。模块将自动从所有子模块收集所有可训练参数。
  • 我们定义了对神经网络进行前向传递计算的前方法。在我们的例子中,我们从一个参数张量 x 开始,并显式地将其传递到所有层和激活函数,从展平开始,直到最终的线性层输出。当我们通过写出 = net(x) 将神经网络应用于某些输入数据 x 时,调用正向方法。

        事实上,顺序网络以非常相似的方式表示,它们只是存储一个层列表并在前向传递期间按顺序应用它们。在这里,我们有机会更明确地表示这个过程,这最终给了我们更大的灵活性。这就是使用类进行神经网络定义是推荐和首选做法的原因之一。

        您现在可以尝试使用我们上面定义的完全相同的训练函数来训练此网络:

hist = train(net,train_loader,test_loader,epochs=5)
plot_results(hist)
Epoch  0, Train acc=0.962, Val acc=0.951, Train loss=0.033, Val loss=0.034
Epoch  1, Train acc=0.964, Val acc=0.951, Train loss=0.033, Val loss=0.034
Epoch  2, Train acc=0.964, Val acc=0.954, Train loss=0.033, Val loss=0.033
Epoch  3, Train acc=0.966, Val acc=0.955, Train loss=0.032, Val loss=0.033
Epoch  4, Train acc=0.966, Val acc=0.957, Train loss=0.032, Val loss=0.033

八、小结语

        在 PyTorch 中训练神经网络可以通过训练循环进行编程。这似乎是一个复杂的过程,但在现实生活中我们需要编写一次,然后我们可以稍后重用此训练代码而无需更改它。

        我们可以看到,单层和多层密集神经网络表现出相对不错的性能,但是如果我们尝试将它们应用于真实世界的图像,精度不会太高。在下一个单元中,我们将介绍卷积的概念,这使我们能够获得更好的图像识别性能。

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

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

相关文章

全球首发搭载“舱驾一体”的智能座舱,诺博汽车如何引领未来出行?

智能座舱升级战已经全面打响。 一方面&#xff0c;智能座舱已经进入了3.0时代&#xff0c;车企对于差异化要求越来越高&#xff0c;如何进一步提升单一功能体验并进行深度融合&#xff0c;已经成为了智能座舱市场比拼的重点。 另一方面&#xff0c;在5G、车联网、大数据、人工…

14.抽象工厂模式

UML 代码 #include <iostream> #include <list> using namespace std;class AbstractProductA { public:virtual void showa() 0; }; class ProductA1:public AbstractProductA { public:virtual void showa(){cout << "我是A1" << endl;}…

【线性回归、岭回归、Lasso回归分别预测患者糖尿病病情】数据挖掘实验一

Ⅰ、项目任务要求 任务描述&#xff1a;将“diabetes”糖尿病患者数据集划分为训练集和测试集&#xff0c;利用训练集分别结合线性回归、岭回归、Lasso回归建立预测模型&#xff0c;再利用测试集来预测糖尿病患者病情并验证预测模型的拟合能力。具体任务要求如下&#xff1a; …

[LLM+AIGC] 01.应用篇之中文ChatGPT初探及利用ChatGPT润色论文对比浅析(文心一言 | 讯飞星火)

近年来&#xff0c;人工智能技术火热发展&#xff0c;尤其是OpenAI在2022年11月30日发布ChatGPT聊天机器人程序&#xff0c;其使用了Transformer神经网络架构&#xff08;GPT-3.5&#xff09;&#xff0c;能够基于在预训练阶段所见的模式、统计规律和知识来生成回答&#xff0c…

电脑C盘爆红怎么办?(小白篇)

文章目录 前言&#xff1a;1、清理临时和系统文件2、更改电脑默认软件安装位置3、微信、QQ文件存储路径放在其它盘4、卸载一些不常用的软件彩蛋 前言&#xff1a; C盘作为电脑的系统盘&#xff0c;如果出现爆满或者剩余空间很小整个C盘变红&#xff0c;这样会导致电脑系统运行…

前端项目练习(练习-001-纯原生)

先创建一个空文件夹&#xff0c;名字为web-001,然后用idea开发工具打开&#xff0c;如图&#xff1a; 可以看到&#xff0c;这是个彻底的空项目&#xff0c;创建 index.html index.js index.css三个文件&#xff0c;如图&#xff1a; 其中&#xff0c;html文件内容如下&am…

华为OD机试 - 最小传输时延 - 深度优先搜索DFS(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明计算源节点1到目的节点5&#xff0c;符合要求的时延集合 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&…

时间复杂度、空间复杂度

一、时间复杂度 1、概念 时间复杂度&#xff1a;计算的是当一个问题量级增加的时间&#xff0c;时间增长的趋势&#xff1b; O&#xff08;大O表示法&#xff09;&#xff1a;渐进的时间复杂度 2、举例 ① 以下 for 循环的时间复杂度&#xff1a;O(1 3n) O(n) 去掉常数…

数据结构上机练习——单链表的基本操作、头文件、类定义、main函数、多种链表算法的实现,含注释

文章目录 单链表的基本操作实现1.头文件2.类定义和多种算法的实现2.1创建空表2.2头插法创建n个元素的线性链表2.3一个带头节点的链表存放一组整数&#xff0c;设计一个算法删除值等于x的所有节点。2.4计算线性表中值为偶数的节点个数2.5一个带头节点的单链表heada存放一组整数&…

nginx实现反向代理实例

1 前言 1.1 演示内容 在服务器上访问nginx端口然后跳转到tomcat服务器 1.2 前提条件 前提条件&#xff1a;利用docker安装好nginx、tomcat、jdk8&#xff08;tomcat运行需要jdk环境&#xff09; 只演示docker安装tomcat&#xff1a; 默认拉取最新版tomcat docker pull t…

Qt地铁智慧换乘系统浅学( 三 )最少路径和最少换乘实现

本算法全都基于广度优先 概念最短路径实现所用容器算法思路 最少换乘实现所需容器算法思路 成果展示代码实现判断是最短路径还是最少换乘最短路径代码实现最少换乘代码实现根据所得List画出线路 ui界面的维护&#xff08;前提条件&#xff09;界面初始化combox控件建立槽函数 概…

84、Redis客户端-->可视化图形界面工具(Another Redis Desktop Manager)的下载、安装及初步使用

Redis客户端–>可视化图形界面工具(Another Redis Desktop Manager)的下载、安装及初步使用 ★ Redis客户端&#xff1a; ▲ Redis自带的命令行工具&#xff08;简陋&#xff09;&#xff1a; CLI工具&#xff0c;重新打开一个命令行窗口&#xff0c;在其中输入如下命令&…

人工智能轨道交通行业周刊-第61期(2023.9.18-9.24)

本期关键词&#xff1a;焊线机器人、智能综合运维管理系统、信号平面图、铁路部门架构、书生浦语大模型 1 整理涉及公众号名单 1.1 行业类 RT轨道交通人民铁道世界轨道交通资讯网铁路信号技术交流北京铁路轨道交通网上榜铁路视点ITS World轨道交通联盟VSTR铁路与城市轨道交通…

uniapp、vue实现滑动拼图验证码

uniapp、vue实现滑动拼图验证码 实际开发工作中&#xff0c;在登陆的时候需要短信验证码&#xff0c;但容易引起爬虫行为&#xff0c;需要用到反爬虫验证码&#xff0c;今天介绍一下拼图验证码&#xff0c;解决验证码反爬虫中的滑动验证码反爬虫。滑动拼图验证码是在滑块验证码…

Android应用线上闪退问题解决

解决Android应用线上闪退问题需要仔细的监控、调试和分析。以下是一些解决Android线上闪退问题的工具和方法&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 工具&#xff1a; 1.Google Play 控制台&…

Mysql安装

一、Mysql官网下载安装 MySQL :: Download MySQL Community Server下载 二、安装 下载后打开文件进行安装&#xff1a; 选择custom自定义 选择后带点击Execute 接着下一步 设置密码&#xff0c;下一步 全好后点击finish 之后继续下一步&#xff0c;finish

怎样防止员工泄露技术?(十条避免公司泄密的措施)

在当今信息化社会&#xff0c;公司信息的安全性和保密性显得尤为重要。一旦公司信息泄露&#xff0c;不仅会对公司的经营造成严重影响&#xff0c;还可能引发法律纠纷。因此&#xff0c;采取有效的措施来防止公司信息泄露是非常必要的。以下是一些具体的措施&#xff1a; 部署洞…

数据结构之【泛型】

泛型&#xff1a;定义阶段不明确具体类型&#xff0c;产生对象时明确具体类型。 //Object是Java中的最高参数统一化&#xff0c;能够接受所有的引用类型&#xff1b; //有了包装类的自动拆装箱之后&#xff0c;Object还能够接收基本类型数值&#xff08;自动装箱&#xff09; …

入门级制作电子期刊的网站推荐

随着数字化时代的到来&#xff0c;越来越多的人开始尝试制作自己的电子期刊。如果你也是其中的一员&#xff0c;那么这篇文章可以帮助你制作电子期刊。无论是初学者还是有一定经验的制作者&#xff0c;都能快速完成高质量的电子期刊制作 小编经常使用的工具是-----FLBOOK在线制…

修改vscode底部栏背景和字体颜色

修改vscode底部栏背景和字体颜色 如图&#xff1a; 首先打开齿轮&#xff0c;打开设置搜索workbench.colorCustomizations,然后点击编辑setting.json修改setting.json内内容 "workbench.colorCustomizations": {"statusBar.foreground": "#FFFFFF…