图片数据的基本预处理与数据增强

1 数据预处理

       当数据顺利导入数据后,我们就可以依据图像的具体情况对图像进行预处理了。与机器学习中较为固定的预处理流程不同,图像的预处理基本完全与数据本身有关。从数据采集的瞬间开始,我们就需要考虑预处理的事项。如果我们的数据是自行从网络爬取或搜索引擎采集,我们可能需要对图像进行去重、删除无效样本等操作,如果数据是自行拍摄、实验提取,那可能也需要根据实验要求进行一些删除、增加的处理。当我们将所有有效数据导入后,至少需要确保:

1)全部样本的尺寸是一致的(同时,全部样本的通道数是一致的)

2)图像最终以Tensor形式被输入卷积网络

3)图像被恰当地归一化

       其中,前两项是为了卷积神经网络能够顺利地运行起来,第三项是为了让训练过程变得更加流畅快速。在PyTorch中,所有的数据预处理都可以在导入数据的时候,通过transform参数来完成,我们通常在transform参数中填写torchvision.transforms这个模块下的类。在预处理时,我们需要使用的常规类如下所示:

Compose     transforms专用的,类似于nn.Sequential的打包功能,可以将数个transforms下的类                        打包,形成类似于管道的结构来统一执行。

CenterCrop  中心裁剪。需要输入最终希望得到的图像尺寸。

Resize           尺寸调整。需要输入最终希望得到的图像尺寸。注意区别于使用裁剪缩小尺寸或使                           用填充放大尺寸。

Normalize     归一化(Tensor Only)。对每张图像的每个通道进行归一化,每个通道上的每个像素 

                      会减去该通道像素值的均值,并除以该通道像素值的方差。

ToTensor       (PIL Only)将任意图像转变为Tensor

       无论使用怎样的卷积网络,我们都倾向于将图像调整到接近28x28或224x224的尺寸。当原图尺寸与目 标尺寸较为接近时,我们可以使用“裁剪”功能。裁剪是会按照我们输入的目标尺寸,将大于目标尺寸的 像素点丢弃的功能,因此使用裁剪必然会导致信息损失,过多的信息损失会导致卷积网络的结果变差。 当需要检测或识别的对象位于图像的中心时,可以使用中心裁剪。中心裁剪会以图像中心点为参照,按照输入的尺寸从外向内进行裁剪,被裁剪掉的像素会被直接丢弃。如果输入的尺寸大于原始图像尺寸, 则在原始图像外侧填充0,再进行中心裁剪。

       当图像的尺寸与目标尺寸相差较大,我们不能接受如此多的信息被丢弃的时候,就需要使用尺寸调整的类Resize。Resize是使用像素聚类、像素插补等一定程度上对信息进行提取或选择、并按要求的尺寸重排像素点的功能。一般来说,Resize过后的图片会呈现出与原图较为相似的信息,但图片尺寸会得到缩放。如果原始图像尺寸很大,目标尺寸很小,我们一般会优先使用Resize将图像尺寸缩小到接近目标尺寸的程度,再用裁剪让图片尺寸完全等于目标尺寸。例如,对于600*800的图像,先Resize将尺寸降到 256x256,再裁剪至224x224。

from torch import nn

transform = transforms.Compose([transforms.Resize(256)

                                                      ,transforms.CenterCrop(224)])

#等价于

transform = nn.Sequential(transforms.Resize(256)

                                          ,transforms.CenterCrop(224))

       调整完尺寸之后,我们需要对数据进行归一化,在这里使用的类是 transforms.Normalize() 。从理论上来说,图像数据的归一化不是必须的,但历史的经验告诉我们,归一化能够非常有效地改善整体训练过程速度,并对最终模型的结果造成巨大的影响,因此各大经典架构的论文和PyTorch官方都强烈建议进行归一化。这里的归一化与BN等训练过程中存在的归一化有较大的区别,这里的归一化主要是让像素值减去一个数(默认为均值)、再除以另一个数(默认是标准差),以实现对像素值大小的改变, 让模型在一个较高的起点上训练,但并不能像BN一样改变数据的分布。

       对表格数据而言,归一化是以特征为单位进行的,每个特征会单独减去自己这个特征的均值,再除以这个特征的标准差。对任意图像而言,归一化都是以通道为单位进行的,每个通道上的全部样本的全部像素点会减去通道像素的均值,再除以通道像素的标准差。为了能够对通道上的全部像素进行计算,图像在被归一化前必须被转化为Tensor。因此在实际中,我们常常将 transforms.Normalize() 常常和 transforms.ToTensor() 连用,具体如下:

    transform = transforms.Compose([transforms.ToTensor()

                                                          ,transforms.Normalize(0.5,0.5)])

:类transforms.ToTensor()已经带有归一化的功能:这个类会按照最大值255,最小值0对图片数据进行归一化,将所有图像的像素值压缩到[0,1]之间。因此类transforms.Normalize()往往是在[0,1]区间执行。因此类 transforms.Normalize() 往往是在[0,1]区间上执行。唯一的例外可能是表格数据,如果输入transforms.ToTensor() 的数据原本是二维表,那其最大值可能会远远超出255,那经过归一化后数字范围也不会在[0,1]之间。为了避免这种情况的出现,我们可以提前将二维表的数据压缩到[0,255]之间。

       在类 transforms.Normalize() 中有两个参数,一个是mean,另一个是std,分别代表需要减去的值和需要除以的值。比较常见的填写方式有以下三种:

 对图像而言,必须完成的预处理就只有尺寸调整和归一化而已。接下来看数据增强(data augmentation)。

2 数据增强

       数据增强是数据科学体系中常用的一种增加数据量的技术,它通过添加略微修改的现有数据、或从现有数据中重新合成新数据来增加数据量。使用数据增强技术可以极大程度地减弱数据量不足所带来的影响,还可以提升模型的鲁棒性、为模型提供各种“不变性”、增加模型抗过拟合的能力。常见的数据增强手段如下:

       在PyTorch中,只要利用torchvision.transforms中包含的类,就能很容易地实现几乎所有常见的增强手段。torchvision.transforms下的类可以分为四大类别:尺寸变化像素值变化视角变化以及其他变化。

      在能够让尺寸变化的类中,各类随机裁剪图像的类可以支持数据增强中的“缩放”功能(可放大,可缩小)。通常来说,如果裁剪是“随机”的,这个类一定是被用于数据增强,而不是被用于数据预处理的。这其中最常用的是transforms.RandomCrop(),常常被放在transforms.Resize()后面替代中心裁剪。

数据增强是增加数据量的技术,而上面的操作哪里增加数据量了呢?

       如果我们更换不同的数据集、不同的数据增强方式,很快就会发现,除了明确标明会生成多张裁剪图片的 FiveCrop 以及 TenCrop 两个类,其他的类都不能改变数据集中的数据总量。看上去这些进行数据增 强的类只是对数据集进行了一个整体的转化而已,并没有真正实现“数据增强”。但深度学习框架在设计上总是非常巧妙的设计。虽然代码上无法直接看出来,但当我们将使用transform处理过的数据放入训练过程时,数据增强就会被实现。我们来看看具体是怎么回事。

回顾构建的CustomDstaset类:

        我们在构建任意继承自Dataset类的、用于读取和构建数据的类CustomDataset时,将transform的使用流程写在了__getitem__(),而没有放在__init__()中,因此CustomDataset被运行时被不会自动对数据执行transform中的操作。相对的,由于继承自Dataset类,CustomDataset会将数据进行读取,并将源数据本身放入内存。当我们通过__getitem__()方法调用数据集中的任意数据时,CustomDataset会从已经存储好的原始数据中,复制出我们希望调用的那些样本,同时激活transform的相关流程,将调用的数据进行transform处理。在这种情况下,返回到我们面前的是从原始数据中复制出的样本经过transform处理后的样子,储存在内存中的原始数据并没有被改变。

       事实上,任何torchvision.datasets下用于提取数据或处理数据的类都遵守这一原则:只保存原始数据,仅在调用数据时才对数据进行transform处理,这既有利于节省内存空间(只需要保存一份数据),也利于计算速度(只对需要使用的样本才进行处理)。一般数据被读取后,可能经过分训练集测试集的sample_split,分批次的DataLoader,但他们都不会触发transform。因此当我们读取数据、分割数据时,没有任何预处理或者数据增强的操作被执行。数据增强什么时候被执行呢?——当我们从分割好的batch_size中提取出数据进行训练时。例如:

 训练时,我们会一个epoch一个epoch地进行循环,并且在每个epochs中循环所有批次。每次当 batchdata中的x和y被调用来训练时,transform就会作用于该批次中所有被提取出的样本。也就是说, 在红色箭头指向处,每个批次中的样本都会经过随机裁剪、随机旋转等图像增强操作。

       在没有transform的时候,全部数据被分割为不同批次,因此一个epoch中每个批次的数据是不同的,但全部批次组成的这个epoch的数据都是一致的,分割批次只不过改变了样本的训练顺序,并没有增加新 的样本,而循环epochs只是在原有的数据上不断进行学习。在存在transform之后,尽管每个批次中的原始数据是一致的,但在每次被调用时,这些数据都被加上了随机裁剪、随机旋转、随机水平翻转等操作,因此每个批次中的样本都变成了“新样本”,这让整个被训练的epoch都与之前的epoch产生了差异。 因此在transform存在时,无论我们循环多少epochs,每个epochs都是独一无二的,这就相当于增加 了数据量,实现了“数据增强”。相对的,当transform存在时,我们的模型一次也不会见到原始的数据。

       这种做法非常巧妙,并且非常节约内存。每次训练时,我们只需要保留原始数据,每个batch训练结束之后,被transform处理过的数据就可以被释放。数据增强也存在缺点,如果存在数据增强操作,模型的迭代周期会更长,毕竟数据中存在大量的随机性,模型收敛得会更慢。但这样得到的模型的鲁棒性和泛化能力都会更强。另一个显而易见的缺点是,数据增强中的随机性无法使用随机数种子进行控制。如果使用随机性种子进行控制,那每次进行的随机操作就会是一致的,每个epochs就会一致,这就和一次性对数据进行处理后再带入训练没有区别,这会让数据增强操作失去意义。

       小数据集算力够,倾向于使用数据增强,帮助小数据集防止过拟合。如果数据集本身就是一些比较大的图片,复杂度比较高,反而不会去使用数据增强了。

  • 22
    点赞
  • 164
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
深度学习数据预处理的流程大致包括以下几个步骤: 1. 数据采集:数据的来源可以是各种形式,包括传感器、文本、图像、音频等。采集数据的方式也有多种,比如手动标注、爬虫抓取、传感器采集等。 2. 数据清洗:清洗数据是非常重要的一步,因为数据中常常存在噪声、缺失值、异常值等问题,需要进行清理。常用的数据清洗方法包括删除重复数据、填补缺失值、去除异常值、转换数据类型等。 3. 数据标准化:由于数据通常具有不同的规模和单位,需要将数据进行标准化,以便于后续的处理。常用的数据标准化方法包括归一化、标准化、离散化等。 4. 特征提取:特征提取是将原始数据转换为机器学习算法能够理解的形式。常用的特征提取方法包括手工设计特征、基于统计的特征、基于深度学习的特征等。 5. 数据划分:将数据分成训练集、验证集和测试集三部分,以便于模型的训练和评估。一般情况下,训练集用于训练模型,验证集用于调整模型的超参数,测试集用于评估模型的性能。 6. 数据增强数据增强是通过对原始数据进行变换或扩充,来增加数据集的规模和多样性,以提高模型的泛化能力。常用的数据增强方法包括旋转、翻转、裁剪、颜色变换等。 7. 数据存储:处理好的数据可以保存为各种格式,如文本文件、二进制文件、数据库等,以便于后续的使用和分享。 以上是深度学习数据预处理的大致流程,具体的实现方法和步骤可能会有所不同,需要根据具体情况进行调整和优化。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

L_bloomer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值