PyTorch dataloader "слишком много открытых файлов", когда файлы не должны открываться

Итак, это минимальный код, который иллюстрирует проблему:

Это набор данных:

class IceShipDataset(Dataset):
    BAND1='band_1'
    BAND2='band_2'
    IMAGE='image'

    @staticmethod
    def get_band_img(sample,band):
        pic_size=75
        img=np.array(sample[band])
        img.resize(pic_size,pic_size)
        return img

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

    def __len__(self):
        return len(self.data)  

    def __getitem__(self, idx):

        sample=self.data[idx]
        band1_img=IceShipDataset.get_band_img(sample,self.BAND1)
        band2_img=IceShipDataset.get_band_img(sample,self.BAND2)
        img=np.stack([band1_img,band2_img],2)
        sample[self.IMAGE]=img
        if self.transform is not None:
                sample=self.transform(sample)
        return sample

И это не сбой кода:

PLAY_BATCH_SIZE=4
#load data. There are 1604 examples.
with open('train.json','r') as f:
        data=f.read()
data=json.loads(data)

ds=IceShipDataset(data)
playloader = torch.utils.data.DataLoader(ds, batch_size=PLAY_BATCH_SIZE,
                                          shuffle=False, num_workers=4)
for i,data in enumerate(playloader):
        print(i)

Это дает ту странную ошибку открытых файлов в цикле for... Моя версия факела 0.3.0.post4

Если вы хотите json файл, он доступен в Kaggle (https://www.kaggle.com/c/statoil-iceberg-classifier-challenge)

Я должен упомянуть, что ошибка не имеет ничего общего с состоянием моего ноутбука:

[email protected]:~$ lsof | wc -l
89114
[email protected]:~$ cat /proc/sys/fs/file-max
791958

Что я здесь делаю неправильно?

Ответ 1

Я знаю, как исправить ошибку, но у меня нет полного объяснения, почему это происходит.

Во-первых, решение: вам нужно убедиться, что данные изображения сохранены как numpy.array s, когда вы вызываете json.loads, он загружает их как python list из float s. Это заставляет torch.utils.data.DataLoader индивидуально преобразовывать каждый поплавок в список в torch.DoubleTensor.

Посмотрите default_collate в torch.utils.data.DataLoader - ваш __getitem__ возвращает dict, который является отображением, поэтому default_collate снова вызывается для каждого элемента dict. Первая пара int s, но затем вы попадаете в данные изображения, которые являются list, т.е. A collections.Sequence - это то, где вещи становятся напуганными, поскольку default_collate вызывается для каждого элемента списка. Это явно не то, что вы намеревались. Я не знаю, какое предположение в torch относится к содержимому a list по сравнению с a numpy.array, но с учетом ошибки кажется, что это предположение нарушается.

Исправление довольно тривиально, просто убедитесь, что две полосы изображения numpy.array s, например, в __init__

def __init__(self,data,transform=None):
    self.data=[]
    for d in data:
        d[self.BAND1] = np.asarray(d[self.BAND1])
        d[self.BAND2] = np.asarray(d[self.BAND2])
        self.data.append(d)
    self.transform=transform

или после того, как вы загрузите json, что когда-либо - на самом деле не имеет значения, где вы это делаете, пока вы это делаете.


Почему приведенные выше результаты в too many open files?

Я не знаю, но, как отмечалось в комментариях, он, вероятно, будет работать с межпроцессным обменом и файлами блокировки в двух очередях, из которых берутся и добавляются данные.

Сноска: train.json недоступен для загрузки из Kaggle из-за того, что соревнование все еще открыто (??). Я сделал фиктивный json файл, который должен иметь ту же структуру и протестировал исправление в этом фиктивном файле.