数据预处理
当我们将所有有效数据导入后,至少需要确保:
- 全部样本的尺寸是一致的(同时,全部样本的通道数是一致的)
- 图像最终以Tensor形式被输入卷积网络
- 图像被恰当地归一化
其中,前两项是为了卷积神经网络能够顺利地运行起来,第三项是为了让训练过程变得更加流畅快速。在PyTorch中,所有的数据预处理都可以在导入数据的时候,通过transform参数来完成,我们通常在transform参数中填写torchvision.transforms这个模块下的类。在预处理时,我们需要使用的常规类如下所示:
- Compose: transforms专用的,类似于nn.Sequential的打包功能,可以将数个transforms下的类打包,形成类似于管道的结构来统一执行。
- CenterCrop: 中心裁剪。需要输入最终希望得到的图像尺寸。
- Resize: 尺寸调整。需要输入最终希望得到的图像尺寸。注意区别于使用裁剪缩小尺寸或使用填充放大尺寸。
- Normalize: 归一化(Tensor Only)。对每张图像的每个通道进行归一化,每个通道上的每个像素会减去该通道像素值的均值,并除以该通道像素值的方差。
- ToTensor: (PIL Only)将任意图像转变为Tensor
1 调整尺寸
无论使用怎样的卷积网络,我们都倾向于将图像调整到接近28x28或224x224的尺寸。
1.1 剪裁 Crop
当原图尺寸与目 标尺寸较为接近时,我们可以使用**裁剪**功能。裁剪是会按照我们输入的目标尺寸,将大于目标尺寸的 像素点丢弃的功能,因此使用裁剪必然会导致信息损失,过多的信息损失会导致卷积网络的结果变差。 当需要检测或识别的对象位于图像的中心时,可以使用中心裁剪。**中心裁剪**会以图像中心点为参照,按照输入的尺寸从外向内进行裁剪,被裁剪掉的像素会被直接丢弃。如果输入的尺寸大于原始图像尺寸, 则在原始图像外侧填充0,再进行中心裁剪。
1.2 Resize
当图像的尺寸与目标尺寸相差较大,我们不能接受如此多的信息被丢弃的时候,就需要使用尺寸调整的类Resize。**Resize**是使用像素聚类、像素插补等一定程度上对信息进行提取或选择、并按要求的尺寸重排像素点的功能。一般来说,Resize过后的图片会呈现出与原图较为相似的信息,但图片尺寸会得到缩放。
如果原始图像尺寸很大,目标尺寸很小,我们一般会优先使用Resize将图像尺寸缩小到接近目标尺寸的程度,再用裁剪让图片尺寸完全等于目标尺寸。例如,对于600*800的图像,先Resize将尺寸降到 256x256,再裁剪至224x224。
from torch import nn
import torchvision.transforms as transforms
transform = transforms.Compose([transforms.Resize(256),transforms.CenterCrop(224)])
#等价于
transform = nn.Sequential(transforms.Resize(256),transforms.CenterCrop(224))
2 归一化/标准化 Normalize()
调整完尺寸之后,我们需要对数据进行归一化,在这里使用的类是 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,分别代表需要减去的值和需要除以的值。
对图像而言,必须完成的预处理就只用尺寸调整和归一化而已。接下来会有一些操作比如数据增强等。