Lesson 17.4 图像数据的数据预处理

Lesson 17.4 图像数据的数据预处理,第1张

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

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

  • 1)全部样本的尺寸是一致的(同时,全部样本的通道数是一致的)
  • 2)图像最终以Tensor形式被输入卷积网络
  • 3)图像被恰当地归一化

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

首先是用来调整尺寸的两个类:中心裁剪 transforms.CenterCrop() 以及 transforms.Resize() 。

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

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

import torch
import torchvision
from torchvision import transforms
from torch import nn
transform1 = transforms.Compose([transforms.Resize(256)
                              ,transforms.CenterCrop(224)])
#等价于
transform2 = nn.Sequential(transforms.Resize(256)
                        ,transforms.CenterCrop(224))
transform1
#Compose(
#    Resize(size=256, interpolation=bilinear, max_size=None, antialias=None)
#    CenterCrop(size=(224, 224))
#)
transform2
#Sequential(
#  (0): Resize(size=256, interpolation=bilinear, max_size=None, antialias=None)
#  (1): CenterCrop(size=(224, 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]之间。

data_val = torchvision.datasets.LSUN(root="/Users/zhucan/Desktop/lsun-master/data"
                                     ,classes=["church_outdoor_val","classroom_val"]
                                     ,transform = transforms.ToTensor()
                                    )
data_val
#Dataset LSUN
#    Number of datapoints: 600
#    Root location: F:\datasets2\lsun-master\data
#    Classes: ['church_outdoor_val', 'classroom_val']
#    StandardTransform
#Transform: ToTensor()
data_val[0][0]
# tensor([[[0.4627, 0.4627, 0.4667,  ..., 0.7137, 0.7098, 0.7098],
#          [0.4627, 0.4627, 0.4667,  ..., 0.7255, 0.7176, 0.7098],
#          [0.4627, 0.4627, 0.4667,  ..., 0.7333, 0.7255, 0.7176],
#          ...,
#          [0.3137, 0.3412, 0.3216,  ..., 0.6471, 0.6392, 0.6353],
#          [0.1961, 0.2353, 0.2078,  ..., 0.6471, 0.6392, 0.6314],
#          [0.1843, 0.2235, 0.1765,  ..., 0.6510, 0.6392, 0.6314]],

#         [[0.5647, 0.5647, 0.5686,  ..., 0.7059, 0.7020, 0.7020],
#          [0.5647, 0.5647, 0.5686,  ..., 0.7176, 0.7098, 0.7020],
#          [0.5647, 0.5647, 0.5686,  ..., 0.7255, 0.7176, 0.7098],
#          ...,
#          [0.3176, 0.3451, 0.3216,  ..., 0.6510, 0.6431, 0.6392],
#          [0.2000, 0.2392, 0.2118,  ..., 0.6510, 0.6431, 0.6353],
#          [0.1882, 0.2275, 0.1804,  ..., 0.6549, 0.6431, 0.6353]],

#         [[0.7882, 0.7882, 0.7922,  ..., 0.7098, 0.7059, 0.7059],
#          [0.7882, 0.7882, 0.7922,  ..., 0.7216, 0.7137, 0.7059],
#          [0.7882, 0.7882, 0.7922,  ..., 0.7294, 0.7216, 0.7137],
#          ...,
#          [0.2980, 0.3255, 0.3137,  ..., 0.6706, 0.6627, 0.6588],
#          [0.1765, 0.2196, 0.1922,  ..., 0.6706, 0.6627, 0.6549],
#          [0.1647, 0.2039, 0.1569,  ..., 0.6745, 0.6627, 0.6549]]])
data_val[0][0].max()
#tensor(1.)
data_val[0][0].min() #所有值都在0-1之间
#tensor(0.)

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

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

#1) 常见且通用的做法,该写法只适用于三通道图像
transforms.Normalize(mean=[0.5, 0.5, 0.5], #代表三个通道上需要减去的值分别是0.5
                     std=[0.5, 0.5, 0.5]) #代表三个通道上需要除以的值分别是0.5
#在保证数据范围在[0,1]的前提下,使用这个值可以令数据范围拓展到[-1,1]
#Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])

#也可写作:
transforms.Normalize(mean = 0.5, std = 0.5)
#这种写法中,Normalize类会根据通道数进行相应的计算,任意通道数的图像都可以使用
#Normalize(mean=0.5, std=0.5)

#注意区分,这种写法只能用于单通道(灰度)图像
transforms.Normalize([0.5],[0.5])
#Normalize(mean=[0.5], std=[0.5])

#2) ImageNet数据集上的均值和方差,可被用于任意实物照片分类
transforms.Normalize(mean=[0.485, 0.456, 0.406],
                     std=[0.229, 0.224, 0.225])
#Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])

#3) MNIST数据集上的均值和方差,可被用于MNIST系列
transforms.Normalize((0.1307), (0.3081))
#Normalize(mean=0.1307, std=0.3081)
#你也可以根据自己的数据集和自己希望实现的数值范围,来计算放入Normalize的值
#在LSUN数据集上尝试一下
transform = transforms.Compose([transforms.ToTensor()
                              ,transforms.Normalize(mean=[0.485, 0.456, 0.406]
                                                    ,std=[0.229, 0.224, 0.225])])
transform1 = transforms.Compose([transforms.ToTensor()
                              ,transforms.Normalize(0.5,0.5)])
data_val = torchvision.datasets.LSUN(root="/Users/zhucan/Desktop/lsun-master/data"
                                     ,classes=["church_outdoor_val","classroom_val"]
                                     ,transform = transform1
                                     )
data_val[1][0].max()
#tensor(1.)
data_val[1][0].min()
#tensor(-0.8824)                                                  

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

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/715301.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-25
下一篇 2022-04-25

发表评论

登录后才能评论

评论列表(0条)

保存