pytorch 数据增强transforms文档阅读笔记

前言

我们在刚开始学习训练网络的时候,经常会遇到因为数据集不够大导致模型欠拟合,为了解决这个问题我们有两种常见的方法,其中之一简单粗暴即增大数据集,但是在数据采集有困难的情况下也有一些比较取巧的办法。数据增强说白了就是将原有数据进行变换,增强数据的丰富性,减少模型对一些实际意义不重要的特征的依赖。

1. transforms随机性

pytorch中最常用的对数据进行增强的工具当属torchvision中的transforms模块。Transforms顾名思义就是对数据进行变换。

shape_aug = torchvision.transforms.RandomResizedCrop(
    (200, 200), scale=(0.1, 1), ratio=(0.5, 2))
shape_aug(img)

上面的代码使用transforms的RandomResizedCrop随机裁剪对输入图片进行裁剪。这样的操作是随机的,连续调用两次shape_aug得到的裁剪结果可能不一致。

2. transforms组合

我么可以利用transforms.Compose来将不同的数据变换按顺序组合起来。

比如这样

transformer = transforms.Compose([
                transforms.Resize((224, 224)),
                # 随机旋转
                transforms.RandomRotation(15, expand=False),
                transforms.RandomHorizontalFlip(p=0.1),
                transforms.RandomVerticalFlip(p=0.1),
                # 中心裁剪
                transforms.CenterCrop(224),
                transforms.ToTensor(),
                transforms.Normalize(mean=[0.85223039, 0.8524969, 0.8526602],
                                     std=[0.27402772, 0.2744828, 0.2744293])
            ])

上面的代码展示了使用transforms.Compose将不同的transforms组合,记住这样的变换是有顺序的。

有时候我们使用的图像变换并不具有随机性,比如CenterCrop,transforms搭配了以下方法来增强随机性

  • transforms.RandomChoice:从给定的一系列transforms中选一个进行操作

  • transforms.RandomApply:给一个transforms加上概率,以一定的概率执行该操作

  • transforms.RandomOrder:将transforms中的操作顺序随机打乱

transformer = transforms.Compose([
                transforms.RandomChoice([transforms.RandomVerticalFlip(p=1), transforms.RandomHorizontalFlip(p=1)]),
    
                transforms.RandomApply([transforms.RandomAffine(degrees=0, shear=45, fillcolor=(255, 0, 0)),
                                                               transforms.Grayscale(num_output_channels=3)], p=0.5),
    
                transforms.RandomOrder([transforms.RandomRotation(15),
                                        transforms.Pad(padding=32),
                                        transforms.RandomAffine(degrees=0, translate=(0.01, 0.1), scale=(0.9, 1.1))]),

                transforms.ToTensor(),
                transforms.Normalize(mean=[0.85223039, 0.8524969, 0.8526602],
                                     std=[0.27402772, 0.2744828, 0.2744293])
             
            ])

记得前面我们讲过的符号编程,使用torchscript来种从PyTorch代码创建可序列化和可优化模型的方法,如果你将要在代码中将transforms脚本化就不能使用transforms.Compose而应该使用torch.nn.Sequential。具体实现如下。

transforms = torch.nn.Sequential(
    transforms.CenterCrop(10),
    transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)),
)
scripted_transforms = torch.jit.script(transforms)

3. transforms特殊情况

当然还有各种各样的对数据进行变换的操作因为时间限制,也不一一展开讲了,但是有一点要注意的是,有的transforms操作只能适用于特定的数据类型。

大部分方法同时可以对PIL Image类型和Tensor类型的数据进行处理,部分方法只能对PIL Image类型的数据进行处理或者是只能对Tensor类型的数据进行处理。

方法PIL ImageTensor
RandomChoice×
RandomOrder×
LinearTransformation×
Normalize×
RandomErasing×
ConvertImageDtype×

3. 适合懒人的transforms

涉及到两个类,第一个就是自动增强的方法类,第二个类描述自动增强策略,作为自动增强方法类的初始化参数用来指定自动增强策略。

Pytorch提供了现成的几个策略可供选择,包含有imagenet训练采取的数据增强策略

torchvision.transforms.AutoAugment
torchvision.transforms.AutoAugmentPolicy

具体使用方法

import torch
import torchvision.transforms as T
tensor_image = torch.randint(0, 256, size=(3, 256, 256), dtype=torch.uint8)
auto_augmentation = T.AutoAugment(T.AutoAugmentPolicy.CIFAR10)
out_image3 = auto_augmentation(tensor_image)

4. 函数式转换functional transforms

functional可以提供了一些更加精细的变换,用于搭建复杂的变换流水线。和前面的变换相反,函数变换的参数不包含随机数种子生成器。这意味着你必须指定所有参数的值,但是你可以自己引入随机数。

PyTorch在torchvision.transforms模块中,给定了很多官配transform,但是当你需要自定义Transforms的时候functional transforms就可以起作用了。

我们以官方的centerCrop为例,查看与源码发现调用了一个F.center_crop

[docs]class CenterCrop(torch.nn.Module):
    """Crops the given image at the center.
    If the image is torch Tensor, it is expected
    to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions.
    If image size is smaller than output size along any edge, image is padded with 0 and then center cropped.

    Args:
        size (sequence or int): Desired output size of the crop. If size is an
            int instead of sequence like (h, w), a square crop (size, size) is
            made. If provided a sequence of length 1, it will be interpreted as (size[0], size[0]).
    """

    def __init__(self, size):
        super().__init__()
        self.size = _setup_size(size, error_msg="Please provide only two dimensions (h, w) for size.")

[docs]    def forward(self, img):
        """
        Args:
            img (PIL Image or Tensor): Image to be cropped.

        Returns:
            PIL Image or Tensor: Cropped image.
        """
        return F.center_crop(img, self.size)

往上翻可以发现这个F的出处

from . import functional as F
from .functional import InterpolationMode, _interpolation_modes_from_int

也就是说,我么使用functional一方面其实是调用更加底层的接口来实现我们自己的transforms,另一方面则在我们不需要随机性或者想要设置随机数种子让transforms变换操作能够被复现的时候我们可以采用这个方法。

函数式变换和之前讲的随机变换进行对比。

torchvision.transforms.RandomAffine(
    degrees, 
    translate=None, 
    scale=None, 
    shear=None, 
    interpolation=<InterpolationMode.NEAREST: 'nearest'>, 
    fill=0, 
    fillcolor=None, 
    resample=None)
torchvision.transforms.functional.affine(
    img: torch.Tensor, 
    angle: float, 
    translate: List[int], 
    scale: float, 
    shear: List[float], 
    interpolation: torchvision.transforms.functional.InterpolationMode = <InterpolationMode.NEAREST: 'nearest'>, 
    fill: Optional[List[float]] = None, 
    resample: Optional[int] = None, 
    fillcolor: Optional[List[float]] = None) → torch.Tensor

下面的例子展示了直接调用torchvision.transforms.functional底层逻辑来实现一些图像变换

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

我们也可以自己实现一个transforms类,

import torchvision.transforms.functional as TF
import random

class MyRotationTransform:
    """Rotate by one of the given angles."""

    def __init__(self, angles):
        self.angles = angles

    def __call__(self, x):
        angle = random.choice(self.angles)
        return TF.rotate(x, angle)

rotation_transform = MyRotationTransform(angles=[-30, -15, 0, 15, 30])
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值