Увеличение данных в PyTorch
Я немного сбит с толку насчет дополнения данных, выполненного в PyTorch. Теперь, насколько мне известно, когда мы выполняем расширение данных, мы СОХРАНЯЕМ наш исходный набор данных, а затем добавляем другие его версии (Flipping, Cropping... и т. Д.). Но это не похоже на то, что происходит в PyTorch. Насколько я понял из ссылок, когда мы используем data.transforms
в PyTorch, то он применяет их один за другим. Так, например:
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)
?
Спасибо
6 ответов
transforms
операции применяются к вашим исходным изображениям при каждом создании серии. Таким образом, ваш набор данных остается без изменений, только пакетные изображения копируются и преобразуются при каждой итерации.
Путаница может возникнуть из-за того, что часто, как в вашем примере, transforms
используются как для подготовки данных (изменение размера / обрезки до ожидаемых размеров, нормализации значений и т. д.), так и для увеличения данных (рандомизация изменения размера / обрезки, случайное переворачивание изображений и т. д.).
Что твое data_transforms['train']
делает это:
- Произвольно изменить размер предоставленного изображения и случайно обрезать его, чтобы получить
(224, 224)
пластырь - Применить или нет случайное горизонтальное отражение к этому патчу, с вероятностью 50/50
- Преобразуйте это в
Tensor
- Нормализовать полученный
Tensor
, учитывая среднее значение и значения отклонения, которые вы предоставили
Что твое data_transforms['val']
делает это:
- Измените размер своего изображения на
(256, 256)
- Центр обрезать измененное изображение, чтобы получить
(224, 224)
пластырь - Преобразуйте это в
Tensor
- Нормализовать полученный
Tensor
, учитывая среднее значение и значения отклонения, которые вы предоставили
(т. е. случайное изменение размера / обрезки для обучающих данных заменяется фиксированной операцией для проверочной, чтобы иметь надежные результаты проверки)
Если вы не хотите, чтобы ваши тренировочные образы отображались горизонтально с вероятностью 50/50, просто удалите transforms.RandomHorizontalFlip()
линия.
Точно так же, если вы хотите, чтобы ваши изображения всегда были по центру, замените transforms.RandomResizedCrop
от transforms.Resize
а также transforms.CenterCrop
как сделано для data_transforms['val']
,
Я предполагаю, что вы спрашиваете, действительно ли эти преобразования дополнения данных (например, RandomHorizontFlip) также увеличивают размер набора данных, или они применяются к каждому элементу в наборе данных по одному и не увеличивают размер набора данных.
Запустив следующий простой фрагмент кода, мы могли заметить, что последнее верно, т. Е. Если у вас есть набор данных из 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)
Результаты: (Небольшие различия в числах с плавающей точкой вызваны преобразованием в изображение и обратно)
Исходный набор фиктивных данных:
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]]])
Да, размер набора данных не меняется после преобразований. Каждое изображение передается на преобразование и возвращается, поэтому размер остается прежним.
Если вы хотите использовать исходный набор данных с преобразованным одним, объедините их.
например
increased_dataset = torch.utils.data.ConcatDataset([transformed_dataset,original])
TL; DR:
Операция преобразования применяет набор преобразований с определенной вероятностью к входному пакету, который входит в цикл. Таким образом, модель теперь подвергается большему количеству примеров в течение нескольких эпох.
Лично я, когда тренировал модель классификации аудио на моем собственном наборе данных, перед расширением, моя модель всегда сходилась с точностью 72%. Я использовал аугментацию вместе с увеличением количества эпох обучения, что повысило точность проверки в тестовом наборе до 89 процентов.
Целью аугментации данных является увеличение разнообразия обучающего набора данных.
Несмотря на то,
data.transforms
не изменяет размер набора данных, однако каждую эпоху, когда мы вспоминаем набор данных, будет выполняться операция преобразования, а затем получать разные данные.
Я немного изменил код @Ashkan372 для вывода данных для нескольких эпох:
import torch
from torchvision import transforms
from torch.utils.data import TensorDataset as Dataset
from torch.utils.data import DataLoader
class experimental_dataset(Dataset):
def __init__(self, data, transform):
self.data = data
self.transform = transform
def __len__(self):
return 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('the original data: \n', x)
epoch_size = 3
batch_size = 4
dataset = experimental_dataset(x,transform)
for i in range(epoch_size):
print('----------------------------------------------')
print('the epoch', i, 'data: \n')
for item in DataLoader(dataset, batch_size, shuffle=False):
print(item)
Результат:
the original data:
tensor([[[[0.5993, 0.5898],
[0.7365, 0.5472]]],
[[[0.1878, 0.3546],
[0.2124, 0.8324]]],
[[[0.9321, 0.0795],
[0.4090, 0.9513]]],
[[[0.2825, 0.6954],
[0.3737, 0.0869]]],
[[[0.2123, 0.7024],
[0.6270, 0.5923]]],
[[[0.9997, 0.9825],
[0.0267, 0.2910]]],
[[[0.2323, 0.1768],
[0.4646, 0.4487]]],
[[[0.2368, 0.0262],
[0.2423, 0.9593]]]])
----------------------------------------------
the epoch 0 data:
tensor([[[[0.5882, 0.5961],
[0.5451, 0.7333]]],
[[[0.3529, 0.1843],
[0.8314, 0.2118]]],
[[[0.9294, 0.0784],
[0.4078, 0.9490]]],
[[[0.6941, 0.2824],
[0.0863, 0.3725]]]])
tensor([[[[0.7020, 0.2118],
[0.5922, 0.6235]]],
[[[0.9804, 0.9961],
[0.2902, 0.0235]]],
[[[0.2314, 0.1765],
[0.4627, 0.4471]]],
[[[0.0235, 0.2353],
[0.9569, 0.2392]]]])
----------------------------------------------
the epoch 1 data:
tensor([[[[0.5882, 0.5961],
[0.5451, 0.7333]]],
[[[0.1843, 0.3529],
[0.2118, 0.8314]]],
[[[0.0784, 0.9294],
[0.9490, 0.4078]]],
[[[0.2824, 0.6941],
[0.3725, 0.0863]]]])
tensor([[[[0.2118, 0.7020],
[0.6235, 0.5922]]],
[[[0.9804, 0.9961],
[0.2902, 0.0235]]],
[[[0.2314, 0.1765],
[0.4627, 0.4471]]],
[[[0.0235, 0.2353],
[0.9569, 0.2392]]]])
----------------------------------------------
the epoch 2 data:
tensor([[[[0.5882, 0.5961],
[0.5451, 0.7333]]],
[[[0.3529, 0.1843],
[0.8314, 0.2118]]],
[[[0.0784, 0.9294],
[0.9490, 0.4078]]],
[[[0.6941, 0.2824],
[0.0863, 0.3725]]]])
tensor([[[[0.2118, 0.7020],
[0.6235, 0.5922]]],
[[[0.9961, 0.9804],
[0.0235, 0.2902]]],
[[[0.2314, 0.1765],
[0.4627, 0.4471]]],
[[[0.0235, 0.2353],
[0.9569, 0.2392]]]])
В разные эпохи мы получаем разные результаты!
В PyTorch есть типы обрезки, которые НЕ изменяют размер набора данных. ЭтиFiveCrop
а также :
КЛАСС 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
то же самое плюс перевернутая версия из пяти патчей (по умолчанию используется горизонтальный переворот).