作者🕵️♂️:让机器理解语言か
专栏🎇:PyTorch
描述🎨:PyTorch 是一个基于 Torch 的 Python 开源机器学习库。
寄语💓:🐾没有白走的路,每一步都算数!🐾
介绍💬
torchvision.transforms
是一个包含了常用的图像变化方法的工具包,该工具包主要用于图像预处理、数据增强等工作之中。本实验,将详细介绍 torchvision.transforms
中常用的数据处理函数。
知识点🍉
- 🍓预处理的批操作:将预处理操作打包 → 预处理集合器
- 🍓葡萄酒数据的预处理
- 🍓图像数据的预处理
数据的预处理💎
Compose
首先让我们先来学习 torchvision.transforms.Compose()
,它的参数是一个由多个 transforms 包中的方法组成的列表。简单的说,该函数的主要目的就是将所有的预处理操作进行打包,当有数据来时,我们只需要将数据传入该函数中,就能一次性对数据进行所有的预处理操作。如下:
import torch
import torchvision
class ToTensor:
# 定义一个将原数据转为 Torch 的操作
def __call__(self, X):
return torch.from_numpy(X)
class MulTransform:
# 定义一个将所有数据翻倍的预处理操作
def __call__(self, X):
X *= 2
return X
# 定义预处理集合器
composed = torchvision.transforms.Compose([ToTensor(), MulTransform()])
composed
"""
Compose(
<__main__.ToTensor object at 0x7fa24c2c6450>
<__main__.MulTransform object at 0x7fa24bbfdf90>
)
"""
上面我们定义了一个预处理的集合器,我们只需要将数据传入 composed
中,PyTorch 就会自动对数据进行 ToTensor()
和 MulTransform()
操作。如下:
import numpy as np
data = np.array([1, 2, 3])
composed(data)
# tensor([2, 4, 6])
从结果可以看出,尽管我们的数据需要进行很多次预处理,但是我们只需要将这些预处理全部放入 Compose
中进行打包,就能形成一个数据预处理集合。当我们需要处理某些数据时,只需要简单的将数据传入这个集合即可。
接下来,我们以葡萄酒的数据预处理为例,修改上一个试验中的 WineDataset
类,使其能够输出归一化后的 Tensor 数据集。
葡萄酒数据的预处理🍀
首先,还是让我们先来定义数据集合:
from torch.utils.data import Dataset
import pandas as pd
class WineDataset(Dataset):
# 建立一个数据集合继承 Dataset 即可
def __init__(self, transform):
# I初始化数据
# 以pandas的形式读入数据
xy = pd.read_csv(
"https://labfile.oss.aliyuncs.com/courses/2316/wine.csv", header=None)
self.n_samples = xy.shape[0]
# 这里我们就不做Tensor的转换了,将其全部放入 transform 中
self.x_data = xy.values[:, 1:]
self.y_data = xy.values[:, 0].reshape(-1,1) # 转为二维向量,和x匹配
# 数据预处理集合
self.transform = transform
# 返回 dataset[index]
def __getitem__(self, index):
sample = self.x_data[index], self.y_data[index]
if self.transform:
sample = self.transform(sample)
return sample
# 返回数据长度
def __len__(self):
return self.n_samples
可以看出 WineDataset
类中的代码和上一个试验大致相同,我们只是多加了一个 transform 变量,即数据预处理操作的集合。该变量并没有在 WineDataset
类 中被定义,只是作为一个参数被传入。
这样做有一个好处就是,当我们需要在原来的基础上添加新的预处理操作时,我们只需要在模型外重新定义 transform 变量即可,无需修改原来类中的代码。
接下来,就让我们来定义预处理操作了。首先,让我们来定义数据的归一化操作,这里使用最大最小归一化:
class Normalization:
def __call__(self, sample):
inputs, targets = sample
amin, amax = inputs.min(), inputs.max() # 求最大最小值
inputs = (inputs-amin)/(amax-amin) # (矩阵元素-最小值)/(最大值-最小值)
return inputs, targets
# 测试代码
a = 10*np.random.random((5, 5))
# 测试数据 前4列表示特征,最后一列表示标签
data = [a[:, 0:4], a[:, 4]]
Normalization()(data)
接下来,让我们来定义数据的转化操作,即将原数据类型转为 Tensor :
class ToTensor:
def __call__(self, sample):
inputs, targets = sample
return torch.from_numpy(inputs), torch.from_numpy(targets)
# 测试代码
a = 10*np.random.random((5, 5))
# 测试数据 前4列表示特征,最后一列表示标签
data = [a[:, 0:4], a[:, 4]]
ToTensor()(data)
最后,让我们使用这两个预处理操作,来处理葡萄酒数据。
我们无需修改上面代码,只需将其封装到 Compose
中,再传入即可。
# 定义 composed
composed = torchvision.transforms.Compose([Normalization(), ToTensor()])
# 传入该参数,即可获得一系列预处理之后的数据
dataset = WineDataset(transform=composed)
first_data = dataset[0]
features, labels = first_data
# 输出类型观察数据是否发生改变
print(type(features), type(labels))
# 输出内容观察数据是否进行了归一
print(features, labels)
如上,我们使用了自定义的预处理方法,完成了 PyTorch 类型的数据集的预处理。当然,除了自定义的预处理方法外,PyTorch 还为我们提供了很多封装好的预处理操作。
图像的预处理🍀
torchvision.transforms
中有很多关于图像预处理的函数。接下来,让我们对一些常用的图像处理函数进行阐述。如果你需要查看所有的函数,可以访问 官方网址。
为了能够更好的阐述这些函数,让我们以一张图片为例。首先,让我们来加载这张图片:
!wget -nc "https://labfile.oss.aliyuncs.com/courses/2534/cat.jpg"
import torchvision.transforms as transforms
from PIL import Image
import matplotlib.pyplot as plt
from matplotlib.pyplot import imshow
%matplotlib inline
img = Image.open("cat.jpg")
imshow(img)
从中心开始,裁剪给定大小的 PIL 图像:transforms.CenterCrop
。
# torchvision.transforms.CenterCrop(size):从中心开始,裁剪给定大小的 PIL 图像
transform = transforms.CenterCrop((64, 200))
new_img = transform(img)
imshow(new_img)
改变图片的亮度、对比度和饱和度:transforms.ColorJitter
。
# transforms.ColorJitter(brightness=0, contrast=0, saturation=0, hue=0):
# 改变图片的亮度、对比度和饱和度
plt.subplot(221)
imshow(img)
# r随机改变亮度
my_img1 = transforms.ColorJitter((0.5, 0.6))(img)
plt.subplot(222)
imshow(my_img1)
# 随机改变对比度
my_img2 = transforms.ColorJitter(0, (0.5, 0.6))(img)
plt.subplot(223)
imshow(my_img2)
# 随机改变饱和度
my_img3 = transforms.ColorJitter(0, 0, (0.5, 0.6))(img)
plt.subplot(224)
imshow(my_img3)
plt.show()
将图像转为灰度图像:torchvision.transforms.Grayscale(num_output_channels )
。
- 如果返回的图像是单通道 num_output_channels = 1。
- 如果返回的图像是 3 通道,其中 num_output_channels = 3。
plt.subplot(1, 3, 1)
imshow(img)
my_img1 = transforms.Grayscale(1)(img)
plt.subplot(132)
imshow(my_img1, 'gray')
my_img2 = transforms.Grayscale(3)(img)
plt.subplot(133)
imshow(my_img2)
使用给定的 pad 值将给定的 PIL 图像四处填充:ransforms.Pad(padding,fill,padding_mode)
。
# transforms.Pad(padding,fill = 0,padding_mode ='constant' ):
# 使用给定的 pad 值将给定的 PIL 图像四处填充
plt.subplot(121)
imshow(img)
# 四周加边界
my_img = transforms.Pad(padding=20, fill=(0, 255, 255),
padding_mode='constant')(img)
plt.subplot(122).set_title("Pad")
imshow(my_img)
保持图像中心不变的中心仿射变换(可以理解为不同程度的旋转,再在空余位置补 0):transforms.RandomAffine (degrees, translate, scale, shear, resample, fillcolor)
。
# transforms.RandomAffine(degrees, translate=None, scale=None,
# shear=None, resample=False, fillcolor=0):
# 保持图像中心不变的中心仿射变换(可以理解为不同程度的旋转,再在空余位置补 0)
my_img1 = transforms.RandomAffine(60)(img)
plt.subplot(221).set_title("rotate_only")
imshow(my_img1)
my_img2 = transforms.RandomAffine(60, translate=(0.3, 0.3))(img)
plt.subplot(222).set_title("rotate_translate")
imshow(my_img2)
my_img3 = transforms.RandomAffine(60, scale=(2.0, 2.1))(img)
plt.subplot(223).set_title("rotate_scale")
imshow(my_img3)
my_img4 = transforms.RandomAffine(0, shear=60)(img)
plt.subplot(224).set_title("shear_only")
imshow(my_img4)
plt.tight_layout()
将原图像进行随机裁剪、裁剪后重新放缩到 size 大小:torchvision.transforms.RandomResizedCrop(size, scale,ratio,interpolation)
。
#torchvision.transforms.RandomResizedCrop(size, scale,ratio,interpolation)
new_img = transforms.RandomResizedCrop((128, 126), scale=(0.08, 1.0),
ratio=(0.75, 1.333333333), interpolation=2)(img)
imshow(new_img)
当然除了上面这些相关图像的处理外,torchvision.transforms
中还有很多图像处理的函数,你可以按照上面代码的思路对其他函数进行检验。官方 API 请点击这里。
实验总结🔑
本实验详细的阐述了 torchvision.transforms
的使用方法和 torchvision.transforms
中内置的图像处理函数。这些预处理操作时非常重要的,一系列好的数据预处理操作,可以大大的加快模型的收敛速度,提高模型的准确率和鲁棒性。