【深度视觉】第二章:卷积网络的数据

四、卷积网络的数据

上个系列我们详细讲解了pytorch框架下的全连接层神经网络DNN。本系列我们开始讲卷积神经网络CNN,Convolutional Neural Networks。

上一章我截取了鲁鹏老师课件里面的一张图,详细展示了和计算机视觉相关的领域,显而易见,这门学科是一门交叉学科,所以尽管扩展你的知识域吧,比如,摄像设备性能,成像原理,图像数据的生成与获取,视频特效,3D,图像复原、图像分割、识别、几何、光学、信号处理等技术,你都要多多少少了解一些。

1、卷积网络的数据结构
我们还是从数据开始聊起。前面讲DNN的时候,一直强调,你得把你的数据转化为二维表格数据,就是有行有列有标签,行表示样本,列表示特征。就是一条样本就是一行数据,多条样本就是多行数据。这样的数据你才能打包分小批次喂入DNN网络进行学习和训练。也就是DNN适合一维和二维数据。一维就是一条样本,二维就是多条样本。

我们现在讲的CNN,和它匹配的数据结构则是三维四维数据结构。就是说不管你是什么数据,你首先得把你的数据转化为三维或者四维才能用卷积神经网络。而图像数据天生就是三维数据,而且CNN天生也是为处理图像而诞生的,这点我们在讲架构时会再讲。所以说CNN的数据一般是图像数据。或者说CNN的一条样本就是一张图片数据。

如果你想喂入CNN一张图片,让数据在网络中正向传播一次,看看网络数据流是否调通,那你喂入的这条样本的数据结构就应该是三维的:(channels, height, width)这样的数据结构。
如果你想一次喂入CNN一个小批次的样本,也就是一次喂入好几张图片查看结果时,那你输入的数据结构就是四维的:(samples, channels, height, width)这样的数据结构。
如果你想一个个小批次的喂入CNN来训练网络,一般情况下我们还得进行数据预处理数据增强,然后打包样本和标签,然后再分小批次,才能喂入CNN网络进行学习和训练。其中数据预处理和数据增强又涉及到非常非常多的细节点,后面会专门开启一个章节讲述。

如果你的数据压根就不是图像数据,你也想用卷积神经网络跑跑,也是可以滴。不管你通过什么手段,只要你最终把数据变形成三维或者四维,就可以用卷积网络。这部分内容涉及到的知识点也非常多,后面会单独写一个小标题来演示。

2、图像的相关概念
卷积网络主要处理的是图像数据,所以我们从图像聊起:

(1)图像是什么?
图:是物体反射或透射光的分布,是物体本身的固有的特征。
像:是人的视觉系统所接受的图在人脑中形成的印象或认识。

(2)模拟图像和数字图像
模拟图像是连续存储的数据,易受噪声干扰,人眼看起来不是很清晰。
数字图像是分级存储的数据,一般是分256级,即8位。
目前模拟图像基本全部都已经被数字图像替代了。

(3)图像的表示方法
二值图像: 每个像素点不是白色就是黑色;一个像素点只要一个bit位就能表示;用0或1表示每个像素点。

灰度图像: 图像只有一种颜色,比如图像可以是红色,可以是灰色,可以蓝色,可以是绿色等等,但不管什么颜色都是只有一种颜色。但是这一种颜色我们给它分成了256个等级,就是256个灰度级,可以理解成256个不同程度的明暗度。比如一张红色的灰度图像,像素值=0就是最暗,黑色,像素值=255就是最亮,就是最亮的红色。255中明暗度正好可以用8位也就是1字节byte表示。

彩色图像: 图像是彩色的,图像的每个像素点都是由三种颜色混合而成。这三种颜色是R G B, 每种颜色的取值都在0-255之间。每种颜色是一个通道,所以彩图一般都是3通道。少数图像是4通道,因为还有一个0-1之间的透明度。

图像的这种表示方法,就可以让我们进行图像处理。比如改变像素的值,就是改变图像的显示。比如改变通道,就是改变图像的色彩空间。比如切片,就是截取图像的特定区域。比如进行加减乘除、按位运算等,就是对图像进行数值运算。

3、图像数据的结构
二值图像的数据结构是一个二维数据结构(行,列),就是没有通道维度。其中行表示图像的高度,列表示图像的宽度。而且所有数字不是0就是1。当然如果你想喂入CNN,你就得升维到三维:(1, 行, 列)这样的结构。

灰度图像的数据结构也是一个二维数据结构,也是没有通道维度,其中行也是表示高度,列表示宽度,但是所有的数字是从0到255之间。同理,如果你想把这种类型的图片数据喂入DNN,你也是得升到三维。

彩色图像的数据结构一般是一个三维数据结构。三个维度分别是(高度height, 宽度width, 通道channels),前两个数值决定了图片的大小尺寸,后面的通道决定了图像的色彩效果。
【深度视觉】第二章:卷积网络的数据_第1张图片
img.shape, 返回:(667, 1000, 3), 说明表示这张图片的数据结构是,667行1000列3通道。就是有3个667行1000列数字。也就是每个通道在竖直方向上有667个像素点,水平方向上有1000个像素点。就是这张图片的尺寸大小。
第3个维度的3表示通道的意思,通道这个维度有时会放在高度和宽度之前。图像显示成什么颜色是由通道数决定的。单通道就是一张灰度图片,三通道和四通道就是一张彩色图片,而我们图像数据的通道一般就只有1通道、3通道和4通道这3种取值。而每个通道里面的数字则决定了图像的轮廓、线条、色彩、边缘等信息,基本就是决定了图像的显示内容。

img.dtype返回的'uint8'表示对象img的数据类型是uint8类型。这种类型数据的取值范围是0-255之间,不会小于0,也不会大于255。比如某个像素点的值是100,如果你给它加了200,那这个像素点就变成了300-255=45,就是这个像素点从100变成了45。就是这种数据结构有自己的一套运算规则,当然这样的运算规则主要是为了适应对图像进行处理的需求。

4、色彩空间
通道为什么只有3种取值?这就得说说色彩空间这个概念了:

世界本是无颜色的,我们人类看到的各种有色光只是特定波长的电磁波能够刺激人眼的锥体细胞,进而在人脑中形成颜色信号而已,实际上电磁波的波长域是非常广的,而我们人类只能感知很小一块区域,并且我们眼睛的光感细胞把特定波长的电磁波刺激信号传递给大脑,大脑反馈出不同的颜色。所以对于人类来 讲人类的色彩空间就是一个特定的空间。

但是如果对于打印机来讲,它的色彩空间和人类的就完全不一样,比如,在黑暗中,光越强,人类就看到越清晰的色彩,但是对于打印机来说某种颜色的墨汁越多,如果有多种颜色,那么它显示出来的色彩就是黑色的。

同理,对于显示器来说,不同显示器由于发光物理原理不同,比如液晶显示器显示屏上的色彩是通过控制电压的高低来控制每个像素的颜色的,而CRT显示器是靠发射电子束来打击荧幕上面的荧光粉来达到显示的,所以不同显示器,即使你输入的相同信号的数据,它显示出来的效果都是不一样的。

同理,很多不同摄像机,通过不同的原理捕捉出来的图像的色彩即使数据一样,但呈现给我们人眼的色彩都是不一样的。比如军事上的图片、气象上的图片、我们日常用的普通相机的图片,用我们人类肉眼看都是不一样的。

所以,为了让人眼看到的图片和打印机打印出来的图片、不同显示器显示出来的图片、不同相机拍摄出来的图片、不同图片显示软件显示出来的图片,人类看着是统一的,就是对人类来说,同一张照片的色准是一致的,这样我们人类看到的风景就和打印机里打印出来的风景是一样的、和显示器显示出来的风景是一致的、和看图软件比如微信传输后显示的图片是一致的,我们就需要定义不同的色彩空间。相反,比如一张图片里面有只猫,如果在某个色彩空间人类看这张照片是只白猫,假如经过微信这张照片传输到另一个人手机上,这张图片还是这张图片,即使这张图片的数据没有变化,但是另一个人的手机的图片显示是在另外一个色彩空间上,比如那个人把手机色彩空间调成护眼模式、夜间模式等等,就是不同的色彩空间,那这只猫很可能那个人的眼睛看到的就是一个别的颜色的猫,这样图片信息在人与人之间的传递中,由于介质————微信的彩色空间不一致而导致两个人收到不一致的信息,就是色准发生了变化。所以我们要定义色彩空间。也所以不同色彩空间需要能够进行相互转化。

下面介绍几种常见的色彩空间:
(1)RGB色彩空间是计算机中常见的基本颜色通道。RGB分别表示red, green,blue红绿蓝三种基本颜色。

(2)RGBA色彩空间是个四维通道:(红色,绿色,蓝色,透明度alpha)。透明度alpha的取值范围在0-1之间。当一个RGB像素加上透明度之后,色彩就会变得“透明”。所以RGBA可以提供更丰富的色彩样式,让图像的色彩变得更加绚丽,更加符合人类眼睛的审美维度。

(3)CMYK色彩空间是彩色打印机的色彩空间,是打印机认识的颜色。是由青色(Cyan)、品红(Magenta)、黄色(Yellow)和黑色(Black)构成,所以是四维通道,在图像数据结构中表示为(高度,宽度,4)这种形式。这是打印机的色彩世界,和我们人类不一样,我们人类看到的色彩更浓就更绚丽,但打印机是色彩更浓就是黑黑的一片,所以色彩空间就是一种标准而已。

(4)HSV(或HSL)色彩空间:H代表色相,S代表饱和度,V代表亮度。HSV颜色模式是除了RGB颜色模式之外的另一种流行的颜色模式。

以上几种色彩空间可以自由切换,都有对应的转化公式,但是会产生数据的轻微损失。

5、再次理解通道概念
在计算机世界里,用于构成其他颜色的基础色彩叫做'通道'。灰度通道是只有一种颜色的通道,而不是灰色的意思。灰度在计算机视觉中指的是明暗程度。
一般情况下,计算机世界中的五颜六色是由RGB ,red, green,blue红绿蓝三种基本颜色不同程度混合形成的颜色世界。这三种基本颜色的取值是[0, 255],0表示这种颜色的光的强度几乎为0,那就是黑色,就是没有光线,255表示这种颜色的光的强度最大,就是颜色最重。如果三种基本颜色的取值都相同那显示出来的颜色就是白色,取值越大白得越刺眼。当某个像素的值为(0,0,0)这个像素点就是黑色。当某个像素的值为(255,255,255)这个像素点就是白色。当某个像素的值为(255,0,0)这个像素点就是红色。当某个像素的值为(0,255,0)这个像素点就是绿色。当某个像素的值为(0,0,255)这个像素点就是蓝色。【深度视觉】第二章:卷积网络的数据_第2张图片

6、图像数据可以用DNN跑嘛?
当然可以。DNN适合二维表格数据,行表示样本,列表示特征。所以你只要把你的图像数据转化成二维数据就能喂入DNN来跑跑试试效果。
如果你的图像是二值图像或者是灰度图像,那你只要把每张图像数据拉平变成一维数据,每张图像当作一个样本,就可以用DNN试效果了。
如果你的图像是三维彩色图像,那你也是得,按通道维度方向,先把图像数据拉平,然后按通道顺序拼接变成一维数据。就是一张图片是一个一维结构,多张图片就是二维结构,其中行表示一条样本。这样就可以用DNN跑了。后面讲架构时,我会单独写一个完整的小案例,展示线性分类器如何分类图片,那就是一个DNN处理图片数据的例子,到时大家可以再细细体会。

7、图像识别领域的常见数据集

全连接神经网络DNN在应对图像数据时,就会显得捉襟见肘,因为现在平平常常的一张图像也得1000x1000的尺寸,就是得有100万个像素点,而且再是三通道图像,就是300万个数字,如果我们之间把300万维的数据喂入神经网络,那神经网络立马就崩掉了,根本无法处理这个数据量。所以只要是图像数据集就必须用卷积网络,卷积网络模型才是真正的开始了用海量数据训练巨大的模型。

我们知道深度学习的三驾马车是数据、算法、算力。数据指的就是训练一个网络的数据,比如训练集验证集测试集;算法就是你的架构、模型、网络、优化算法等的统称;算力就是计算资源GPU等硬件资源。这里我们聊的是卷积网络的数据。

前面讲过计算机视觉任务,不同的任务要对应不同的标签。当你明确你的任务时,你就要开始着手建立你的数据集,而整理数据集是一件非常复杂、非常耗时、耗钱的一项工作。这个工作看似简单但其实是非常复杂和繁琐的。一个视觉项目从开始到交付,整理数据集有时耗时三分之二的时间和金钱都是正常现象。不管多么强大的模型,garbage in garbage out, 所以整理数据是一项非常非常重要的工作。你看计算机视觉领域的红人李飞飞教授,其他它出圈的不是她发明了多么强大的算法,而是她整理了鼎鼎大名的imagenet数据集,这个工作就足够她闪亮和荣耀了。这里之所以这么说是因为有一次,一个人让我给他整理视频数据集,他说他要训练一个电子人,但是一听啥数据都没有,要我现从网上找视频给他剪辑做,而且细节什么都没有,我都懵了。后来他们的技术和我沟通,也是不考虑获取数据集的难度和途径,只简单说了要什么什么样的数据,然后来一句,至于怎么获取就是你自己的事情了,他是训练模型的,没有义务去搜集数据集。我也一时无语。我想说作为模型训练的人,一定是那个最了解数据的人,如果你对你的数据都不是非常了解,你能训练出什么模型呢?任何一个优秀的模型或者效果好的模型,一定是在数据上做了非常非常多的处理和特征工程,甚至一些trick,样本的均衡性、样本的特点、有没有异常的样本、有没有特殊的样本、要识别的对象在图像中位置、尺寸大小、清晰度、光照、形变、角度、遮挡等等,各个细微处都要了然于胸,才能对症去调整模型,而不是不管三七二十一,觉得只要找到一个强大的模型,就可以闭着眼睛一股脑输出,至于输出就听天由命了,如果输出不好,就反过来把责任扣到标注人员身上。这种人就特别讨厌。

扯得有点远了,我只是想说你想用海量数据训练一个巨大的模型,你自己搜集数据集几乎是不可能的,搜集数据和给数据打标的成本是非常昂贵的。这里我们是学习,所以我们不可能花很多精力去整理一个数据集,我们一般都是自己生成一些toy data,或者使用一些pytorch框架中内置的一些数据集。这些数据集比较简单和经典。而且我们现在讲分类算法,所以也不太牵扯更复杂标签的数据集。下面给大家罗列几个常见的数据集。

(1)MNIST、FashionMNIST、SVHN、Omniglot、CIFAR数据集
这五个数据集是可以从pytorch提供的相关接口下载的数据集,是最容易获取的数据集,所以先介绍这五个数据集:
MNIST:黑底白字的手写数字数据集。用来做识别任务,不能用于检测和分割,有10种标签类别,图像尺寸是28x28。

FashionMNIST:衣物用品数据集,用来做识别任务,不能用于检测和分割,也是有10种标签类别,图像尺寸是28x28。

SVHN:实拍街景数字数据集street view house number。是一个基于谷歌地球 (Google Earth)实拍的街景图中的门牌号图片,而制作而成的数字识别数据集,是数字识别和检测中比较困难的一个数据集,因为比较模糊。数据集支持识别、检测、无监督三种任务,不能用于分割。因为也是数字嘛,所以也是有10种标签类别,图像尺寸是32x32。

Omniglot:全语种手写字母数据集。数据集包含来⾃50个不同字⺟的1623个不同⼿写字符。共1623个类别,每个类别有20个样本,每个样本⼤⼩为28X28。这个数据集是专用于'一次性学习'one-shot learning。比如人脸识别的策略就是一次性学习。在‘一次性学习’策略中训练数据是一组组照片,标签只要是'是'或者'不是',就是标签只要是 标出这组照片是不是同一个人就可以,这种策略是一种二分类策略。在训练样本中是给算法两张照片,通过计算距离或计算相似性,判断两张照片是否是同一个人,输出的标签为“是/否、相似/不相似”,在这种策略中,测试集的样本也是两张照片,并且测试集的样本不需要出现在训练集中。现在实际落地的人脸识别项目都是基于一次性学习完成的。先扫描一下你的身份证得到一张身份证的图片,然后再用摄像头拍一张你的脸的照片,然后判断摄像头拍的照片和身份证照片是否是同一个人即可,至于这个人叫啥,是男是女,模型就不关心啦。Omniglot数据集就是专门训练一次性学习的数据集,只能用来做识别任务,不能用于检测和分割。

CIFAR10:十分类通用数据集。涵盖十类动物与交通工具。是最常用的教学图像,因为大多数人的个人电脑都没有GPU,cifa就非常适合做识别任务,而且还可以通过pytorch导入。这个数据集是彩色图片,每张图片的大小为32x32x3。
CIFAR100:100种动物,食物,交通工具等类别的数据集,有20个coarse标签和100个fine grain标签。非常适合做识别任务。下载下来是cifar.zip文件,解压后还是个压缩文件:cifar-100-python.tar.gz,这种文件是不是Linux系统中非常常见的文件格式,但是pytorch也是可以直接调用.tar.gz格式文件,或者tar.gz解压后的文件。

(2)展示一下FashionMNIST的获取过程:【深度视觉】第二章:卷积网络的数据_第3张图片

【深度视觉】第二章:卷积网络的数据_第4张图片

同理,MNIST、SVHN、Omniglot、CIFAR10也是上面的方式就可以获取:【深度视觉】第二章:卷积网络的数据_第5张图片

只是有些细节不太一样:
B:参数split='train'/'test'/'val', 表示你要加载的数据是训练集还是测试集还是验证集validation set
C:因为omnist数据集的发源地论文中是将训练集命名为background的,所以这里的参数background=True,就表示要加载的数据是训练集数据。background=False表示要加载的是测试集。

可见,不同数据集有不同的接口,而且不同的数据集它们各自的属性和方法也不同,如果你想知道这个数据集都有哪些属性可以调用,就必须要进入到数据集的源码进行查看。如果不想看源码,你就只能尝试用索引和循环去查看看行不行。
如何有效地阅读PyTorch的源代码? - 知乎

上面的数据集都是识别任务中的最基本、最简单的数据集,这些数据集只能用来测试一下我们模型的基准线,就是看看模型的效果。深度学习中的SOTA架构在这些数据集上一般都是99%以上的高分。如果你嫌这些数据集数据量太小、数据太简单,想使用一些更复杂的数据集上GPU上跑跑,那成本最低的就是竞赛数据集。比如ImageNet、VOC、LSUN等数据集。

(3)ImageNet、VOC、LSUN数据集的获取渠道
ILSVRC在2017年shutdown了识别任务, 将比赛转移到Kaggle上举办。据说ImageNet2019的数据集可以在Kaggle上可以免费下载,主要是用于检测任务。但是2017年之前的数据集现在是找不到了。而且据说下载时必须有个edu邮箱才可以,就是不允许商业应用,只能做研究使用。

VOC数据集是在PASCAL(pattern analysis, statistical modeling and computation learning 模式分析,统计建模和计算学习大赛)大赛中视觉对象分类中的数据集VOCSegmentation&Detection。
VOCSegmentation用于分割任务,2012年版本中,训练数据5717,测试数据5823,对象标签27450。
VOCDetection用于检测和分割任务,2012年版本中,训练数据1464,测试聚聚1449,分割标签6929。
在pytorch中支持从2007到2015年的5个版本的下载,因为它比较小,只有3.6G左右,但是非常不稳定。

LSUN数据集也是出于大规模场景理解挑战赛中的数据集,它是专门专注于景观的。数据集是用于识别任务的。不同景观场景的数据集大小不同,有的场景数据集大,比如超过40G,有的小,比如2G左右。全部的数据集有200G左右,下面给大家展示一个类别的下载流程:
【深度视觉】第二章:卷积网络的数据_第6张图片
坑很多啊,即使你已经获取了这个数据集,想顺利读取也是非常费劲的,后面演示的的代码也是这个数据集。

上面的数据集除了LSUN勉强可以在cpu上跑一下,其他必须要有GPU,没有GPU计算资源的话,这些数据很难进行适当的训练proper training,你必须使用非常小的batch_size,但batch_size过小又会延长训练时间。如果训练一个ImageNet需要20个小时,那后面的调参、网格搜索等操作你就耗不起这个时间。如果非要使用ImageNet数据集,建议使用Colab等线上平台的大型GPU。

(4)其他数据集简介
Kuzushiji::日语手写平假名识别,数据集一共分为三种,Kuzushiji-MNIST,Kuzushuji-49和Kuzushiji-Kanji,下载地址:GitHub - rois-codh/kmnist: Repository for Kuzushiji-MNIST, Kuzushiji-49, and Kuzushiji-Kanji ,你可以手动下载,也是用baseline里面给的download_data.py下载。这个数据集是一个样本高度不平衡数据集,这个数据集主要用来研究深度学习中的样本不平衡问题。

USPS:也是一个手写数字数据集,是白底黑字的数字,常用来与MNIST对比。用来做识别任务,不能用于检测和分割,pytorch提供下载,有10种标签类别,图像尺寸是16x16。
MS COCO:全称是Microsoft Common Objects in Context,起源于微软于2014年出资标注的Microsoft COCO数据集,与ImageNet竞赛一样,被视为是计算机视觉领域最受关注和最权威的比赛之一。 COCO数据集是一个大型的、丰富的物体检测,分割和字幕数据集。这个数据集以scene understanding为目标,主要从复杂的日常场景中截取,图像中的目标通过精确的segmentation进行位置的标定。图像包括91类目标,328,000影像和2,500,000个label。目前为止有语义分割的最大数据集,提供的类别有80 类,有超过33 万张图片,其中20 万张有标注,整个数据集中个体的数目超过150 万个。这个数据集可以到官网下载。这里顺便说一下,你只要知道有什么数据集、以及这些数据集的特点、适用场景即可。当你要获取这个数据集的时候,你肯定会百度一下这个数据集,你就找到很多人分析的获取渠道,一般多试试都会得到的。

CelebA:全球名人(单人)人脸数据集,可能是全球最大的人脸数据集之一,图片质量很高,拥有丰富的标签。虽然PyTorch官方说明中允许下载,但实际上下载链接已失效,需自行下载。且由于CelebA下载后的文件是7z压缩格式,类datasets.CelebA无法读取,因此下载后的图像无法使用CelebA类来进行导入,需要用别的工具来读。CelebA的属性标签有很多,比如eyeglasses是否戴眼镜、bangs是否有刘海、pointyNose是否有雀斑、ovalface是否是鹅蛋脸、smiling是否笑、mustache是否有胡子,,,有40类属性标签。CelebA还有个体识别的人名标签,有几千个人名类别吧。所以celeba是同时可以用于识别和检测的。数据集有20G左右,这个数据集和lsun数据集不一样,它下载后解压后就是图片文件,有png和jpg两种格式的图片。所以我们可以不用读取到jupyter 里面,就可以抽样,比如去解压后的文件夹里面复制前1万张图即可,用其中1万张去做个体识别或者属性识别。所以这个数据集非常有用,而且是免费的。

STL10:从ImageNet数据集中抽样的、专用于无监督/半监督深度学习的数据集。STL10是一个10分类的数据集,数据量也不大。不能做识别任务。

SBD:全称为语义边界数据集(Semantic Boundaries Dataset and Benchmark),专门用于语义轮廓识别,不能用于图像识别任务。数据可以从torchvision.datasets中的download接口获取,但是就是下载速度非常慢,比如3G可能要下载3天,你中间不能断网,但是实际上是特别容易出现下载超时而断网。

Places365:目前为止最大规模的景观数据集,由MIT支持,涵盖城市景观、自然风光、室内室外各种场景。其场景的复杂程度是超过LSUN的。数据总量超过1T,类别有400个,非常大。

说明:其实数据集没啥可介绍的,当你学某种经典模型的时候,你一定会查相关资料,你在看资料的时候,你留意一下别人的数据集,然后再专门百度这个数据集,网上会有一大堆相关资料。一般如果是公开数据集,你基本都会找到下载渠道。

8、读取数据的相关函数和类
你想低成本获取深度学习中的数据集就已经非常不容易了。而当你不管从何种途径获取到数据后,一般获得的都是一个压缩文件,解压后你会看到它可能是:pt文件、mat文件、lmdb文件、excel、csv、txt文件,或者就直接是文件夹,文件夹里面都是一张张jpg或者png图片,而标签在另外一个文件里。这些不同的情况没有通用的读取方法,只能不同情况不同处理:

(1)如果你获得的是.mat文件。那是matlab的数据存储的标准格式,mat文件是标准的二进制文件,如果你用的C环境,那你可以直接读。但我们学深度学习一般都是python环境,你得用scipy库的loadmat函数来读取mat文件:【深度视觉】第二章:卷积网络的数据_第7张图片

(2)如果获得的是.pt格式的文件。pt文件是PyTorch中保存模型和数据的文件格式。所以你可以使用PyTorch库中的torch.load()函数来加载.pt文件。【深度视觉】第二章:卷积网络的数据_第8张图片

(3)如果你获得的是.mdb格式的文件。LMDB即Lightning Memory-Mapped Database Manager闪电内存映射数据库管理器,是一个基于btree的内存映射数据库。所以.mdb是一个数据库文件。

此时你要先:【深度视觉】第二章:卷积网络的数据_第9张图片

安装完毕后,你还得写一个专门用于读取mdb格式的类:
【深度视觉】第二章:卷积网络的数据_第10张图片
【深度视觉】第二章:卷积网络的数据_第11张图片
上面我自己写的一个读取lmdb文件的类。其实这个类我们在讲DNN适配的数据的时候,就写过一个样例,思路都是一样的,继承Datasets部分和架构部分照葫芦画瓢即可。除此之外就是,你要非常了解如何用python处理lmdb文件,代码比较偏工程,而是还要很熟悉图片数据的各种数据类型。但是如果看懂了也不难,都是固定流程。下面看看效果:【深度视觉】第二章:卷积网络的数据_第12张图片

此外,前面我们讲过LSUN数据集是lmdb格式的数据集,pytorch中有专门针对这个数据集的接口,下面我演示下如何用pytorch中的接口读取:【深度视觉】第二章:卷积网络的数据_第13张图片

(4)如果你获取的数据是excel、csv、txt格式的数据,那你的数据大概率是一个二维表格数据。这类数据一般都是比较私密的公司自己的数据,这类数据读取比较容易,难点是这类数据如何变形才能喂入CNN。而此时你也只是处于对数据的摸底,也只是想用不同的模型跑跑数据的baseline,就是你还处于模型选择的过程,你也不确定你的数据适合哪个模型,只是探测一下基准。

当你的数据是二维表格数据,但你却想用CNN跑的时候:
CNN中的经典卷积网络LeNet的输入数据尺寸是(1,1,32,32),32x32=1024,也就是如果你想用lenet跑,你的数据的特征个数至少得1024个;
如果你想用经典网络AlexNet,alexnet的输入数据是(1,3,227,227), 3x227x227=154587,也就是的特征个数得有154587个;
如果你想用VGG,VGG的输入数据是(1,3,224,224),3x224x224=150528,你的数据特征不得小于这个数。

当然你也可以自建CNN架构,或者缩减经典架构的一些卷积层和池化层,比如去个头改个尾、或者只截取部分架构等操作,来适应你的数据。但是无论你怎么做,你的数据维度都不能太低,太低CNN还没卷几下就没了,那是不行的。因为我们都知道越深越宽的架构效果比浅层的效果好。如果你只能用浅层网络,那你还不如先试试机器学习的算法。

情况1:当你的二维表格数据特征有数万个,适合CNN的时候,此时你得变换你的数据结构来匹配CNN。下面展示一下如何用代码将二维表格数据转为为四维tensor:
【深度视觉】第二章:卷积网络的数据_第14张图片
对,就是这么简单,用reshape和tensor函数把数据转化维四维tensor了,然后打包特征和标签,然后分训练集和测试集,然后分小批次,你就可以加载数据喂入卷积网络了。 但是这里又有一个问题,如何我的特征不能正好reshape成经典网络需要的尺寸呢?

此时你要做的就是降维一点点,让数据长度正好能变形成你的目标尺寸。那此时就用到了降维操作。降维操作有很多实现途径,我前面写过一个kaggle比赛项目,里面详细介绍了基本是所有的降维方法、原理和代码实现,此时你都可以使用。但是一般情况下,我们不会单纯为了降维而降维,我们一般会在这个过程顺便筛选一下特征。所以一般的做法是,先把数据放入机器学习模型中跑一下,一是简单试探一下数据,二是让模型帮我们选择。比如我们很多机器学习模型像随机森林、逻辑回归、支持向量机等模型,都有coef_、feature_importance_属性、或者有L1,L2惩罚项。像这些模型就可以帮我们把特征筛选到目标个数,然后再reshape即可,后面操作就都一样了。

情况2:当你的二维表格数据特征只有几十个或者几百个的时候,你还特别想用卷积网络跑!
那此时你就得先给数据升维再变换数据结构。在机器学习中,我们常常遇到的是需要降维的需求,而在深度学习中我们时常是要升维的。其中最常用的做法是多项式升维,就是将特征交互相乘的方式来增加特征维度的方法,就是通过增加自变量上的次幂来提升维度的方法。这样就可以将特征映射到高维空间。这是特征工程中常见的一种数据处理方法。任何非线性函数,都可以通过多项式来进行无限逼近,也就是说,只要数据分布是有内在规律的,就一定可以通过多项式进行拟合,除非数据内部没有任何规律。

这种方法的工具:class sklearn.preprocessing.PolynomiaFeatures(degree=2, interaction_only=False,include_bias=True)
我们知道sklearn是一个机器学习框架,所以可以从sklearn中调用多项式升维函数PolynomiaFeatures()
【深度视觉】第二章:卷积网络的数据_第15张图片
其实这个实现代码我之前在讲DNN调优时展示过。大家可以结合看。

但是这里要强调的是:有的数据适合升维,有些事情低维度看不清,一旦上升一个维度就不是个事情,所以有的数据适合升维,作为升维后效果蹭蹭的往上走。但是有的数据却不适合做升维。比如当你的原始特征非常少时,多项式升维虽然可以大幅增加特征数量,但新生成的这些特征都是简单粗暴的对原始特征排列组合后相互相乘而生成的特征,并不是对原始特征的深层提取。 所以,多项式升维后的新特征其本质上和原始特征是没有很大区别的。那当你的原始特征非常少时,你多项式升维到几百几千个特征后放到深度学习模型中,就非常容易出现过拟合,模型的泛化能力就非常的弱。而且升维操作后你的数据就失去了可解释性,介意的要慎重选择。

所以,当我们拿到的二维表格数据的特征不是很多的时候,如果你是回归数据,我们就多项式升维后放到机器学习的线性回归模型中跑跑,如果你是分类数据,我们就多项式升维后放到逻辑回归中跑跑。如果跑出来的结果出现过拟合,说明数据就不适合做升维,这个数据就只能在机器学习的各种模型中广泛试试,看这个数据集的机器学习模型的上限是多少。如果前面跑出来的结果没有出现夸张的过拟合,并且数据升维后机器学习模型表现比升维前有较大的提升,就说明这个数据集是适合做升维操作的,你就可以将这个数据集升维后的数据放到深度学习模型中试试,看是否能打破机器学习模型的上限。

下面我们举个列子证明有的数据适合升维操作,而有的数据不适合升维操作:

【深度视觉】第二章:卷积网络的数据_第16张图片
【深度视觉】第二章:卷积网络的数据_第17张图片
【深度视觉】第二章:卷积网络的数据_第18张图片【深度视觉】第二章:卷积网络的数据_第19张图片

(5)如果你获取的数据是一个个文件夹,文件夹里面是jpg、png或其他格式的图片,有时还会配一个txt文件的不同层级的标签。此时也分两种情况:
情况1:当你的训练集是一个文件夹train、测试集也是一个文件夹test,而且train文件夹里面又是几个子文件夹,子文件夹名称都是都是类别名称,比如class1、class2、class3等类别个数个子文件夹,每个类别文件夹里面才是图片数据时,此时你就可以用torchvision.datasets.ImageFolder()类,把你的数据打包成能输入卷积网络的数据集:

torchvision.datasets.ImageFolder()可以把一批图片文件转化为 tensor类型的图片数据+标签 的打包元组数据。其中,样本数据是tensor类型,class1文件夹里的图像标签是0,class2文件夹里的图像标签是1。我们不仅可以用切片查看数据,也可以用循环查看,而且经过imagefolder函数处理后生成的数据,还带有一些属性,方便我们使用!
同理,我们可以生成一个多分类数据集,只要我们把不同的类别的图片存放在多个文件夹,并且文件夹的名字就是类别的名字即可,而图片的格式不需要统一,imagefolder可以读取任何图片格式。folder是文件夹的意思。imagefolder就是图像文件夹。imagefolder()表示从图像文件夹里面读取图像并生成图像数据集的类。imagefolder会根据图片文件的上一层目录的目录名称,把所有的图像数据都读取了并且打上目录名称的标签。

情况2:当你的图片数据是放在文件夹里面,但是标签是放在别的文件中,比如csv、txt文件中。此时你就得自己手动写一个继承了Dataset父类的类,来读取数据。那个类是根据你的任务和数据存储不同而有所不同的,就是一个任务要单独写一个针对这个任务的类。但是写这个类的整体思路,我前面现后写过两个,多多少少都介绍过,大家可以参考。这里也没有合适的数据,我就不再展示了。

9、分测试集和训练集、打包数据和标签、分小批次相关的类
【深度视觉】第二章:卷积网络的数据_第20张图片
基本上常用的就是这几个类,都是前面内容都涉及到的,所以这里也不展开讲了。如果你用得不顺手,也可以自己写。

10、总结:
机器学习中使用的数据一般都是表格数据,深度学习一般都使用的图像数据在格式、标签、内容等方面都有非常复杂的个性化,比如有的可以用来做识别,有的可以做检测,有的是用来做分割的,所以大部分图像数据集无法用同一个API调用,就是说一般情况下我们不能使用相同的代码加载不同的数据集。就是说如果我们要加载别的数据集,我们还需要特别的再单独研究一下那个数据集对应的类,才能正确导出并读出那个数据集。

而且,CNN对数据也是有要求的,比如不管你是啥数据,你都需要将其处理成四维张量,而且每条样本的结构都得是一样得,数据才能够被输入到卷积神经网络中,被卷积神经网络处理。那数据升维、降维、变形等操作细节你都要知道。

除此之外,你要想模型效果好,你还得进行数据预处理和数据增强。比如识别任务中,要求图像要清晰、要具体、内容不能太复杂、被识别对象要在图片中心等等,此时你的重点就是数据预处理,你要掌握一些预处理的基本手段和一些trick。除了数据预处理,当你建立的是一个巨大的模型,你还得对抗模型的过拟合问题,此时你还得做数据增强,你就得知道数据增强的基本手段和一些trick细节点。一个模型分值提升多少,尤其是后期哪怕是提升0.1个百分点,都是需要在预处理和增强方面大做文章才能实现的。

最后,对于一些特别的任务或者特别的目的,比如你训练的是一个对抗网络,你可能还会想把标签也一起喂入网络,让网络学出更好规律,进而输出更加逼真的图像,此时你就会用到更多数据变形的高级操作,比如embeding,比如上下采样等,这些实现方法也是都要要掌握的。后面我们只能是遇到问题,再针对问题演示这些操作。

11、图片处理函数小总结:

【深度视觉】第二章:卷积网络的数据_第21张图片

可能截图看不清,我把原文档再贴一下:   

1、读取图像:

(1)opencv库: import cv2, cv2.imread(path) #读出是BGR通道顺序,而且ndarray格式,dtype是uint8类型的数据
(2)用PIL库:from PIL import Image, Image.open(path) #返回的就是这种对象可以直接在jupyter中显示出图像 (3)用skimage库:from skimage import io, io.imread(path) #读出来的图像是RGB通道顺序,而且是ndarray格式,dtype也是uint8类型的数据

2、图片格式转换: 图片格式有PIL格式、ndarray格式、tensor格式,这三种格式之间的转换方法:
(1)PIL转ndarray:np.array(img_PIL)
(2)ndarray转PIL:Image.fromarray(img_cv2)或者Image.fromarray(img_io)

(3)ndarray、PIL转tensor:transforms = transforms.ToTensor(), img_tensor = transforms(img_cv2/img_io/img_PIL) #说明:转为tensor后像素值由之前[0,255]归一化为[0,1]的小数区间,但原通道顺序不变!

(4)tensor转ndarray:img_numpy = img_tensor.squeeze().cpu().numpy()*255.0, img_numpy = np.transpose(img_numpy, [1, 2, 0])
#说明np.squeeze(xxx)是降维,就是去掉是1的维度,np.transpose()是重整通道顺序,这些操作都是为了方便后面的可视化。

(5)当你用torchvision里面的transforms处理图像时,必须是PIL格式和tensor格式才能被处理:
from torchvision import transforms,T = transforms.RandomCrop(128),re = T(img_PIL\img_tensor) #PIL处理完毕还是显示图像,而tensor处理完毕还是tensor。你给一个array类型的就会报错!

3、显示图像:

(1)用opencv库:cv2.imshow(img) #不好用,一般和cv2.waitKey()、cv2.destoryWindow()搭配,很容易卡死,需要重启python。
(2)用matplotlib库:
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))或者plt.imshow(img[:,:,::-1])或者plt.imshow(img, cmap=plt.cm.gray) 是按RGB显示的。
plt.title('titlename'), plt.axis('off'), plt.xticks([]), plt.yticks([]), plt.show()

如果要显示多张图片:
fig,ax = plt.subplots(x,x,figsize=(x,x))
ax[i,j].imshow(img), ax[i,j].grid(False), ax[i,j].axis('off'), ax[i,j].set_title('xxx')
也可以:ax[i,j].scatter(x,y, cmp='gray')

4、保存图像:
(1)保存为ndarray格式:cv2.imwrite(path, img)
(2)保存为tensor格式:torchvision.utils.save_image(img, path)

你可能感兴趣的:(深度学习,人工智能,计算机视觉,卷积神经网络)