Я пытаюсь обучить модели глубокого обучения в 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
, но при таком подходе я не верю, что смогу контролировать, какие индексы могут находиться в одном и том же мини-пакете.Любое руководство будет с благодарностью.