Обучение на мини-партиях разного размера

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


person Roflcakzorz    schedule 03.06.2018    source источник


Ответы (2)


У вас есть 2 сети для каждого из образцов (размер ядра cnn должен быть исправлен). Если да, просто передайте указанный выше custom_sampler в аргументы batch_sampler класса DataLoader. Это решило бы проблему.

person Kris    schedule 05.06.2018
comment
Я не уверен, что понимаю вопрос. Я использую CNN для обработки изображений, но изображения имеют переменный размер, и выходные данные CNN не обязательно должны быть фиксированного размера. К сожалению, я получаю ошибки, когда использую свой CustomSampler в качестве аргумента для класса DataLoader. - person Roflcakzorz; 09.06.2018
comment
Я думаю, что вы хотите сказать, что ваши изменения размера партии (-1, 1, 28, 28), вы хотите сказать, что изображения имеют одинаковый размер. Если нет, не могли бы вы показать мне свой код? Кроме того, вы можете посмотреть здесь мою реализацию RandomSampler I. думаю, вы можете изменить его для вашего случая. - person Kris; 09.06.2018
comment
Да, поэтому я предполагаю, что здесь могут измениться две вещи: размер пакета и пространственные размеры изображений в пакете. Однако для любого заданного пакета изображения в этом пакете имеют одинаковое пространственное измерение. - person Roflcakzorz; 12.06.2018
comment
Оказывается, моя ошибка заключалась в вызове моего CustomSampler при вызове DataLoader. К сожалению, я только недавно понял, что DataLoader имеет отдельные аргументы ключевого слова для сэмплера и batch_sampler. Спасибо, что указали мне на это. Класс CustomSampler, который я реализовал, теперь работает, как и ожидалось. - person Roflcakzorz; 12.06.2018

Привет, так как каждый пакет должен содержать изображения одного размера, ваш CustomSampler отлично работает, его нужно передать в качестве аргумента mx.gluon.data.DataLoader с ключевым словом batch_sampler. Однако, как указано в документах, помните следующее:

"Не указывайте shuffle, sampler и last_batch, если указано batch_sampler"

person kithri    schedule 21.01.2020