torch.utils.data相关部分已经在上一个博客内容讲解过,如何使用Dataset和DataLoader,请参见pytorch深度学习和入门实战(二)Dataset和DataLoader使用详解
数据下载和预处理是机器学习、深度学习实际项目中耗时又重要的任务,
尤其是数据预处理,关系到数据质量和模型性能,往往要占据项目的大部分时间。
PyTorch涉及数据处理(数据装载、数据预处理、数据增强等)主要工具包及相互关系如图:
主要包括两大部分:
(1)torch.utils.data相关部分
torch.utils.data工具包,它包括以下4个类函数。
1)Dataset:是一个抽象类,其他数据集需要继承这个类,并且覆写其
中的两个方法( getitem_()、len ())。
2)DataLoader:定义一个新的迭代器,实现批量(batch)读取,打乱
数据(shuffle)并提供并行加速等功能。
3)random_split:把数据集随机拆分为给定长度的非重叠的新数据集。
4)*sampler:多种采样函数
(2)torchvision工具包相关部分
它包括4个类,各类的主要功能如下。
1)datasets:提供常用的数据集加载,设计上都是继承自
torch.utils.data.Dataset,主要包括MMIST、CIFAR10/100、ImageNet和COCO
等。
2)models:提供深度学习中各种经典的网络结构以及训练好的模型
(如果选择pretrained=True),包括AlexNet、VGG系列、ResNet系列、
Inception系列等。
3)transforms:常用的数据预处理操作,主要包括对Tensor及PIL Image
对象的操作。
4)utils:含两个函数,一个是make_grid,它能将多张图片拼接在一个
网格中;另一个是save_img,它能将Tensor保存成图片。
本节主要介绍如何使用datasets的ImageFolder处理自定义数据集,以及如何使用transforms对源数据进行预处理、增强等。下面将重点介绍transforms及ImageFolder
Transforms是指常见的图像变换功能。它们可以用Compose组合在一起。
# transforms (list of Transform objects) – list of transforms to compose.
# for example
transforms.Compose([
#将给定的 PIL.Image 进行中心切割,得到给定的 size,
#size 可以是 tuple,(target_height, target_width)。
#size 也可以是一个 Integer,在这种情况下,切出来的图片形状是正方形。
transforms.CenterCrop(10),
#切割中心点的位置随机选取
transforms.RandomCrop(20, padding=0),
#把一个取值范围是 [0, 255] 的 PIL.Image 或者 shape 为 (H, W, C) 的 numpy.ndarray,
#转换为形状为 (C, H, W),取值范围是 [0, 1] 的 torch.FloatTensor
transforms.ToTensor(),
#规范化到[-1,1]
transforms.Normalize(mean = (0.5, 0.5, 0.5), std = (0.5, 0.5, 0.5))
])
此外,还有torchvision.transforms.functional模块。函数转换(Functional transforms)提供对转换的细粒度控制。在构建一些比较复杂的转换流程(例如,在分割任务的情况下),这可能很有用。
# for example
import torchvision.transforms.functional as TF
import random
def my_segmentation_transforms(image, segmentation):
if random.random() > 0.5:
angle = random.randint(-30, 30)
image = TF.rotate(image, angle)
segmentation = TF.rotate(segmentation, angle)
# more transforms ...
return image, segmentation
ref to official api : https://pytorch.org/docs/stable/
主要有一下几类:
1)针对PIL image的处理-----> 主要包括一些图像的裁剪、翻转、亮度变化等增强
2)针对 torch.*Tensor的处理-----> tensor数据标准化和线性化等
3)转化处理(Conversion Transforms)-----> 主要是图像类型的变换,imgs->tensor, tensor->imgs
4)自定义转化处理(Generic Transforms)-----> 自定义转换函数
5)转化函数 -----> 更加细粒度的图像处理函数
中心裁剪:transforms.CenterCrop
class torchvision.transforms.CenterCrop(size)
功能:
依据给定的 size 从中心裁剪
参数:
size- (sequence or int),若为 sequence,则为(h,w),若为 int,则(size,size)
随机裁剪:transforms.RandomCrop
class
torchvision.transforms.RandomCrop(size, padding=None, pad_if_needed=False, fill=0, padding_mode='constant')
功能:
依据给定的 size 随机裁剪
参数:
size- (sequence or int),若为 sequence,则为(h,w),若为 int,则(size,size)
padding-(sequence or int, optional),此参数是设置填充多少个 pixel。当为 int 时,图像上下左右均填充 int 个,例如 padding=4,则上下左右均填充 4 个 pixel,若为 32*32,则会变成 40*40。当为 sequence 时,若有 2 个数,则第一个数表示左右扩充多少,第二个数表示上下的。当有 4 个数时,则为左,上,右,下。
fill- (int or tuple) 填充的值是什么(仅当填充模式为 constant 时有用)。int 时,各通道均填充该值,当长度为 3 的 tuple 时,表示 RGB 通道需要填充的值。
padding_mode- 填充模式,这里提供了 4 种填充模式:
1.constant,常量。2.edge 按照图片边缘的像素值来填充。3.reflect,像镜像一样对称填充(不重复边缘上的最后一个值)。 4. symmetric,像镜像一样对称填充(重复边缘上的最后一个值)
随机长宽比裁剪:transforms.RandomResizedCrop
class torchvision.transforms.RandomResizedCrop(size, scale=(0.08, 1.0), ratio=(0.75, 1.33333 33333333333), interpolation=2)
功能:
随机大小,随机长宽比裁剪原始图片,最后将图片 resize 到设定好的 size
参数:
size- 输出的分辨率
scale- 随机 crop 的大小区间,如 scale=(0.08, 1.0),表示随机 crop 出来的图片会在的 0.08倍至 1 倍之间。
ratio- 随机长宽比设置
interpolation- 插值的方法,默认为双线性插值(PIL.Image.BILINEAR)
上下左右中心裁剪:transforms.FiveCrop
class torchvision.transforms.FiveCrop(size)
功能:对图片进行上下左右以及中心裁剪,获得 5 张图片,返回一个 4D-tensor
参数:
size- (sequence or int),若为 sequence,则为(h,w),若为 int,则(size,size)
上下左右中心裁剪后翻转,transforms.TenCrop
class torchvision.transforms.TenCrop(size, vertical_flip=False)
功能:
对图片进行上下左右以及中心裁剪,然后全部翻转(水平或者垂直),获得 10 张图片,返回一个 4D-tensor。
参数:
size- (sequence or int),若为 sequence,则为(h,w),若为 int,则(size,size)
vertical_flip (bool) - 是否垂直翻转,默认为 flase,即默认为水平翻转
依概率 p 水平翻转:transforms.RandomHorizontalFlip(p=0.5)
class torchvision.transforms.RandomHorizontalFlip(p=0.5)
功能:依据概率 p 对 PIL 图片进行水平翻转
参数:
p- 概率,默认值为 0.5
依概率 p 垂直翻转:transforms.RandomVerticalFlip(p=0.5)
class torchvision.transforms.RandomVerticalFlip(p=0.5)
功能:依据概率 p 对 PIL 图片进行垂直翻转
参数:
p- 概率,默认值为 0.5
随机旋转:transforms.RandomRotation
class torchvision.transforms.RandomRotation(degrees, resample=False, expand=False, center=None, fill=None)
功能:依 degrees 随机旋转一定角度
参数:
degress- (sequence or float or int) ,若为单个数,如 30,则表示在(-30,+30)之间随机旋转若为 sequence,如(30,60),则表示在 30-60 度之间随机旋转
resample- 重采样方法选择,可选PIL.Image.NEAREST, PIL.Image.BILINEAR, PIL.Image.BICUBIC,默认为最近邻
expand- –可选扩展标志。如果为true,则展开输出以使其足够大以容纳整个旋转图像。如果为false或省略,则使输出图像与输入图像的大小相同。请注意,expand标志假定围绕中心旋转而不平移。
center- 可选为中心旋转还是左上角旋转
fill(n元组或int或float)–旋转图像外部区域的像素填充值。如果是int或float,则该值分别用于所有带区。所有标注栏的默认值为0。此选项仅适用于pillow>=5.2.0。
填充:transforms.Pad
class torchvision.transforms.Pad(padding, fill=0, padding_mode='constant')
功能:对图像进行填充
参数:
padding-(sequence or int, optional),此参数是设置填充多少个 pixel。当为 int 时,图像上下左右均填充 int 个,例如 padding=4,则上下左右均填充 4 个 pixel,若为 32*32,则会变成 40*40。当为 sequence 时,若有 2 个数,则第一个数表示左右扩充多少,第二个数表示上下的。当有 4 个数时,则为左,上,右,下。
fill- (int or tuple) 填充的值是什么(仅当填充模式为 constant 时有用)。int 时,各通道均填充该值,当长度为 3 的 tuple 时,表示 RGB 通道需要填充的值。
padding_mode- 填充模式,这里提供了 4 种填充模式:
1.constant,常量。2.edge 按照图片边缘的像素值来填充。3.reflect,像镜像一样对称填充(不重复边缘上的最后一个值)。 4. symmetric,像镜像一样对称填充(重复边缘上的最后一个值)
修改亮度、对比度和饱和度:transforms.ColorJitter
class torchvision.transforms.ColorJitter(brightness=0, contrast=0, saturation=0, hue=0)
功能:修改修改亮度、对比度和饱和度
转灰度图:transforms.Grayscale
class torchvision.transforms.Grayscale(num_output_channels=1)
功能:将图片转换为灰度图
参数:
num_output_channels- (int) ,当为 1 时,正常的灰度图,当为 3 时, 3 channel with r == g == b
仿射变换:transforms.RandomAffine
class torchvision.transforms.RandomAffine(degrees, translate=None, scale=None, shear=None, resample=False, fillcolor=0)
功能:仿射变换
依概率 p 转为灰度图:transforms.RandomGrayscale
class torchvision.transforms.RandomGrayscale(p=0.1)
功能:依概率 p 将图片转换为灰度图,若通道数为 3,则 3 channel with r == g == b
resize:transforms.Resize
class torchvision.transforms.Resize(size, interpolation=2)
功能:重置图像分辨率
参数:
size- If size is an int, if height > width, then image will be rescaled to (size * height / width, size),所以建议 size 设定为 h*w
interpolation- 插值方法选择,默认为 PIL.Image.BILINEAR
transforms.RandomChoice(transforms), 从给定的一系列 transforms 中选一个进行操作
transforms.RandomApply(transforms, p=0.5),给一个 transform 加上概率,依概率进行操作
功能:给一个 transform 加上概率,以一定的概率执行该操作
transforms.RandomOrder,将 transforms 中的操作随机打乱
功能:将 transforms 中的操作顺序随机打乱
class torchvision.transforms.Normalize(mean, std)
功能:对数据按通道进行标准化,即先减均值,再除以标准差,注意是 h*w*c
class torchvision.transforms.LinearTransformation(transformation_matrix)
功能:对矩阵做线性变化,可用于白化处理!
whitening: zero-center the data, compute the data covariance matrix
参数:
transformation_matrix (Tensor) – tensor [D x D], D = C x H x W
class torchvision.transforms.RandomErasing(p=0.5, scale=(0.02, 0.33), ratio=(0.3, 3.3), value=0, inplace=False)
功能:随机选择图像中的矩形区域并删除其像素。参见 https://arxiv.org/pdf/1708.04896.pdf
参数
p–执行随机擦除操作的概率。
scale–擦除区域相对于输入图像的比例范围。
ratio–擦除区域的纵横比范围。值–删除值。默认值为0。如果是一个整型,它将被用来清除所有像素。如果图像的channel为3,则分别用于擦除R、G、B通道。如果str为“random”,则用随机值擦除每个像素。
inplace–布尔值以使此变换就位。默认设置为False。
class torchvision.transforms.ToTensor
功能:将 PIL Image 或者 ndarray 转换为 tensor,并且归一化至[0-1]
注意事项:归一化至[0-1]是直接除以 255,若自己的 ndarray 数据尺度有变化,则需要自行修改
class torchvision.transforms.ToPILImage(mode=None)
功能:将 tensor 或者 ndarray 的数据转换为 PIL Image 类型数据
参数:
mode- 为 None 时,为 1 通道, mode=3 通道默认转换为 RGB,4 通道默认转换为 RGBA
用户自定义转换函数lambd:torchvision.transforms.Lambda(lambd)
class torchvision.transforms.Lambda(lambd)
功能: 使用用户自定义的lambd函数作为转换函数
参数:
lambd (function) – Lambda/function to be used for transform.
---------------------------------------修改色彩和饱和度等------------------------------------------------------
transforms.functional.adjust_brightness(img: torch.Tensor, brightness_factor: float) → torch.Tensor
transforms.functional.adjust_contrast(img: torch.Tensor, contrast_factor: float) → torch.Tensor
transforms.functional.adjust_gamma(img, gamma, gain=1)
transforms.functional.adjust_hue(img: torch.Tensor, hue_factor: float) → torch.Tensor
transforms.functional.adjust_saturation(img: torch.Tensor, saturation_factor: float) → torch.Tensor
---------------------------------------仿射和变换等------------------------------------------------------
transforms.functional.affine(img, angle, translate, scale, shear, resample=0, fillcolor=None)
transforms.functional.perspective(img, startpoints, endpoints, interpolation=3, fill=None)
transforms.functional.erase(img, i, j, h, w, v, inplace=False)
transforms.functional.resize(img, size, interpolation=2)
transforms.functional.pad(img, padding, fill=0, padding_mode=‘constant’)
---------------------------------------裁剪等------------------------------------------------------
transforms.functional.center_crop(img, output_size)
transforms.functional.convert_image_dtype(image: torch.Tensor, dtype: torch.dtype = torch.float32) → torch.Tensor
transforms.functional.crop(img, top, left, height, width)
transforms.functional.five_crop(img, size)
transforms.functional.ten_crop(img, size, vertical_flip=False)
transforms.functional.resized_crop(img, top, left, height, width, size, interpolation=2)
---------------------------------------翻转和旋转------------------------------------------------------
transforms.functional.hflip(img: torch.Tensor) → torch.Tensor
transforms.functional.vflip(img: torch.Tensor) → torch.Tensor
transforms.functional.rotate(img, angle, resample=False, expand=False, center=None, fill=None)
---------------------------------------图像数据类型变化等------------------------------------------------------
transforms.functional.to_grayscale(img, num_output_channels=1)
transforms.functional.to_pil_image(pic, mode=None)
transforms.functional.to_tensor(pic)
transforms.functional.pil_to_tensor(pic)
transforms.functional.normalize(tensor, mean, std, inplace=False)
# 数据预处理设置
normMean = [0.4948052, 0.48568845, 0.44682974]
normStd = [0.24580306, 0.24236229, 0.2603115]
normTransform = transforms.Normalize(normMean, normStd)
trainTransform = transforms.Compose([
transforms.Resize(32),
transforms.RandomCrop(32, padding=4),
transforms.ToTensor(),
normTransform
])
validTransform = transforms.Compose([
transforms.ToTensor(),
normTransform
])
前三行设置均值,标准差,以及数据标准化:transforms.Normalize()函数,这里是以通
道为单位进行计算均值,标准差。然后用 transforms.Compose 将所需要进行的处理给 compose 起来,并且按照顺序执行!
在训练时,依次对图片进行以下操作:
1. 随机裁剪
2. Totensor
3. 数据标准化(减均值,除以标准差)
1. 随机裁剪
第一个处理是随机裁剪,在裁剪之前先对图片的上下左右均填充上 4 个 pixel,值为0,即变成一个 4040 的数据,然后再随机进行 3232 的裁剪。例如下图,是经过 transforms.RandomCrop(32, padding=4),之后的图片,其中红色框是原始图片数据,31 列是填充的 0,28-31 行也是填充的 0
2. Totensor
第二个处理是 transforms.ToTensor()在这里会对数据进行 transpose,原来是 hwc,会经过 img = img.transpose(0, 1).transpose(0, 2).contiguous(),变成 chw 再除以 255,使得像素值归一化至[0-1]之间,来看看 Red 通道.
来看看 27 行,30 列 原来是 8 的, 经过 ToTensor 之后变成:8/255= 0.03137255
3. 数据标准化(减均值,除以标准差)
第三个处理是对图像进行标准化,通过标准化之后,至此,数据预处理完毕,最后转换成 Variable 类型,就是输入网络模型的数据了。 再来看看 Red 通道的数据:
当文件依据标签处于不同文件下时,如:
─── data
├── zhangliu
│ ├── 001.jpg
│ └── 002.jpg
├── wuhua
│ ├── 001.jpg
│ └── 002.jpg
…
我们可以利用torchvision.datasets.ImageFolder来直接构造出dataset,代码如下:
data = datasets.ImageFolder(path)
loader = data.DataLoader(data)
class torchvision.datasets.ImageFolder(root, transform=None, target_transform=None, loader=
功能:一种通用数据加载器,其中图像以这种方式排列:
root/dog/xxx.png
root/dog/xxy.png
root/dog/xxz.png
root/cat/123.png
root/cat/nsdf3.png
root/cat/asd932_.png
参数:
root (string) – 文件根目录
transform (callable, optional) –一种接受PIL图像并返回转换后的版本的函数/转换。. E.g, transforms.RandomCrop
target_transform (callable, optional) – 作用于目标对象的function/transform
loader (callable, optional) – 加载已知路径图片的函数
is_valid_file – 检查文件路径是否争取
重要特性:
classes (list): 分类名称
class_to_idx (dict): 分类名称(文件夹名称)映射到数字(自动编码的类别序号,输出的label就是这个)上的对应dict, 形式为 (class_name, class_index).
imgs (list): (image path, class_index) tuples显示,即显示(文件位置,类别序号)
ImageFolder会将目录中的文件夹名自动转化成序列,当DataLoader载入时,标签自动就是整数序列了。
下面我们利用ImageFolder读取不同目录下的图片数据,然后使用transforms进行图像预处理,预处理有多个,我们用compose把这些操作拼接在一起。然后使用DataLoader加载。
注:
1. 本次使用的数据是cifa-10 保存成png图片的合集,共有10个文件夹,分别标注为0-10,用以存放不同类别的照片。
2. .totensor 过程 图片数据会有axis变化, 格式由 hwc ----> chw, 后续如果通过plt显示的时候,.transpose((1, 2, 0))进行转置
3. 构造的dataloader的图片img 数据格式 {batch, channel, height , weight}, 和make_grid对输入数据的要求一样。
4. 可以通过ImageFolder.classes()/.class_to_idx()/.imgs(), 显示相关分类名称, 分类序列号和分类名称dict, 图片路径和分类序号dict
对处理后的数据用torchvision.utils中的save_image保存为一个png格式文件,然后用Image.open打开该png文件,详细代码如下:
from torchvision import transforms, utils
from torchvision.datasets import ImageFolder
import matplotlib.pyplot as plt
from torch.utils.data import DataLoader
"""
数据预处理
"""
my_trans = transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor()
])
"""
生成batch_size迭代器,用于加载数据
"""
train_data = ImageFolder(r'D:\Study\pytorch\PyTorch_Tutorial-master\Data\test', transform=my_trans)
train_loader = DataLoader(train_data, batch_size=8, shuffle=True, )
"""
读取第一个batch的数据,并且通过plt显示和save_image保存
"""
for i_batch, img in enumerate(train_loader):
if i_batch == 0:
# img[0] ->图像, img[1] -> label
print(img[1])
fig = plt.figure()
# img 格式 {batch, channel, height , weight}, 和make_grid要求一样;
# make_grid 批量显示多幅画面 -> 输出 {channel, height , weight}
grid = utils.make_grid(img[0], padding=0)
# tensor -> numpy; chw -> hwc, plt才能显示
plt.imshow(grid.numpy().transpose((1, 2, 0)))
plt.show()
utils.save_image(grid, 'test01.png')
break
输出:
tensor([8, 3, 1, 9, 8, 6, 3, 4])
https://pytorch.org/docs/stable/
https://github.com/TingsongYu/PyTorch_Tutorial