web-dev-qa-db-fra.com

Comment itérer sur deux chargeurs de données simultanément en utilisant pytorch?

J'essaie de mettre en place un réseau siamois qui prend en deux images. Je charge ces images et crée deux chargeurs de données distincts.

Dans ma boucle, je veux passer simultanément par les deux chargeurs de données afin de pouvoir entraîner le réseau sur les deux images.

for i, data in enumerate(Zip(dataloaders1, dataloaders2)):

    # get the inputs
    inputs1 = data[0][0].cuda(async=True);
    labels1 = data[0][1].cuda(async=True);

    inputs2 = data[1][0].cuda(async=True);
    labels2 = data[1][1].cuda(async=True);

    labels1 = labels1.view(batchSize,1)
    labels2 = labels2.view(batchSize,1)

    # zero the parameter gradients
    optimizer.zero_grad()

    # forward + backward + optimize
    outputs1 = alexnet(inputs1)
    outputs2 = alexnet(inputs2)

La valeur de retour du chargeur de données est un tuple. Cependant, lorsque j'essaie d'utiliser Zip pour les parcourir, j'obtiens l'erreur suivante:

OSError: [Errno 24] Too many open files
Exception NameError: "global name 'FileNotFoundError' is not defined" in <bound method _DataLoaderIter.__del__ of <torch.utils.data.dataloader._DataLoaderIter object at 0x7f2d3c00c190>> ignored                           

Le Zip ne devrait-il pas fonctionner sur tous les éléments itérables? Mais il semble qu'ici, je ne puisse pas l'utiliser sur des chargeurs de données.

Y a-t-il une autre façon de poursuivre cela? Ou est-ce que j'aborde la mise en place d'un réseau siamois de manière incorrecte?

7
ce1

Je vois que vous avez du mal à faire une bonne fonction de capteur de données. je ferais:

class Siamese(Dataset):


def __init__(self, transform=None):

   #init data here

def __len__(self):
    return   #length of the data

def __getitem__(self, idx):
    #get images and labels here 
    #returned images must be tensor
    #labels should be int 
    return img1, img2 , label1, label2 
4
Manoj Acharya

Pour compléter la réponse de @ ManojAcharya:

L'erreur que vous obtenez ne provient ni de Zip() ni DataLoader() directement. Python essaie de vous dire qu'il n'a pas pu trouver l'un des fichiers de données que vous demandez (cf FileNotFoundError dans la trace d'exception), probablement dans votre Dataset.

Trouvez ci-dessous un exemple de travail utilisant DataLoader et Zip ensemble. Notez que si vous souhaitez mélanger vos données, il devient difficile de conserver les correspondances entre les 2 jeux de données. Cela justifie la solution de @ ManojAcharya.

import torch
from torch.utils.data import DataLoader, Dataset

class DummyDataset(Dataset):
    """
    Dataset of numbers in [a,b] inclusive
    """

    def __init__(self, a=0, b=100):
        super(DummyDataset, self).__init__()
        self.a = a
        self.b = b

    def __len__(self):
        return self.b - self.a + 1

    def __getitem__(self, index):
        return index, "label_{}".format(index)

dataloaders1 = DataLoader(DummyDataset(0, 9), batch_size=2, shuffle=True)
dataloaders2 = DataLoader(DummyDataset(0, 9), batch_size=2, shuffle=True)

for i, data in enumerate(Zip(dataloaders1, dataloaders2)):
    print(data)
# ([tensor([ 4,  7]), ('label_4', 'label_7')], [tensor([ 8,  5]), ('label_8', 'label_5')])
# ([tensor([ 1,  9]), ('label_1', 'label_9')], [tensor([ 6,  9]), ('label_6', 'label_9')])
# ([tensor([ 6,  5]), ('label_6', 'label_5')], [tensor([ 0,  4]), ('label_0', 'label_4')])
# ([tensor([ 8,  2]), ('label_8', 'label_2')], [tensor([ 2,  7]), ('label_2', 'label_7')])
# ([tensor([ 0,  3]), ('label_0', 'label_3')], [tensor([ 3,  1]), ('label_3', 'label_1')])
6
benjaminplanche

En ajoutant la solution de @ Aldream pour le cas où nous avons une longueur variable de l'ensemble de données et si nous voulons les parcourir tous à la même époque, nous pourrions utiliser la cycle() de itertools, a = Python Bibliothèque standard. En utilisant l'extrait de code de @Aldrem, le code mis à jour ressemblera à:

from torch.utils.data import DataLoader, Dataset
from itertools import cycle

class DummyDataset(Dataset):
    """
    Dataset of numbers in [a,b] inclusive
    """

    def __init__(self, a=0, b=100):
        super(DummyDataset, self).__init__()
        self.a = a
        self.b = b

    def __len__(self):
        return self.b - self.a + 1

    def __getitem__(self, index):
        return index

dataloaders1 = DataLoader(DummyDataset(0, 100), batch_size=10, shuffle=True)
dataloaders2 = DataLoader(DummyDataset(0, 200), batch_size=10, shuffle=True)
num_epochs = 10

for Epoch in range(num_epochs):
    for i, data in enumerate(Zip(cycle(dataloaders1), dataloaders2)):
        print(data)

Avec seulement Zip() l'itérateur sera épuisé lorsque la longueur est égale à celle du plus petit ensemble de données (ici 100). Mais avec l'utilisation de cycle(), nous répéterons le plus petit ensemble de données à moins que notre itérateur ne regarde tous les échantillons du plus grand ensemble de données (ici 200).

P.S. On peut toujours affirmer que cette approche peut ne pas être nécessaire pour atteindre la convergence tant que l'on fait des échantillons au hasard, mais avec cette approche, l'évaluation pourrait être plus facile.

2
user3901687

En plus de ce qui est déjà mentionné, cycle() et Zip() peut créer un problème de fuite de mémoire - surtout lorsque en utilisant des ensembles de données d'image! Pour résoudre cela, au lieu d'itérer comme ceci:

dataloaders1 = DataLoader(DummyDataset(0, 100), batch_size=10, shuffle=True)
dataloaders2 = DataLoader(DummyDataset(0, 200), batch_size=10, shuffle=True)
num_epochs = 10

for Epoch in range(num_epochs):

    for i, (data1, data2) in enumerate(Zip(cycle(dataloaders1), dataloaders2)):

        do_cool_things()

vous pourriez utiliser:

dataloaders1 = DataLoader(DummyDataset(0, 100), batch_size=10, shuffle=True)
dataloaders2 = DataLoader(DummyDataset(0, 200), batch_size=10, shuffle=True)
num_epochs = 10

for Epoch in range(num_epochs):
    dataloader_iterator = iter(dataloaders1)

    for i, data1 in enumerate(dataloaders2)):

        try:
            data2 = next(dataloader_iterator)
        except StopIteration:
            dataloader_iterator = iter(dataloaders1)
            data2 = next(dataloader_iterator)

        do_cool_things()

Gardez à l'esprit que si vous utilisez également des étiquettes, vous devez remplacer dans cet exemple data1 avec (inputs1,targets1) et data2 avec inputs2,targets2, comme l'a dit @Sajad Norouzi.

KUDOS à celui-ci: https://github.com/pytorch/pytorch/issues/1917#issuecomment-433698337

1
afroditi

Si vous souhaitez parcourir plusieurs jeux de données simultanément, il n'est pas nécessaire de définir votre propre classe de jeu de données, utilisez simplement TensorDataset comme ci-dessous:

dataset = torch.utils.data.TensorDataset(dataset1, dataset2)
dataloader = DataLoader(dataset, batch_size=128, shuffle=True)
for index, data in enumerate(dataloader):
    xb1, xb2 = data
    ....

Si vous souhaitez que les étiquettes ou l'itération sur plus de deux jeux de données, alimentez-les simplement en tant qu'argument au TensorDataset après le jeu de données2.

1
Sajad Norouzi