Я пытаюсь обучить модель глубокого обучения в PyTorch на изображениях, которые были разбиты на определенные размеры. Я хотел бы обучать свою модель с помощью мини-пакетов, но размер мини-пакета не точно делит количество примеров в каждом сегменте.
Одно решение я видел в предыдущем сообщении. должен был дополнять изображения дополнительными пробелами (либо на лету, либо все сразу в начале обучения), но я не хочу этого делать. Вместо этого я хотел бы, чтобы размер пакета был гибким во время обучения.
В частности, если N
— это количество изображений в сегменте, а B
— размер пакета, то для этого сегмента я хотел бы получить N // B
пакетов, если B
делит N
, и N // B + 1
пакетов в противном случае. В последней партии может быть меньше B
примеров.
В качестве примера предположим, что у меня есть индексы [0, 1,..., 19] включительно, и я хотел бы использовать размер пакета 3.
Индексы [0, 9] соответствуют изображениям в сегменте 0 (форма (C, W1, H1))
Индексы [10, 19] соответствуют изображениям в сегменте 1 (форма (C, W2, H2))
(Глубина канала одинакова для всех изображений). Тогда приемлемое разделение индексов будет
batches = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[9],
[10, 11, 12],
[13, 14, 15],
[16, 17, 18],
[19]
]
Я бы предпочел обрабатывать изображения с индексами 9 и 19 отдельно, потому что они имеют разные размеры.
Просматривая документацию PyTorch, я нашел BatchSampler
класс, который генерирует списки мини-пакетных индексов. Я создал собственный класс Sampler
, который эмулирует разделение индексов, описанное выше. Если это поможет, вот моя реализация для этого:
class CustomSampler(Sampler):
def __init__(self, dataset, batch_size):
self.batch_size = batch_size
self.buckets = self._get_buckets(dataset)
self.num_examples = len(dataset)
def __iter__(self):
batch = []
# Process buckets in random order
dims = random.sample(list(self.buckets), len(self.buckets))
for dim in dims:
# Process images in buckets in random order
bucket = self.buckets[dim]
bucket = random.sample(bucket, len(bucket))
for idx in bucket:
batch.append(idx)
if len(batch) == self.batch_size:
yield batch
batch = []
# Yield half-full batch before moving to next bucket
if len(batch) > 0:
yield batch
batch = []
def __len__(self):
return self.num_examples
def _get_buckets(self, dataset):
buckets = defaultdict(list)
for i in range(len(dataset)):
img, _ = dataset[i]
dims = img.shape
buckets[dims].append(i)
return buckets
Однако, когда я использую свой собственный класс Sampler
, я получаю следующую ошибку:
Traceback (most recent call last):
File "sampler.py", line 143, in <module>
for i, batch in enumerate(dataloader):
File "/home/roflcakzorz/anaconda3/lib/python3.6/site-packages/torch/utils/data/dataloader.py", line 263, in __next__
indices = next(self.sample_iter) # may raise StopIteration
File "/home/roflcakzorz/anaconda3/lib/python3.6/site-packages/torch/utils/data/sampler.py", line 139, in __iter__
batch.append(int(idx))
TypeError: int() argument must be a string, a bytes-like object or a number, not 'list'
Похоже, что класс DataLoader
ожидает передачи индексов, а не списка индексов.
Разве я не должен использовать собственный класс Sampler
для этой задачи? Я также рассматривал возможность создания пользовательского collate_fn
для перехода к DataLoader
, но с таким подходом я не верю, что смогу контролировать, какие индексы могут находиться в одном и том же мини-пакете. Мы будем очень признательны за любые рекомендации.