这里写目录标题
环境配置和安装
anaconda
简介
这是一个python库的集合,在python中,库也叫做包,而anaconda中包含了大部分我们训练模型所需要的包
可以使用miniconda+pip 代替anaconda
虚拟环境
使用该命令可以创建出一个虚拟环境,-n后指定虚拟环境的名字,并且创建时还可以指定在该环境中安装python的哪个版本(当然也可以指定其他的包)
可以使用$后面的命令。激活进入该虚拟环境
安装pytorch
安装网站:https://pytorch.org/
之后根据自己的电脑,选择对应的版本:
Linux下推荐选择pip,而对于第四行的选择,可以去查一下当前电脑的独显是否支持CUDA,且要满足下图要求(Driver版本要>396.26)
可以使用清华源(需要conda config --add channels来指定) + navida源(直接在安装命令后面跟上-c nvidia)
之所以要使用navida源,是因为我们下载的是带独显的pytorch,所以,要使用-c nvidia
pycharm
创建项目时
找到condabin/conda.bat
在下面选择我们的虚拟环境
在终端键入:
import torch(检查torch是否可以检测到)
torch.cuda.is_available()(检查是否可以使用显卡加速)
jupyter
在虚拟环境中,先激活虚拟环境
之后,使用pip安装jupyter notebook
https://www.cnblogs.com/rolandhe/p/18193602
然后参考上面的链接,将虚拟环境配置到jupyter中,即可在jupyter中选择虚拟环境的编译器以及环境
两个操作库的API
介绍
首先我们知道,一个package就是一个包,或者一个库,而基础的对这些库的操作,有两个API
1:dir()
传入一个库名
他的功能是打开一个库,并且输出这个库里面下一级子目录的内容
2:help()
传入库中的某个API
他的功能就是提供库中API的帮助文档
总结:
实操
dir
我们使用dir打开并查看了torch
在输出中找到了cuda
那么我们可以继续dir下一级,我们dir(torch.cuda)
会输出cuda下面的内容
而我们继续dir(torch.cuda.is_available)时,会发现输出的内容前后都有"__",这个表示该元素是不可修改的
同时也说明,is_available是一个API,不是文件夹了,所以,无法继续向下dir了
help
(接上一节)这时,我们就可以使用help了
注意,对API使用help时,不需要带上(),因为我们不是调用,所以使用函数名即可
会输出该API的一些解释
IDE的对比
jupyter可以以任意块为单位进行运行,这是非常灵活的,既然是任意块,那么jupyter也可以是单行为单位进行运行
而对于大型项目,还是使用python文件为主
关于数据
两个对数据处理的类(Dataset类)/库(Transforms库)
简介
数据集
组织形式1:
每个文件夹的名称是一种label,里面存放这种label的数据
组织形式2:
一个文件夹放入所有的数据
另一个文件夹,对应每个数据,存放一个label描述
也就是,这两个文件夹里的内容是一一对应的
组织形式3:
数据的命名用label命名
Dataset类
简介
这个类主要是提供API去获取数据以及label(标签)
他提供的API:
获取每一个数据以及其label
获取数据集中的数据总数
使用jupyter来查看某个类或者API的帮助文档,这里使用jupyter查看一下Dataset的介绍
在jupyter中运行help(Dataset),或者"Dataset??"
Dataset是一个抽象类,使用时要继承他,并重写getitem方法,用来获取一个数据及其标签
也可以选择性的重写len,用于获取数据集的数据个数
代码
这里不仅用到了Dataset类,还用到了Image类,以及一个os库
首先定义一个类,继承了Dataset
之后是构造函数,这里传入参数一:进入数据集文件夹的路径
参数二:label(最深一级文件夹名字)
构造函数中,拿到root_dir、以及label_dir
之后使用os.path.join,将两个路径拼接,这个函数会根据系统进行路径的拼接(根据测试,在python中,在win环境下,路径可以是/ // \进行拼接,但是不能是\)
最后使用os.listdir,可以将数据集的文件名(文件名+后缀)形成一个列表
之后是重写的 __getitem__函数,该函数无需手动调用,当使用对象[ ]时,相当于调用了该函数
在该函数中,先是根据列表拿到传来的索引所指向的文件的文件名
之后,使用os.path.join将完整的相对路径拼出来
之后,使用Image.open,将图片打开,存到变量img中(相当于拿到其文件描述符)
而label则是self.laber_dir,之前在构造函数被赋值进来的
最后重写__len__函数,返回列表的长度即可
调用时,先将路径传入,会创建出Dataset实例(这个对象是一个集合,其中每个位置都是一个二元组“打开的数据或者说文件描述符、label”)之后,拿到实例之后,使用[idx]来调用__getitem__函数,返回一个二元组,我们使用二元组接收,然后可以分别操作img文件描述符、label
这里我们操作img文件描述符,让其show
而我们还可以将两个最后的“数据集及其标签”组,合二为一:
最终,这个train_dataset就是ants_dataset拼接上bees_dataset的内容
(该结论由下图得出)
补充:修改数据集为“组织形式2”:
最终效果:
label中txt文件与image中的数据一一对应,且文件名相同,只是后缀不同,在各个数据对应的txt文件中,写着数据的label
补充:Tensorboard库
add_scalar方法(常用来绘制loss表)
目的:
我们要绘制上图右边的一个图表
代码:
在学习下一个类之前,我们先学习一个库:tensorboard
首先来看该库里面的SummaryWriter类,这个类的构造函数可以传入一个参数,这个参数一个指定路径(如果指定放在当前文件夹,那么直接传入文件名即可),指定最终的产出路径
之后使用该类的add_scalar方法,该方法第一个参数是设置图表的标题,第二个参数是y轴的数据(用value表示),第三个参数是x轴的数据(用step表示)
再配合for循环,即可绘制上图右图
最后使用完毕要进行close
效果:
运行之后我们可以发现,在我们指定的路径上,出现了产出文件,这个文件称为“事件文件”
打开该事件文件:
可以使用pycharm的终端(注意是终端而不是控制台,使用pycharm的终端会自动激活虚拟环境,且将工作目录切到当前项目目录),输入以上命令,即可打开事件文件
当然,为了避免好多人在用一台服务器时,端口冲突的情况,我们可以自己手动指定端口
我们还可以设置y=2x、y=3x,其中,参数一要改成对应的标题,参数二和参数三也要对应关系
此时再运行py文件,之后在终端使用命令打开事件文件,因为有三个事件文件,所以会显示三个图表:
ps:如果出现某些图表合二为一,也就是其自动进行了拟合,我们可以将其事件文件单独放到一个新的文件夹中去单独运行该文件夹,(可以直接剪切或者使用py文件,改动其中的产出路径),这样没有其他事件文件的干扰,就不会自动拟合
或者,我们设置不同的标题名,这样,同一个标题的事件文件会在同一个区域单独运行,不会受到不相干的事件文件的干扰(推荐)
add_image方法(常用来观察训练结果)
代码:
在编译器中可以使用跳转,来查看定义
可以看到,该方法第二个参数对于打开的数据类型有要求
而使用之前的方法,用Image类的话,拿到的文件描述符不是对应的类型,一种方式是,我们使用OpenCV读取图片(拿到其文件描述符),因为OpenCV默认返回是numpy类型
还有另外一种方式:
导入numpy,起个别名:np
使用该类的array方法,将img(打开的图片数据,或者理解为文件描述符),传入,即可转为numpy类型
运行代码:
(从第6行开始)
首先拿到一个图片的路径
之后使用Image.open打开
然后将其返回值使用numpy.array转为numpy类型(注意,只要是numpy打头的即可,其后面.跟的是什么无所谓)
但是,这样直接传入add_image是会报错的
我们还要打印一下img_array的shape,结果,其是HWC,但是add_image默认是CHW,所以,我们需要传入dataformats='HWC’来指定其shape(这个shape信息可以使用ctrl跳转到函数内部查看),这个是PIL到numpy特有的步骤
参数一是标题,参数三是步骤编号,用于在UI界面中区分同一个标题下的不同的图片
(也就是如果运行出来的两个事件文件在使用同一个标题,那么要用step区分,如果两张图不是同一个标题,那么UI界面会显示两块区域给到两个标题)
效果:
这个图片也会产生一个事件文件
同样的,使用tensorboard --logdir=logs --port=6007
即可查看所打开的图片
同一个标题下,占用同一块区域,以不同的step区分,上图上面的区域右上角可以进行step的选择,也就可以进行同标题不同图片的选择
不同的标题,分为不同的区域
Transforms库
其中,totensor和resize都是该库中的一个类,具体的API集成在这些类的call方法里,而真正使用时,call可以理解为(),即括号调用、括号呼叫
totensor类
如何使用Transforms来获取tensor类型的数据:
首先使用Transforms取出其中一个类,并进行构造,创建出一个实例
之后该实例调用()方法(定义中写的是call方法,而()就是调用,呼叫的作用),传入PIL Image 、numpy类型的图片,会返回一个tensor类型的数据
补充:
对于使用cv2打开的数据来说,其默认是numpy.ndarray类型,也可以使用该类转为tensor
为什么要获取tensor类型的数据:
我们将代码放到控制台跑一遍,可以看到tensor类型的数据,他实际上就是一个包装了“反馈神经网络”所需的理论数据参数
使用上一节学的add_image打开tensor类型的image:
还是利用SummaryWriter,指定输出路径,然后调用add_image,将图片打开,使用命令tensorboard --logdir=logs --port=6007展示到UI界面
Normalize类
代码:
该类的用法与totensor一样,同样是先构造对象,之后,对象调用“()”方法
构造函数:传入均值和标准差(由于我们是 图片数据,所以传入三元数组,一个三元数组代表RGB的数据)
而对象的"()"传入原先的tensor类型数据,返回一个归一化之后的tensor类型的数据
我们可以使用add_image来打开这个归一化之后的图片
并使用命令tensorboard --logdir=logs/logs_trans --port=6007进行UI展示
效果:
上面是归一化之后的数据展示,下面是归一化之前的
Compose类
Compose是一系列操作的集合,他可以将transforms库中的多个变换操作整合到一起
使用:
首先,创建Compose实例,传入一个数组,数组中是依次要执行的操作的实例,特别注意,因为数组中的操作会顺序执行,所有要保证他们输入输出的连贯性,即上一个操作的输出类型可以用于下一个操作的输入
最后,直接将原始数据传入Compose实例的调用中即可,其内部会依次按照Compose构造时的数组中对象的顺序对该原始数据进行调用操作
Resize类
Resize是自由控制大小,而Compose + Resize则是进行等比例缩放
对于Resize:
首先还是使用Transforms.Resize(),参数传入长和宽
之后对象调用"()",传入PIL类型的数据,最终返回的也是PIL类型
至此,Resize结束,而想要显示在Tensorboard的UI界面上,则要利用totensor先转为tensor类型,然后使用add_image+UI命令
对于Resize+Compose:
他需要Resize的配合,首先,使用创建一个Resize对象,参数传入一个参数(表示要把图片的宽设置为多大,“长”会等比例放大)
之后,创建Compose对象,传参:数组:[ 刚刚的Resize对象,无参构造的Totensor对象 ]
(Compose构造函数传参时,要传入transfroms库中的类的对象所组成的数组,且第一个代表输入,第二个代表输出,也就是转换的一个列表)
之后该Composed对象调用"()",传入PIL类型的数据
最终会返回一个缩放之后的tensor类型的数据
可以直接使用writer对象add_image到Tensorboard的UI界面
torchvision中的数据集的使用
介绍
在pytorch官网中提供了一些标准的数据集,以及获取这些数据集的API,我们进入torchvision类别
里面会有许多个模块:
Dataset模块:
里面根据不同的应用场景,分为了不同的类别:
其中有图像检测或分割,常用的有MS Coco数据集:
包括图像分类的数据集,常用的有CIFAR10:
使用
以CFAIR10为例:
首先导入torchvision
之后,调出想要使用的dataset的名字,然后传参,这里原本的参数有很多,但是有好多是设置了默认值,我们传参时可以加上形参的名字=“实参”,这样,不用再根据形参的个数来定位是哪个参数了,可以省去中间的那些默认参数的传入(当然默认参数也可以传值,只是说我们不需要的时候,这样的方法不用再传默认参数的值)
参数:
1:指定输出路径(创建文件夹)
2:指定该数据集是训练数据集还是测试数据集(他们的区别是从库中拿到的图片不同,以及拿到图片的数量也不同)
3:是否自动下载(如果指定的输出路径下没有该数据集,那么该参数必须为True,因为指定为False,他不会下载,但是会去验证数据集在不在,而指定为True,他也会去验证数据集在不在,但是他会先下载后验证。所以,如果事先有数据集那么是True还是False无所谓,要是没有,那么必须是True)
启动之后,控制台会出现下载链接,如果第三个参数为true,那么会自动下载
当然如果嫌控制台下载的太慢,也可以复制链接到迅雷下载
下载完成之后将数据集移动到指定路径,此时根据上面参数3的描述,是True是False无所谓了
而返回的test_set里的内容,跟我们的自定义数据集很类似:(图片+ label)
首先,返回的数据集,是许多图片的“二元信息”的集合
每个图片都对应一个二元信息,这个“二元信息”,其中一个是图片本身(已经打开的图片的文件描述符)
另一个是一个target,target配合返回的数据集的classes属性,可以得到这个图片的类别(或者说label)
上图第9行,是得到test_set数据集的第一个二元组,拿两个变量去接住,然后分别打印或者使用
综合使用:
首先,创建一个Compose对象,操作是将PIL类型图片转为tensor
之后,在调取数据集时的API中,就可以将该Compose对象当做参数传入,这样的话,即使最后返回的数据集集合中每个元素不止PIL这一个信息,那么也会将其转换,target的存在并不影响
之后,创建一个writer
通过for循环,将数据集中前十个图片进行add_image,由于在调用datasetAPI时已经完成了转换,那么现在数据集中的第一元已经是tensor型图片了,可以直接add_image,最后要进行关闭,之后使用命令行将其展示在UI界面即可
补充
ctrl点进去某个数据集(例如此处进入CRIFA10),往上翻找,可以找到下载链接URL
dataloader的使用
简介
之前我们所说的dataset,就是数据集,他是数据的集合
这里我们学习dataloader,他就是数据的加载器,将数据集中的数据加载到神经网络当中
官方文档
可以看到他的API原型以及参数的解释
代码
DataLoader的作用,就是将一个数据集中的数据,进行抓取并打包,将其分为许多个数据包,存到返回值中
返回值中的每个元素也是一个二元组:“数据包,target包(有的是label包)”
数据包中的数据与target包中的target一一对应,但是不一定是按照数据集的顺序抓取,如果参数指定随机抓取,那么就进行随机抓取
值得注意的是:在27行,使用的是add_images,注意是加了s的,这个用于每步展示一个数据包
效果:
每一步都是64个图组成的数据包
drop_last传True与False的区别:
传False时会将余数也抓取,而传入True时,则会把余数舍掉
shuffle的作用:
设置下一轮抓取,数据集是否打乱顺序
当设置为True时:
每轮读取数据集之前,数据集打乱了顺序
代码:
可以看到,对test_loader进行两次读取,就是进行两轮数据集的读取打包工作,而无需对Dataloader做两次调用
而这里Dataloader设置为True,就是说每轮读取数据集之前,数据集要打乱顺序
神经网络的搭建与填充
官方文档
进去之后,在API一栏找到 torch.nn,就可以看到许多的模块,其中,第一个Containers是整个神经网络的骨架,下面那些都是往骨架内进行内容的填充
所以,我们先看骨架:
神经网络的基本骨架-nn.Module
文档
在Containers中,也分了许多的模块,其中,最常用的就是Module模块。根据右侧的介绍可知,他是所有神经网络模型的基类,也就是任何一个神经网络都要依赖他进行骨架的搭建
这是依据Module类进行自定义神经网络骨架搭建的一个demo
首先,需要继承nn.Module基类
之后,构造函数里,先对父类进行了构造,之后的操作就是demo的自己的操作
再之后会重写一个forward方法,里面是demo的自己操作
关于forward:
forward就是神经网络中的一个向前推进的一个函数,笼统的讲,就是从input进行输入,然后经过forward的函数处理,forward会将神经网络处理数据的步骤进行一个集合汇总,最后给出输出
注意,forward必须被重写
本demo中,对输入进来的x进行了两遍的“卷积 + 非线性”
代码
我们利用这个基类可以构建自己的神经网络
在我们的神经网络当中,可以看到,我们只是对输入进行一个*2
需要注意的点:
1、神经网络的输入最好是tensor类型,因为他包含了神经网络所需要的大部分属性,所以,任何数据最好都转为tensor类型
2、调用my_model对象时,可以直接使用"()"调用并传入数据
补充:卷积操作介绍
文档
可以看到,这些模块分为了1d、2d、3d,实际上代表的就是一维二维和三维,上一节的Module是一个class类API,这次的卷积层就是一个纯粹的函数API
在使用这些API之前,我们可以先去看一下这些API的原理的详细介绍(以2d为例):
进来之后就是关于该API的原理性代码的详细介绍(注意与其最终使用的API是不同的,仅仅是原理代码)
卷积的过程
卷积分为输入图像,和卷积核(也被称为权重),他们都是一个矩形矩阵,在卷积时,就是将卷积核从左上角开始,将其重叠的每个位置,相乘之后再相加,就会得到一个输出
而设置参数Strtde,就是控制每次卷积核的移动步距,该参数可以是一个数,也可以是二元组
如果是一个数,那么就是设置横向和纵向的移动步距,横纵向相等。如果是二元组,那么就是分别设置横向和纵向
而其默认是先横向后纵向,即向横向移动,若有出界,则纵向移动到行首,再横向移动
如上图所示:设置Strtde为1。那么就是设置横纵向都是1,所以从左到右从上到下每次移动一个单元格的距离,最终会得到九个数据,九个数据按照相对位置进行摆放
如下图:
补充:而如果Stride设置为2,那么会得到4个输出,相对位置要保持
注意,最终得到的卷积输出,其实其本质还是一个图像,所以说,卷积操作,最终是将一个图像经过特定变化后的一个图像,而不是一个新的别的类型的数据
代码1
前两个参数:
第一个参数是输入数据的tensor的shape(尺寸)属性的要求,可以看到要求输入的tensor数据的shape属性是一个四元组,因为他要求四个方面的数据
第二个参数是卷积核,或者说权重矩阵转为tensor型后的shape的要求,同样要是一个四元组
而原始状态下:
原始状态下,其shape均为一个二元组
解决:
使用reshape,可以对一个tensor数据样本的shape进行设置,参数一输入要操作的数据(tensor型),参数二就是其最终要设置成的shape
这里我们对应卷积2dAPI,设置一个四元组,输入四个元,用括号包住
第一个是minibatch,表示一次抓取几个数据,我们只有一个数据,所以传入1
第二个是channels,表示通道,这个数据仅为数字数据,所以是1通道
后面两个参数是高和宽
最终代码:
导入functional中的conv2d API,先传入三个参数(注意第三个参数指明形参,因为我们忽略了第三个形参,直接传入第四个形参)
设置Stride为1
最后的输出与我们之前计算的一致
当Stride为2时,也是与我们预想的一样
代码2
padding,表示填充的意思,同样,他可以是一个数(表示横轴相同),还可以是一个二元组(分别表示H和W)
如果padding为1,那么输入图像就会变为上图所示,上下左右都会多出一个空白行列,这些空白行列的值为0,卷积核去做卷积时,会从padding之后的矩阵的左上角进行卷积,之后进行移动
代码:
卷积层
文档
可以看到,这些模块分为了1d、2d、3d,实际上代表的就是一维二维和三维,上一节的Module是一个class类API,这次的卷积层就是一个纯粹的函数API,但是该函数不是为了返回一个结果,而是返回一个卷积层,相当于向神经网络的骨架中填充内容,但是仍然是一个“层”,没有涉及到具体数据的读入输出,只是对“如何处理数据”进行了设置
进来之后,我们可以看到API的介绍:
以及他们的参数:
参数一:输入图片的通道数(例如一个彩色的图片有rgb三个通道,而抽象出来的一个矩阵,就只是一个通道了)
参数二:输出结果的通道:
当输出结果的通道设置为2时,那么卷积会生成两个卷积核(这两个卷积核的内容不一定一样),进行两遍卷积操作,得到两个卷积输出,最终的输出会以这两个卷积结果进行融合,两个通道进行融合
参数三:设置卷积核(或者称为权重)的尺寸大小,如果设置一个数,那么是一个n * n的一个方阵,当然也可以设置二元组,分别设置其高和宽,我们只需要设置卷积核的尺寸大小即可,无需设置其里面的内容,因为我们的模型训练,训练的就是卷积核里的内容,因为他就相当于是函数的参数,模型训练就是调整参数的过程
参数四:卷积核每次移动的步距
参数五:填充
(参数四和五可以看上面的“补充:卷积操作”)
代码
这里的10行到12行,是事先准备好了数据集以及经过打包之后的数据集,但是暂时没用到,因为此处仅仅是进行了模型的搭建与填充,没有真正处理数据
之后14行到21行,就是一个神经网络模型的搭建与填充,首先利用nn.Module,先搭建骨架
之后,在构造函数里,对卷积层进行搭建以及设置,上图的17行就是在搭建卷积层,并做一些设置(设置为输入3个通道,输出6个通道,卷积核大小为3*3,步距为1)
下面重写forward方法,进行处理顺序的设置:目前设置“将形参x,直接进行卷积(直接用self.conv1调用传入数据即可)”
我们可以创建实例,然后打印,看一下当前所搭建的神经网络的样子:
可以看到,Tudui这个神经网络,里面有一个卷积层,以及其卷积层的一些设置
之后,我们可以传入数据集中的数据使用我们的神经网络进行数据处理了:
首先定义一个writer,为后续进行UI展示做准备
之后,使用for循环,将打包之后的数据集进行遍历,每次取出一个数据样本,该数据样本是一个二元组,即(打包好的数据包,target包)
其实一个数据包就是相当于一个单个数据,因为他是把好多原始数据拼成了一个数据,由此可以,神经网络处理数据是每次单个数据进行处理的,只不过我们可以使用打包的方式,让其单次处理一张集成了许多数据的单个数据
第31行,我们单独拿取imgs数据(这是一个集成后的单个数据),将其放入神经网络中处理,得到输出
32 33 我们可以打印一下神经网络处理前后数据的shape,看是否与我们的预想一致(包括抓取了多少个数据进行的打包、信道数量、尺寸(输入数据的尺寸没设置,所以是数据集的原始尺寸,输出数据的尺寸会收到padding、stride、dilation等参数的影响,我们可以通过控制这些参数,从而控制卷积的尺寸变化))
补充:卷积,我们可以从参数规定其卷积前后的通道数,所以,我们可以认为卷积实际上,主要关注的点在于通道数,但是其尺寸也会在卷积的过程中发生变化,这种变化是可控的,也就是说,我们可以通过设置padding、stride、dilation等参数,控制卷积过程中尺寸的变化,如下图:
最后,使用add_images,将所有step的原始图和卷积之后的图进行展示
但是:
由于我们设置了卷积之后的信道是6,对于彩色图片来说,Tensorboard无法显示信道为6的图片,所以,我们使用reshape将其shape进行修改,将其信道数量改成3,但是将打包数据的数量改成64 * 2,当然如果我们设置为-1,他会自动进行计算
(这只是一种测试方法,为了展示图像,实际上在产品中这是不合理的)
最终效果:
池化层
文档
我们最常用的就是第二个,最大池化(2d)
(ps:下面的MaxUnpool,是上面Maxpool的相反的意思)
简介:
最大池化操作,同样的,他有他的池化核,我们还是只需要规定池化核的大小(因为池化核没有内容,只有大小(与卷积核不同))
而他的步距stride,默认是池化核的大小(如上图,stride是3)
之后,每次会取重叠区中,值最大的那个,作为输出节点,这就是池化操作
而Ceil_model设置True还是False,他的效果在于,如果移动步距之后,池化区超出了输入图像的边界,是否还会在目前仅剩的重叠区中进行池化,如果设置为True,那么就表示要继续进行池化
如果设置为False,那么就表示不再进行池化
(默认情况下,Ceil_mode是False)
代码
首先先准备数据:准备一个矩阵
之后对input进行reshape,同样是四个参数(batchsize单次抓取数量, channel信道数量, H高, W宽),当batchsize设置为-1时,是让编译器自己计算batchsize
之后,我们要搭建神经网络,只填充池化层
然后重写forward,进行数据处理顺序的定义
然后,就可以创建对象,使用神经网络进行数据处理了
ps:注意,如果报Long的报错,那么说明输入数据不能是整形,所以,加一个参数:dtype=torch.float,可以使矩阵变为浮点型矩阵
最后的输出,(Ceil:False)只输出一个2;(Ceil:True)输出一个四个数的矩阵
代码2
效果:
可以看到,最大池化,就是将其图像的一些突出特征进行保留(这也符合保留最大值的逻辑),所以,最终池化后的图片类似于马赛克,实际上就是对突出特征进行了保留,非突出特征进行了舍去
非线性激活
文档
代码1
以RELU为例:
该非线性激活描述的是,当输入的input小于0,那么其output就截断为0,如果input大于0,那么就是输出原值
代码:
其中:对于ReLUAPI的参数:inplace,如果为True,那么就代表在原值地址上进行修改,如果为False,那么就代表拷贝一份,对新拷贝出来的变量进行修改(用一个变量接住),原输入值不变
代码2
以Sigmoid()为例,对图片进行非线性激活
代码:
效果:
作用
非线性激活,可以实现许多曲线特性,以实现更加灵活的贴近现实场景
其他层
我们上面已经学习到了Non-linear Activstions,也就是非线性激活层
之后的Normalization Layers 是正则化层
再之后用红色圈起来的,是看具体需求来选择使用
而蓝色圈起来的Linear Layers是线性层,我们之后会学习
至于,倒数第二个Dropout Layers,是用来防止过拟合的,也是看具体需求
线性层
文档
线性层,就基本上关注三个参数即可,输入数据的个数、输出数据的个数、是否偏置(即是否要加b)
作用:
上面是一张深度神经网络的图,
输入:
首先要进行多个x的输入
中间处理层:
之后会得到许多中间输出,用作下一次的输入,可以看做为中间的隐藏层
输出:
最终得到的输出,称为输出层
而线性处理,在上面每一步都起着作用,我们以从输入层到中间层为例,就需要进行线性处理
可以看到,一个g1,就是来自x1到xd的线性处理之后,综合的结果
而其中,每一个x,都会进行线性处理,基本上,线性处理时的公式就是kx+b,而是否+b,是根据参数3的bool值来决定的
而k值,和b值,也就是参数值,与前面所学习的一样,不用管他的值,他的初始值以及后续的调整,都是在模型训练过程中自己完成的
下图就是他们的取值依据:
代码
首先,先处理数据:
我们还是下载官方的datasedt,之后使用Dataloader抓取打包
然后,for循环拿到每一个预处理完之后的数据
将其Size修改,改成1个数据,1个信道,高为1,长设置为-1,让编译器去推算。这样就变成了一维的、线性的数据了
搭建模型,并进行数据处理:
这里我们介绍一个可以更快捷的将一个多维数据变为一维的API:flatten
他的效果如下图圈出来的地方所示:
首先搭建模型,设置线性层的输入输出的数据的数量,输入设置为196608(因为之前对数据变为一维,并进行了数量打印,可以看到其数量是196608),输出我们设置为10
然后,创建模型对象,之后在for循环中,获取到原始数据之后,不再使用reshape将其变为一维,而是使用flatten这个API,将数据变为一维
然后放入模型中,最后打印结果
效果:
可以看到,flatten成功将数据变为了一维,而模型也将数据线性化到了10个
pytorch提供的模型
文档
我们以视觉为例
进去之后就可以看到这些都是视觉方面的模型,上图展示的是“物体分类”方面的模型
右侧有着不同类别的模型索引
分别是“分类”、“语义分割”、“目标检测、实例分割”等等