PyTorch中的数据增强

时间:2018-08-03 17:51:49

标签: python image-processing dataset pytorch data-augmentation

我对PyTorch中执行的数据扩充有些困惑。现在,据我所知,当我们执行数据扩充时,我们将保留原始数据集,然后添加它的其他版本(Flipping,Cropping等)。但这似乎并没有在PyTorch中发生。据参考资料了解,当我们在PyTorch中使用data.transforms时,它将一一应用。例如:

data_transforms = {
    'train': transforms.Compose([
        transforms.RandomResizedCrop(224),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
    'val': transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
}

在这里,为了进行训练,我们首先要随机裁剪图像并将其大小调整为(224,224)。然后,我们拍摄这些(224,224)图像并水平翻转它们。因此,我们的数据集现在仅包含水平翻转的图像,因此在这种情况下我们的原始图像会丢失。

我是对的吗?这种理解正确吗?如果不是,那么我们在上面的代码中(从官方文档中获取)告诉PyTorch如何保留原始图像并将其大小调整为预期的形状(224,224)

谢谢

5 个答案:

答案 0 :(得分:15)

我假设您是在问这些数据增强转换(例如,RandomHorizo​​ntalFlip)是否确实也增加了数据集的大小,还是将它们分别应用于数据集中的每个项目?而不会增加数据集的大小

运行下面的简单代码片段,我们可以观察到后来是真的,即,如果您有一个包含8张图像的数据集,并在遍历该数据集时为该数据集创建一个PyTorch数据集对象,在每个数据点上调用转换,然后返回转换后的数据点。因此,举例来说,如果您进行随机翻转,则某些数据点将作为原始数据返回,某些数据点将作为翻转数据返回(例如4个翻转数据和4个原始数据)。 换句话说,通过遍历数据集项目一次,您将获得8个数据点(有些翻转,有些则没有)。 [与扩充数据集的传统理解不一致(例如,在这种情况下,扩充数据集中有16个数据点)]

class experimental_dataset(Dataset):

def __init__(self, data, transform):
    self.data = data
    self.transform = transform

def __len__(self):
    return len(self.data.shape[0])

def __getitem__(self, idx):
    item = self.data[idx]
    item = self.transform(item)
    return item

transform = transforms.Compose([
    transforms.ToPILImage(),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor()
])

x = torch.rand(8, 1, 2, 2)
print(x)

dataset = experimental_dataset(x,transform)

for item in dataset:
print(item)

结果:(浮点数的微小差异是由转换为pil图像和向后转换引起的)

原始虚拟数据集:

tensor([[[[0.1872, 0.5518],
          [0.5733, 0.6593]]],


    [[[0.6570, 0.6487],
      [0.4415, 0.5883]]],


    [[[0.5682, 0.3294],
      [0.9346, 0.1243]]],


    [[[0.1829, 0.5607],
      [0.3661, 0.6277]]],


    [[[0.1201, 0.1574],
      [0.4224, 0.6146]]],


    [[[0.9301, 0.3369],
      [0.9210, 0.9616]]],


    [[[0.8567, 0.2297],
      [0.1789, 0.8954]]],


    [[[0.0068, 0.8932],
      [0.9971, 0.3548]]]])

转换后的数据集:

tensor([[[0.1843, 0.5490],
     [0.5725, 0.6588]]])
tensor([[[0.6549, 0.6471],
     [0.4392, 0.5882]]])
tensor([[[0.5647, 0.3255],
         [0.9333, 0.1216]]])
tensor([[[0.5569, 0.1804],
         [0.6275, 0.3647]]])
tensor([[[0.1569, 0.1176],
         [0.6118, 0.4196]]])
tensor([[[0.9294, 0.3333],
         [0.9176, 0.9608]]])
tensor([[[0.8549, 0.2275],
         [0.1765, 0.8941]]])
tensor([[[0.8902, 0.0039],
         [0.3529, 0.9961]]])

答案 1 :(得分:10)

Infinity操作在每个批次生成时都应用于原始图像。因此,您的数据集保持不变,每次迭代仅复制和转换批处理图像。

这种混淆可能是由于这样的事实,例如在您的示例中,state = (0, 1) for i in x.length - 2 downto 0 noPreSwap, withPreSwap = [#INFINITY], [#INFINITY] if (isValid(i, preSwap = false, postSwap = false)) noPreSwap += state.left if (isValid(i, preSwap = false, postSwap = true)) noPreSwap += state.right if (isValid(i, preSwap = true, postSwap = true)) withPreSwap += state.right + 1 if (isValid(i, preSwap = true, postSwap = false)) withPreSwap += state.right state = (noPreSwap.min(), withPreSwap.min()) return if state.min().isInfinity() -1 else state.min() 经常用于数据准备(调整大小/裁剪到期望的尺寸,标准化值等)和数据扩充(随机分配调整大小/裁剪,随机翻转图像等)。


您的transforms所做的是:

  • 随机调整提供的图像大小并随机裁剪以获取transforms补丁
  • 对此补丁应用或不进行随机水平翻转,机会为50/50
  • 将其转换为data_transforms['train']
  • 根据您提供的平均值和偏差值对结果(224, 224)进行归一化

您的Tensor所做的是:

  • 将图像尺寸调整为Tensor
  • 对裁剪后的尺寸图像进行中心裁剪以获得data_transforms['val']补丁
  • 将其转换为(256, 256)
  • 根据您提供的平均值和偏差值对结果(224, 224)进行归一化

(即将训练数据的随机调整大小/裁剪替换为用于验证的固定操作,以得到可靠的验证结果)


如果您不希望训练图像以50/50的机会水平翻转,只需移除Tensor行即可。

类似地,如果希望始终对图像进行中心裁剪,则将Tensor替换为transforms.RandomHorizontalFlip()transforms.RandomResizedCrop,就像transforms.Resize一样。

答案 2 :(得分:0)

是的,转换后数据集大小不会更改。每个图像都会传递给转换并返回,因此大小保持不变。

如果您希望将原始数据集与转换后的一个连接使用。

例如increased_dataset = torch.utils.data.ConcatDataset([transformed_dataset,original])

答案 3 :(得分:0)

在 PyTorch 中,有一些裁剪类型会改变数据集的大小。它们是 FiveCropTenCrop

<块引用>

CLASS torchvision.transforms.FiveCrop(尺寸)

将给定的图像裁剪成四个角和中央裁剪。

此转换返回一个图像元组,可能存在不匹配 在您的数据集返回的输入和目标数量中。见下文 有关如何处理此问题的示例。

示例:

>>> transform = Compose([
>>>    TenCrop(size), # this is a list of PIL Images
>>>    Lambda(lambda crops: torch.stack([ToTensor()(crop) for crop in crops])) # returns a 4D tensor
>>> ])
>>> #In your test loop you can do the following:
>>> input, target = batch # input is a 5d tensor, target is 2d
>>> bs, ncrops, c, h, w = input.size()
>>> result = model(input.view(-1, c, h, w)) # fuse batch size and ncrops
>>> result_avg = result.view(bs, ncrops, -1).mean(1) # avg over crops

TenCrop 是相同的加上五个补丁的翻转版本(默认使用水平翻转)。

答案 4 :(得分:-1)

TLDR:

  • 变换操作以一定的概率将一组变换应用于循环中的输入批次。因此,该模型现在在多个时期的过程中会接触到更多示例。

  • 就我个人而言,当我在自己的数据集上训练音频分类模型时,在增强之前,我的模型似乎总是以 72% 的准确率收敛。我使用了增强以及增加训练时期的数量,这将测试集中的验证准确率提高到了 89%。