在深度学习任务中,通常读入数据后,我们都需要对数据做transform操作,最后才将transform后的数据送入模型进行训练测试。
一个完整数据流pipeline可以定义为如下:
读取数据 -> transform -> 模型
本文学习pipeline中的transform部分(torchvision.transforms
)。
torchvision.transforms是torchvision中的一个用于数据增强的包,包含了很多transform操作。
torchvision.transforms.Compose(transforms)
作用:将多个transform组合起来使用。
其源码如下:
class Compose(object):
"""Composes several transforms together.
Args:
transforms (list of ``Transform`` objects): list of transforms to compose.
Example:
>>> transforms.Compose([
>>> transforms.CenterCrop(10),
>>> transforms.ToTensor(),
>>> ])
"""
def __init__(self, transforms):
self.transforms = transforms
def __call__(self, img):
for t in self.transforms:
img = t(img)
return img
def __repr__(self):
format_string = self.__class__.__name__ + '('
for t in self.transforms:
format_string += '\n'
format_string += ' {0}'.format(t)
format_string += '\n)'
return format_string
可以看到主要的__call__
方法就是对输入图像img循环所有的transform操作。
使用例子:
train_transform = transforms.Compose([
# transforms.RandomGrayscale(),
transforms.Resize((512, 512)),
transforms.RandomAffine(5),
# transforms.ColorJitter(hue=.05, saturation=.05),
# transforms.RandomCrop((88, 88)),
transforms.RandomHorizontalFlip(),
transforms.RandomVerticalFlip(),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
class QRDataset(Dataset):
def __init__(self, img_df, transform=None):
self.img_df = img_df
if transform is not None:
self.transform = transform
else:
self.transform = None
def __getitem__(self, index):
start_time = time.time()
img = Image.open(self.img_df.iloc[index]['id']).convert('RGB')
if self.transform is not None:
img = self.transform(img)
return img,torch.from_numpy(np.array(self.img_df.iloc[index]['label']))
def __len__(self):
return len(self.img_df)
train_loader = torch.utils.data.DataLoader(
QRDataset(train_jpg.iloc[train_idx],
train_transform,
), batch_size=10, shuffle=True, num_workers=20, pin_memory=True
)
])
这里定义了Resize
、RandomAffine
、RandomHorizontalFlip
等数据预处理操作,并最终作为数据读取类QRDataset
的一个参数传入,可以在内部方法__getitem__
中实现数据增强操作。
torchvision.transforms.CenterCrop(size)
作用:将给定的PIL.Image
进行中心切割,得到给定的size
,size
可以是tuple
,(target_height, target_width)
。size
也可以是一个Integer
,在这种情况下,切出来的图片的形状是正方形。
torchvision.transforms.RandomCrop(size, padding=0)
作用:切割中心点的位置随机选取。size
可以是tuple
也可以是Integer
。
torchvision.transforms.RandomHorizontalFlip
作用:随机水平翻转给定的PIL.Image
,概率为0.5。即:一半的概率翻转,一半的概率不翻转。
torchvision.transforms.RandomSizedCrop(size, interpolation=2)
作用:先将给定的PIL.Image
随机切,然后再resize
成给定的size
大小
torchvision.transforms.Pad(padding, fill=0)
作用:将给定的PIL.Image的所有边用给定的pad value填充。 padding:要填充多少像素 fill:用什么值
例子:
from torchvision import transforms
from PIL import Image
padding_img = transforms.Pad(padding=10, fill=0)
img = Image.open('test.jpg')
print(type(img))
print(img.size)
padded_img=padding(img)
print(type(padded_img))
print(padded_img.size)
<class 'PIL.PngImagePlugin.PngImageFile'>
(10, 10)
<class 'PIL.Image.Image'>
(30, 30) #由于上下左右都要填充10个像素,所以填充后的size是(30,30)
torchvision.transforms.ToTensor
作用:把一个取值范围是[0,255]
的PIL.Image
或者shape为(H,W,C)
的numpy.ndarray
,转换成形状为[C,H,W]
,取值范围是[0,1.0]
的torch.FloadTensor
class ToTensor(object):
"""Convert a ``PIL Image`` or ``numpy.ndarray`` to tensor.
Converts a PIL Image or numpy.ndarray (H x W x C) in the range
[0, 255] to a torch.FloatTensor of shape (C x H x W) in the range [0.0, 1.0].
"""
def __call__(self, pic):
"""
Args:
pic (PIL Image or numpy.ndarray): Image to be converted to tensor.
Returns:
Tensor: Converted image.
"""
return F.to_tensor(pic)
def __repr__(self):
return self.__class__.__name__ + '()'
在PyTorch中常用PIL库来读取图像数据,因此这个方法相当于搭建了PIL.Image
和Tensor的桥梁。另外要强调的是在做数据归一化之前必须要把PIL.Image
转成Tensor
torchvision.transforms.Normalize(mean, std)
作用:归一化操作。
给定均值:(R,G,B) 方差:(R,G,B),将会把Tensor正则化。
class Normalize(object):
"""Normalize a tensor image with mean and standard deviation.
Given mean: ``(M1,...,Mn)`` and std: ``(S1,..,Sn)`` for ``n`` channels, this transform
will normalize each channel of the input ``torch.*Tensor`` i.e.
``input[channel] = (input[channel] - mean[channel]) / std[channel]``
Args:
mean (sequence): Sequence of means for each channel.
std (sequence): Sequence of standard deviations for each channel.
"""
def __init__(self, mean, std):
self.mean = mean
self.std = std
def __call__(self, tensor):
"""
Args:
tensor (Tensor): Tensor image of size (C, H, W) to be normalized.
Returns:
Tensor: Normalized Tensor image.
"""
return F.normalize(tensor, self.mean, self.std)
def __repr__(self):
return self.__class__.__name__ + '(mean={0}, std={1})'.format(self.mean, self.std)
在深度学习分类检测任务中,常用的是
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]
这几个值是根据imagnet数据集计算得到的均值、方差。
torchvision.transforms.ToPILImage
作用:将shape
为(C,H,W)
的Tensor
或shape
为(H,W,C)
的numpy.ndarray
转换成PIL.Image
,值不变。
参考
https://www.jianshu.com/p/1ae863c1e66d
https://pytorch-cn.readthedocs.io/zh/latest/torchvision/torchvision-transform/