深度学习之torchvision、多层感知器与激活函数

news2025/1/19 13:15:25

文章目录

    • 1 torchvision库与加载内置图片数据集
    • 2 多层感知器
    • 3 激活函数
      • 3.1 ReLU激活函数
      • 3.2 Sigmoid激活函数
      • 3.3 Tanh激活函数
      • 3.4 LeakyReLU激活函数

学习笔记

1 torchvision库与加载内置图片数据集

torchvision库是PyTorch中用来处理图像和视频的一个辅助库,提供了一些常用的数据集、模型、转换函数等。

torchvision库提供的内置数据集可用于测试、学习和创建基准模型,为了统一数据加载和处理代码,PyTorch提供了两个类用以处理数据加载,它们分别是torch.utils.data.Dataset类和torch.utils.data.DataLoader类,通过这两个类可使数据集加载和预处理代码与模型训练代码脱钩,从而获得更好的代码模块化和代码可读性。

torchvision加载的内置图片数据集均继承自torch.utils.data.Dataset类,因此可直接使用加载的内置数据集创建DataLoader。

PyTorch的内置图片数据集均在torchvision.datasets模块下,包含Caltech、CelebA、CIFAR、Cityscapes、COCO、Fashion-MNIST、ImageNet、MNIST等很多著名的数据集。其中MNIST数据集是手写数字数据集,这是一个很适合入门者学习使用的小型计算机视觉数据集,它包含0~9的手写数字图片和每一张图片对应的标签。

下面以此数据集为例学习如何加载使用内置图片数据集,加载内置图片数据集的代码如下:

import torchvision
from torchvision.transforms import ToTensor


train_ds = torchvision.datasets.MNIST('data/', train=True, transform=ToTensor(), download=True)
test_ds = torchvision.datasets.MNIST('data/', train=False, transform=ToTensor(), download=True)

上述代码中,首先导入了torchvision库,并从torchvision.transforms模块下导入ToTensor类。

torchvision.transforms模块包含了转换函数,使用它可以很方便地对加载的图像做各种变换。

在这里我们用到了ToTensor类,该类的主要作用有以下3点。

  • (1) 将输入转换为张量。

  • (2) 将读取图片的格式规范为(channel,height,width),这与我们以前经常遇到的图片格式可能有些区别,PyTorch中的图片格式一般是通道数(channel)在前,然后是高度(height)和宽度(width)。

  • (3) 将图片像素的取值范围归一化,规范为0~1。

上述加载代码中,通过torchvision.datasets.MNIST方法加载MNIST数据集,
方法中的第一个参数data/表示下载数据集存放的位置,这里放在了当前程序目录下的data文件夹中;

参数train表示是否是训练数据,若为True,则加载训练数据集,若为False,则加载测试数据集;

使用参数transform表示对加载数据的预处理,参数值为ToTensor();

最后一个参数download=True表示将下载此数据集,一旦下载完成后,下一次执行此代码时,将优先从本地文件夹直接加载。

现在我们得到了两个数据集,分别是训练数据集和测试数据集,PyTorch还提供了torch.utils.data.DataLoader类用以对数据集做进一步的处理,DataLoader接收数据集,并执行复杂的操作,如小批次处理、多线程、随机打乱等,以便从数据集中获取数据。它接收来自用户的Dataset实例,并使用采样器策略将数据采样为小批次。

DataLoader主要有以下4个目的。

  • (1)使用shuffle参数对数据集做乱序的操作

​ 一般情况下,需要对训练数据集进行乱序的操作。因为原始的数据在样本均衡的情况下可能是按照某种顺序进行排列的,如数据集的前半部分为某一类别的数据,后半部分为另一类别的数据。但经过打乱顺序之后,数据的排列就会拥有一定的随机性,在顺序读取的情况下,读取一次得到的样本为任何一种类型数据的可能性相同。这样可避免出现模型反复依次序学习数据的特征或者学习到的只是数据的次序特征的情况。

  • (2)将数据采样为小批次,可用batch_size参数指定批次大小

​ 若同时对输入和标签迭代送入模型进行训练,这样单个样本训练有一个很大的缺点,就是损失和梯度会受到单个样本的影响,如果样本分布不均匀,或者有错误标注样本,则会引起梯度的巨大震荡,从而导致模型训练效果很差。为了解决此问题,可考虑使用批量数据训练(也叫作批量梯度下降算法),通过遍历全部数据集算一次损失函数,然后计算损失对各个参数的梯度,并更新参数。这种训练方式每更新一次,参数都要把数据集里的所有样本都看一遍,不仅计算开销大,而且计算速度慢。

​ 为了克服上述两种方法的缺点,一般采用的是一种折中手段进行损失函数计算:即把数据分为若干个小的批次(batch),按批次来更新参数,这样,一个批次中的一组数据共同决定了本次梯度的方向,大大降低了参数更新时的梯度方差,下降起来更加稳定,减少了随机性。与单样本训练相比,小批次训练可利用矩阵操作进行有效的梯度计算,计算量也不是很大,对计算机内存的要求也不高。

  • (3)可以充分利用多个子进程加速数据预处理, num_workers参数可以指定子进程的数量

  • (4)可通过collate_fn参数传递批次数据的处理函数,实现在DataLoader中对批次数据做转换处理

train_dl = torch.utils.data.DataLoader(train_ds, batch_size=64,shuffle=True)
test_dl = torch.utils.data.DataLoader(test_ds, batch_size=46)

上述代码中分别创建了训练数据和测试数据的DataLoader,并设置它们的批次大小为64,对训练数据设置了shuffle为True;

对测试数据,由于仅仅作为测试,没必要做乱序。

DataLoader是可迭代对象,我们观察它返回的数据集的形状,以方便对DataLoader和MNIST数据集有一个直观的印象,代码如下:

imgs,labels = next(iter(train_dl))# 创建生成器,并用next方法返回一个批次数据
print(imgs.shape)  # 输出torch.Size([64, 1, 28, 28])
print(labels.shape) # 输出torch.Size([64])

上述代码中使用iter方法将DataLoader对象创建为生成器,并使用next方法返回了一个批次的图像(imgs)和对应的一个批次的标签(labels),imgs.shape为torch.Size([64,1, 28, 28]),如何理解这个shape呢?

很显然,这里的64是批次,我们可以认为这代表64张形状为(1, 28, 28)的图片,其中1为通道数,28和28分别表示高和宽;既然这里有64张图片,对应的也应该有64个标签,也就是labels.shape所显示的torch.Size([64])。

下面通过绘图来看一下MNIST数据集中的这些图片是什么样子的。使用Matplotlib库绘图,绘制imgs中的前10张图片,代码如下:

在这里插入图片描述

2 多层感知器

单个的神经元所完成的任务就是对所有输入的特征乘以权重(w)、加上偏置(b)。

例如,输入有3个特征,分别为x1、x2、x3,那么线性回归模型所做的就是对这些输入特征乘以权重(w)、加上偏置(b),使用激活函数(Activation)激活后,得到输出(Prediction),公式如下:
P r e d i c t i o n = A c t i v a t i o n ( w 1 × x 1 + w 2 × x 2 + w 3 × x 3 + b ) Prediction=Activation(w1×x1+w2×x2+w3×x3+b) Prediction=Activation(w1×x1+w2×x2+w3×x3+b)
当然,在回归问题上并不需要激活函数,或Activation相当于将计算结果直接传递输出。
上面这个公式可看作一个多变量的线性回归模型,一旦通过训练得到了模型的参数值(也就是w1、w2、w3和b的值),就可以使用这个模型做多变量的预测,只需要将参数代入上面的公式即可。

总结单个神经元的一般结构如图所示:
在这里插入图片描述

这里神经元是单层的,也就是说,只有一层计算就输出了结果,没有什么深度。

那什么是深度学习呢?所谓深度学习就是使用更深层的网络来学习,而不是只有一层。
这种只有一层的模型,我们叫作浅层学习,经典机器学习中经常使用这种网络。
浅层学习有一个缺陷,也就是单层神经元的一个缺陷,它无法拟合异或运算,异或运算看上去非常简单,相同为0相异为1。

但是看似简单,使用单个的神经元的缺陷却没有办法解决。单个神经元要求数据必须是线性可分的,异或问题无法找到一条直线分割这两个类,这个问题使得神经网络的发展停滞了很多年。当然,对于无法线性可分的问题,在经典机器学习中可以通过将特征映射到高维空间来实现线性可分。

为了解决此问题,人们提出了使用多层神经元创建模型。多层神经元互相连接,使得模型的深度大大增加,这也是深度学习这个名字的由来。多层神经元互相连接一般称为多层感知器或者神经网络。那为什么叫它神经网络呢?人们解决拟合异或问题,其实受到了生物神经元的启发。生物大脑中的神经元是互相连接的,构成的神经网络彼此连接变得非常的深,当一个信号传递到某一个神经元,如果信号达到了一定的强度,此神经元会被激活,将信号往下传递;如果传递过来的信号没有激活此神经元,这个信号就不再往下传递。这便是大脑中神经元大致的工作原理,它有以下两个特点:

(1)互相连接,网络非常深。

(2)对信号有一个判断或激活。

深度学习或神经网络的工作方式与生物大脑神经元的这种传递的特点类似,为了继续使用神经网络拟合异或问题或者解决各种不具备线性可分的问题,人们想出了一个方法,就是在神经网络的输入和输出之间插入更多的神经元。如下图所示为多层感知器的图示:

在这里插入图片描述

输入层(input)和输出层(output)之间插入了一个隐藏层(hidden),这样使得网络层数变深,我们称它为多层感知器。在多层感知器这个网络结构中,每一层之间的所有单元均互相连接,每一个单元均与前一层所有单元连接,所以我们也称这样的网络为全连接网络。多层感知器的显著特点是包含多层神经元,上图中只有一个隐藏层,实际上可以添加更多的层,层越多,模型的拟合能力会越大。

3 激活函数

​ 首先,为什么要激活?激活也是模拟了上面提到的生物大脑中神经元的工作原理,大脑中神经元会对接收到的信号进行考察,如果此信号满足一定条件,神经元才会将信号往下输出,反之就不往下传递了。激活函数的作用也类似,激活会为网络带来非线性,使得网络可以拟合非线性问题等更多复杂问题,大大增强网络的拟合能力。假如没有激活函数,无论输入与输出之间插入多少层,整个网络仍然是一个线性网络,它还是只能拟合线性问题。

总结起来,激活函数为网络带来了非线性,使得网络可以拟合各种各样复杂的问题。神经网络中常见的激活函数有ReLU、Sigmoid、Tanh、LeakyReLU等,激活函数也可以自定义,只要一个函数是线性可导的,就可以拿来作为激活函数。下面介绍几个常见的。

3.1 ReLU激活函数

​ ReLU也叫修正线性单元,是目前应用最多的一个激活函数,在当前大部分模型中,都使用ReLU函数作为激活函数,其公式如下:
f ( x ) = m a x ( x , 0 ) f(x)=max(x,0) f(x)=max(x,0)
ReLU激活函数图像如下图所示:

在这里插入图片描述

ReLU激活函数的特点是,如果一个输入是大于0的,将其往后传递,反之则输出0,ReLU激活函数在实际应用中几乎是当前最受欢迎的激活函数,它是大多数神经网络的默认选择。在PyTorch中有内置的torch.relu()方法用来实现ReLU激活,代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5NWfJDFK-1682583811608)(pytorch深度学习.assets/image-20230427120744784.png)]

代码中可以看到如果输入中有小于0的元素,输出会被置为0。

3.2 Sigmoid激活函数

​ Sigmoid激活函数是神经网络早期最常用的激活函数,其公式如下:
S i g m o i d ( x ) = 1 / ( 1 + e − x ) Sigmoid(x)=1/(1+e-x) Sigmoid(x)=1/(1+ex)
Sigmoid激活函数图像如下图所示:

在这里插入图片描述

从上图中可以看到,Sigmoid函数的输出为0~1,其在输入0附近的曲线斜率比较大,在远离0的部分的曲线斜率接近于0。

Sigmoid函数也常作为逻辑回归问题的输出函数,这时Sigmoid的输出可以认为是一个是或否的概率值,常用来解决二分类问题。如果对经典机器学习中的逻辑回归有所了解的话,就会发现,逻辑回归模型的输出层就使用了Sigmoid函数。

Sigmoid函数在深度学习的早期是最常用的中间层激活函数,但是后来逐渐被ReLU函数所取代,这是因为Sigmoid函数在远离0这个中心点的区域几乎接近于水平线,会导致梯度的大大衰减,甚至发生梯度消失,使得模型不容易训练。

在PyTorch中有内置的torch.sigmoid()方法用来实现Sigmoid激活,代码如下:

input = torch.randn(2)
output = torch.sigmoid(input) #使用Sigmoid函数激活,输出为(0,1)
print(output)  # 类似 tensor([0.4569, 0.5355])

3.3 Tanh激活函数

​ Tanh激活函数,又叫作双曲正切激活函数,其公式如下:
T a n h ( x ) = ( e x + e − x ) / ( e x − e − x ) Tanh(x)=(ex+e-x)/(ex-e-x) Tanh(x)=(ex+ex)/(exex)
Tanh激活函数的图像如下图所示:

在这里插入图片描述

在形状上,Tanh激活函数类似Sigmoid激活函数,但是它的中心位置是0,其输出为(−1, 1)。

Tanh激活函数的一个常见的使用场景是对生成模型的输出做激活,从而使得输出规范为(−1, 1)。

PyTorch的内置函数torch.tanh()用来实现双曲正切激活函数。

input = torch.randn(2)
output = torch.tanh(input)   # 对输入使用Tanh函数激活,输出为(-1, 1)
print(output)  # 输出类似tensor([ 0.5529, -0.9574])

3.4 LeakyReLU激活函数

LeakyReLU激活函数(带泄漏单元的ReLU)的数学表达式如下:
y = m a x ( 0 , x ) + l e a k × m i n ( 0 , x ) y=max(0,x)+leak×min(0,x) y=max(0,x)+leak×min(0,x)
LeakyReLU激活函数的图像如下图所示:

在这里插入图片描述

ReLU激活函数将所有的负值都设为零不同,LeakyReLU激活函数给所有负值赋予一个小的非零斜率leak,这样保留了一些负轴的值,使得负轴的信息不会全部丢失。

实际中,LeakyReLU激活函数中的leak取值一般比较小,使用LeakyReLU函数激活后,在反向传播过程中,LeakyReLU激活函数输入小于零的部分,也可以计算得到梯度,这有利于一些情况下的模型训练,例如生成器模型。

PyTorch中有内置的LeakyReLU实现,代码如下:

#初始化 LeakyReLU,参数表示负轴部分的斜率
m = nn.LeakyReLU(0.1)
input = torch.randn(2)
output = m(input)#使用LeakyReLU函数对输入激活
print(m)
print(input)
print(output)

'''
LeakyReLU(negative_slope=0.1)
tensor([-0.0633,  0.7387])
tensor([-0.0063,  0.7387])
'''

Leaky ReLU函数(ReLU的改进)

1、与ReLU函数相比,把x的非常小的线性分量给予负输入(0.01x)来调整负值的零梯度问题;有助于扩大 ReLU 函数的范围,通常𝜆λ的值为 0.01 左右;函数范围是负无穷到正无穷。

2、LeakyRelu激活函数通过在负半轴添加一个小的正斜率(使得负轴的信息不会全部丢失)来解决ReLU激活函数的“死区”问题,该斜率参数𝜆λ是手动设置的超参数,一般设置为0.01。通过这种方式,LeakyRelu激活函数可以确保模型训练过程中神经元的权重在输入小于0的情况下依然会得到更新。

3、不会出现 Dead ReLu 问题,但是关于输入函数f(x) 的部分容易出现梯度爆炸的情况是一样的,所以必要时也可以搭配 sigmoid 或 tanh 使用。

Leaky ReLU不足

1、经典(以及广泛使用的)ReLU 激活函数的变体,带泄露修正线性单元(Leaky ReLU)的输出对负值输入有很小的坡度。由于导数总是不为零,这能减少静默神经元的出现,允许基于梯度的学习(虽然会很慢)。

2、从理论上讲,Leaky ReLU 具有 ReLU 的所有优点,而且 Dead ReLU 不会有任何问题,但在实际操作中,尚未完全证明 Leaky ReLU 总是比 ReLU 更好。

还有很多比较著名的激活函数,如ELU、ReLU6、PReLU、SELU等。

关于激活函数可参考链接自行了解:常用的激活函数合集
pytorch学习推荐日月光华老师的pytorch免费课程:pytorch深度学习与简明教程

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

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

相关文章

Docker部署文本语义检索系统

Docker 部署 pipelines服务 需求:基于docker部署百度飞桨公开项目pipelines服务之 端到端文本语义检索系统 项目链接:https://aistudio.baidu.com/aistudio/projectdetail/3351784?channelType0&channel0 代码地址:https://gitee.com/pa…

03 Kubernetes 系统快速入门

课件 Kubernetes 中的 ownerReference 字段用于建立资源之间的所有者关系,即父子关系。这个字段可以用来指定一个资源是另一个资源的所有者。例如,一个 Deployment 可以拥有多个 Pod,这些 Pod 就可以通过 ownerReference 字段引用到对应的 De…

D. Maximum Distance(最小生成树)

Problem - D - Codeforces Chouti已经厌倦了乏味的作业,于是他打开了数年前创建的一个旧编程问题。 给定一个具有n个节点和m条加权边的连通无向图。其中有k个特殊节点:x1,x2,...,xk。 现在定义路径的成本为其边权的最大值。两个顶点之间的距离定义为连…

从C出发 29 --- 指针与函数

只有知道具体位置,才有可能跳到那个位置去执行,如果不知道在哪里,怎么跳? 函数是什么? 函数就是一片连续的内存 数组是什么? 数组就是一片连续的内存 很显然,这一片连续的内存当中&#xff0c…

百度智能云六大产品系列将率先基于大模型升级,加速全面智能化

百度智能云在上海召开文心一言技术交流会。百度集团副总裁袁佛玉表示,大模型的技术突破,使得全球性的“AI再造”已经拉开序幕。在技术底座层面,百度智能云拥有全栈自研的AI大底座;在大模型平台层面,拥有全球首个一站式…

OSGI详解

最近项目用到了OSGI,第一反应就是什么是OSGI? OSGI是一个为Java提供动态模块化的系统; 准确的说,OSGI是一个标准,一个框架,也可以理解为一个容器,具体的实现有Eclipse下的Equinox和Appach下的Fe…

18. Unity - 2D游戏开发小记01 --- 瓦片地图搭建基本操作

1. 瓦片地图创建(Tilemap) 在2D游戏开发中,瓦片地图就是游戏中的场景或者说游戏背景,可以使用把图片资源制作成Tiles,然后在游戏场景中搭建2D游戏的背景即可。 效果展示: 规则瓦片制作 首先在 Hierarchy层级窗口中右键鼠标,依次选择 2D Object → Tilemap → Rectan…

SpirngMVC的创建和使用

SpirngMVC的创建 SpringMVC的创建很容易。 步骤:创建SpringBoot项目,并在添加依赖时,添加Spring Web。 SpringMVC的创建就完成了。 创建SpringBoot项目详情:SpringBoot项目创建和使用_追梦不止~的博客-CSDN博客 S…

lua元表、元方法

lua元表、元方法 lua官方参考手册:https://www.runoob.com/manual/lua53doc/manual.html#2.4 一、总结: ☺ 1、普通的表,找不到了,或者无法进行运算的时候,考虑设置到它身上的元表的元方法 2、元表的本质&#xff1a…

JavaWeb03(域对象EL表达式JSTL标签)

目录 一.jsp内置对象之域对象 1.1 什么是jsp的内置对象? JSP的内置对象是指在JSP页面系统中已经默认内置的Java对象,这些对象不需要开发人员显式声明即可使用。一共有9个: 分别为request、response、session、application、out、pageContext、confi…

netfilter filter表(三)

修改《netfilter filter表(二)》的hello_open函数,将ipt_entry的信息打印处理,代码如下: char* get_verdict(int verdict) {verdict -(verdict 1);char* p "";switch (verdict){case NF_DROP:p "NF_DROP";break;cas…

【LeetCode训练营】反转链表 移除链表元素 详细图解 203,206

💌 博客内容:LeetCode 训练营 😀 作  者:陈大大陈 🚀 个人简介:一个正在努力学技术的准前端,专注基础和实战分享 ,欢迎私信! 💖 欢迎大家:这…

SpringMVC实现文件上传和下载和Json的简单实用

目录 SpringMVC实现文件上传和下载 1、文件下载 2、文件上传 (1)文件上传操作的分析 (2)实现步骤 (3)解决文件重名覆盖问题 Json的简单使用 1、Json概述 2、Json的语法格式 3、Jackson的QuickStart SpringMVC…

【C++STL】map

文章目录 一. map的介绍二. map的使用结束语 一. map的介绍 map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素在map中,键值key通常用于排序和唯一地标识元素,而value中存储与此键值…

OSI七层模型、TCP/IP四层模型

OSI七层模型和TCP/IP四层模型 OSI七层模型 物理层:底层数据传输,如网线、网卡标准数据链路层:定义数据基本格式,如何传输如何标识;如网卡MAC地址网络层:定义IP地址,定义路由功能;如…

【中级软件设计师】—(针对上午题)二十三种设计模式(三十九)

【中级软件设计师】—(针对上午题)二十三种设计模式(三十九) 一、设计模式的分类 背背背 1 二、创建型设计模式(5种) 三、简单工厂模式 简单工厂模式代码实现如下: /*** author:Ca…

CentOS分区挂载 fdisk、parted方式解析

1 介绍 在linux中,通常会将持久化数据保存到硬盘当中,但是硬盘一把会比较大,因此我们为了方便管理,会将一个硬盘分成多个逻辑硬盘,称之为分区。 为了能够让分区中的文件使得能让操作系统处理,则需要对分区…

Java阶段二Day11

Java阶段二Day11 文章目录 Java阶段二Day11DQL主键与外键外键约束多对多关系内连接外连接自连接 JDBC核心接口JDBC连接的基本流程 教师总结主键与外键外键约束结论:关联关系中通常不适用外键约束。 多对多关系多对多关联查询例 练习题干答案 内连接语法例 外连接例 自连接定义场…

研报精选230427

目录 【行业230427东方金诚】有色金属行业信用风险回顾与2023年展望 【行业230427头豹研究院】2023年中国汽车安全气囊行业词条报告 【行业230427头豹研究院】2023年中国钠离子负极材料行业词条报告 【个股230427开源证券_佳禾食品】公司首次覆盖报告:植脂末龙头再启…

小匠物联出席宁波第五届家电产业数字化对接会助推家电产业数智化

宁波的家电资源与市场充足,特别是小家电,涉及20多个细分行业、近千个品种,其中10多个细分行业小家电产量一直位居全国或全球首列。但受原材料上涨等多重因素的影响,作为宁波市传统优势产业的家电产业也未能避免,产业数…