61 votes

Comment obtenir des mini-lots en pytorch de manière propre et efficace?

J'ai essayé de faire une chose simple qui consiste à former un modèle linéaire avec la Descente de Gradient Stochastique (SGD) à l'aide de la torche:

import numpy as np

import torch
from torch.autograd import Variable

import pdb

def get_batch2(X,Y,M,dtype):
    X,Y = X.data.numpy(), Y.data.numpy()
    N = len(Y)
    valid_indices = np.array( range(N) )
    batch_indices = np.random.choice(valid_indices,size=M,replace=False)
    batch_xs = torch.FloatTensor(X[batch_indices,:]).type(dtype)
    batch_ys = torch.FloatTensor(Y[batch_indices]).type(dtype)
    return Variable(batch_xs, requires_grad=False), Variable(batch_ys, requires_grad=False)

def poly_kernel_matrix( x,D ):
    N = len(x)
    Kern = np.zeros( (N,D+1) )
    for n in range(N):
        for d in range(D+1):
            Kern[n,d] = x[n]**d;
    return Kern

## data params
N=5 # data set size
Degree=4 # number dimensions/features
D_sgd = Degree+1
##
x_true = np.linspace(0,1,N) # the real data points
y = np.sin(2*np.pi*x_true)
y.shape = (N,1)
## TORCH
dtype = torch.FloatTensor
# dtype = torch.cuda.FloatTensor # Uncomment this to run on GPU
X_mdl = poly_kernel_matrix( x_true,Degree )
X_mdl = Variable(torch.FloatTensor(X_mdl).type(dtype), requires_grad=False)
y = Variable(torch.FloatTensor(y).type(dtype), requires_grad=False)
## SGD mdl
w_init = torch.zeros(D_sgd,1).type(dtype)
W = Variable(w_init, requires_grad=True)
M = 5 # mini-batch size
eta = 0.1 # step size
for i in range(500):
    batch_xs, batch_ys = get_batch2(X_mdl,y,M,dtype)
    # Forward pass: compute predicted y using operations on Variables
    y_pred = batch_xs.mm(W)
    # Compute and print loss using operations on Variables. Now loss is a Variable of shape (1,) and loss.data is a Tensor of shape (1,); loss.data[0] is a scalar value holding the loss.
    loss = (1/N)*(y_pred - batch_ys).pow(2).sum()
    # Use autograd to compute the backward pass. Now w will have gradients
    loss.backward()
    # Update weights using gradient descent; w1.data are Tensors,
    # w.grad are Variables and w.grad.data are Tensors.
    W.data -= eta * W.grad.data
    # Manually zero the gradients after updating weights
    W.grad.data.zero_()

#
c_sgd = W.data.numpy()
X_mdl = X_mdl.data.numpy()
y = y.data.numpy()
#
Xc_pinv = np.dot(X_mdl,c_sgd)
print('J(c_sgd) = ', (1/N)*(np.linalg.norm(y-Xc_pinv)**2) )
print('loss = ',loss.data[0])

le code fonctionne très bien et tout bien que mes get_batch2 méthode semble vraiment dum/naïve, c'est probablement parce que je suis nouveau à pytorch mais je n'ai pas trouvé un bon endroit où elles discuter de la façon de récupérer des données des lots. Je suis allé à travers leurs tutoriels (http://pytorch.org/tutorials/beginner/pytorch_with_examples.html) et à travers l'ensemble de données (http://pytorch.org/tutorials/beginner/data_loading_tutorial.html) avec pas de chance. Les tutoriels tous semblent supposer que l'on a déjà du lot et du lot-taille au début et puis continue à s'entraîner avec les données sans les modifier (en particulier à http://pytorch.org/tutorials/beginner/pytorch_with_examples.html#pytorch-variables-and-autograd).

Donc ma question est ai-je vraiment besoin de tourner mes données dans numpy pour que je puisse aller chercher de l'échantillon aléatoire, puis tourner de nouveau à pytorch Variable pour être en mesure de former à la mémoire? Il n'y a pas moyen d'obtenir des mini-lots avec lampe de poche?

J'ai regardé un peu les fonctions de la torche offre, mais avec pas de chance:

#pdb.set_trace()
#valid_indices = torch.arange(0,N).numpy()
#valid_indices = np.array( range(N) )
#batch_indices = np.random.choice(valid_indices,size=M,replace=False)
#indices = torch.LongTensor(batch_indices)
#batch_xs, batch_ys = torch.index_select(X_mdl, 0, indices), torch.index_select(y, 0, indices)
#batch_xs,batch_ys = torch.index_select(X_mdl, 0, indices), torch.index_select(y, 0, indices)

même si le code que j'ai fourni fonctionne très bien, je suis inquiet que ce n'est pas une mise en œuvre efficace ET que si je devais utiliser les Gpu qu'il y aurait beaucoup plus de ralentir (parce que je suppose que c'mettre les choses en mémoire, puis de les chercher en arrière pour les mettre GPU comme ce qui est stupide).


J'ai mis en place un nouveau basé sur la réponse que m'a suggéré d'utiliser torch.index_select():

def get_batch2(X,Y,M):
    '''
    get batch for pytorch model
    '''
    # TODO fix and make it nicer, there is pytorch forum question
    #X,Y = X.data.numpy(), Y.data.numpy()
    X,Y = X, Y
    N = X.size()[0]
    batch_indices = torch.LongTensor( np.random.randint(0,N+1,size=M) )
    pdb.set_trace()
    batch_xs = torch.index_select(X,0,batch_indices)
    batch_ys = torch.index_select(Y,0,batch_indices)
    return Variable(batch_xs, requires_grad=False), Variable(batch_ys, requires_grad=False)

toutefois, cela semble avoir des problèmes car il ne fonctionne pas si X,Y ne sont PAS des variables...ce qui est vraiment bizarre. J'ai ajouté ceci à la pytorch forum: https://discuss.pytorch.org/t/how-to-get-mini-batches-in-pytorch-in-a-clean-and-efficient-way/10322

Droit maintenant ce que je fais de mal, c'est faire ce travail pour le gpu. Ma version actuelle:

def get_batch2(X,Y,M,dtype):
    '''
    get batch for pytorch model
    '''
    # TODO fix and make it nicer, there is pytorch forum question
    #X,Y = X.data.numpy(), Y.data.numpy()
    X,Y = X, Y
    N = X.size()[0]
    if dtype ==  torch.cuda.FloatTensor:
        batch_indices = torch.cuda.LongTensor( np.random.randint(0,N,size=M) )# without replacement
    else:
        batch_indices = torch.LongTensor( np.random.randint(0,N,size=M) ).type(dtype)  # without replacement
    pdb.set_trace()
    batch_xs = torch.index_select(X,0,batch_indices)
    batch_ys = torch.index_select(Y,0,batch_indices)
    return Variable(batch_xs, requires_grad=False), Variable(batch_ys, requires_grad=False)

le message d'erreur:

RuntimeError: tried to construct a tensor from a int sequence, but found an item of type numpy.int64 at index (0)

Je ne l'obtenez pas, dois-je vraiment faire:

ints = [ random.randint(0,N) for i i range(M)]

pour obtenir les nombres entiers?

Il serait également idéal si les données pourraient être une variable. Il semble que c' torch.index_select ne fonctionne pas pour Variable type de données.

cette liste d'entiers chose ne fonctionne toujours pas:

TypeError: torch.addmm received an invalid combination of arguments - got (int, torch.cuda.FloatTensor, int, torch.cuda.FloatTensor, torch.FloatTensor, out=torch.cuda.FloatTensor), but expected one of:
 * (torch.cuda.FloatTensor source, torch.cuda.FloatTensor mat1, torch.cuda.FloatTensor mat2, *, torch.cuda.FloatTensor out)
 * (torch.cuda.FloatTensor source, torch.cuda.sparse.FloatTensor mat1, torch.cuda.FloatTensor mat2, *, torch.cuda.FloatTensor out)
 * (float beta, torch.cuda.FloatTensor source, torch.cuda.FloatTensor mat1, torch.cuda.FloatTensor mat2, *, torch.cuda.FloatTensor out)
 * (torch.cuda.FloatTensor source, float alpha, torch.cuda.FloatTensor mat1, torch.cuda.FloatTensor mat2, *, torch.cuda.FloatTensor out)
 * (float beta, torch.cuda.FloatTensor source, torch.cuda.sparse.FloatTensor mat1, torch.cuda.FloatTensor mat2, *, torch.cuda.FloatTensor out)
 * (torch.cuda.FloatTensor source, float alpha, torch.cuda.sparse.FloatTensor mat1, torch.cuda.FloatTensor mat2, *, torch.cuda.FloatTensor out)
 * (float beta, torch.cuda.FloatTensor source, float alpha, torch.cuda.FloatTensor mat1, torch.cuda.FloatTensor mat2, *, torch.cuda.FloatTensor out)
      didn't match because some of the arguments have invalid types: (int, torch.cuda.FloatTensor, int, torch.cuda.FloatTensor, torch.FloatTensor, out=torch.cuda.FloatTensor)
 * (float beta, torch.cuda.FloatTensor source, float alpha, torch.cuda.sparse.FloatTensor mat1, torch.cuda.FloatTensor mat2, *, torch.cuda.FloatTensor out)
      didn't match because some of the arguments have invalid types: (int, torch.cuda.FloatTensor, int, torch.cuda.FloatTensor, torch.FloatTensor, out=torch.cuda.FloatTensor)

84voto

saetch_g Points 454

Si je suis la compréhension de votre code correctement, votre get_batch2 fonction semble être de prendre aléatoire mini-lots à partir de votre jeu de données sans suivi sur les indices que vous avez déjà utilisé à l'époque. Le problème avec cette implémentation est qu'elle ne sera probablement pas faire usage de toutes vos données.

La façon dont j'ai l'habitude de faire le traitement par lots est la création d'une permutation aléatoire de tous les sommets à l'aide de torch.randperm(N) et boucle à travers eux dans les lots. Par exemple:

n_epochs = 100 # or whatever
batch_size = 128 # or whatever

for epoch in range(n_epochs):

    # X is a torch Variable
    permutation = torch.randperm(X.size()[0])

    for i in range(0,X.size()[0], batch_size):
        optimizer.zero_grad()

        indices = permutation[i:i+batch_size]
        batch_x, batch_y = X[indices], Y[indices]

        # in case you wanted a semi-full example
        outputs = model.forward(batch_x)
        loss = lossfunction(outputs,batch_y)

        loss.backward()
        optimizer.step()

Si vous souhaitez copier et coller, assurez-vous de définir votre optimiseur, le modèle, et lossfunction quelque part avant le début de l'époque de la boucle.

En ce qui concerne votre erreur, essayez d'utiliser torch.from_numpy(np.random.randint(0,N,size=M)).long() au lieu de torch.LongTensor(np.random.randint(0,N,size=M)). Je ne suis pas sûr si cela va résoudre l'erreur que vous obtenez, mais il permettra de résoudre une erreur à l'avenir.

42voto

Mo Hossny Points 504

Utiliser les données des chargeurs.

Ensemble De Données

Vous définissez d'abord un ensemble de données. Vous pouvez utiliser des paquets de données en torchvision.datasets ou utiliser ImageFolder classe dataset qui suit la structure de Imagenet.

trainset=torchvision.datasets.ImageFolder(root='/path/to/your/data/trn', transform=generic_transform)
testset=torchvision.datasets.ImageFolder(root='/path/to/your/data/val', transform=generic_transform)

Transforme

Des transformations sont très utiles pour le traitement préalable des données chargées à la volée. Si vous utilisez des images, vous devez utiliser l' ToTensor() transformation pour convertir des images chargées à partir d' PIL de torch.tensor. Plus d'transforme peut être emballé dans un composit transformer comme suit.

generic_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.ToPILImage(),
    #transforms.CenterCrop(size=128),
    transforms.Lambda(lambda x: myimresize(x, (128, 128))),
    transforms.ToTensor(),
    transforms.Normalize((0., 0., 0.), (6, 6, 6))
])

Le Chargeur De Données

Ensuite, vous définissez un chargeur de données qui prépare le prochain lot pendant la formation. Vous pouvez définir le nombre de threads pour le chargement des données.

trainloader=torch.utils.data.DataLoader(trainset, batch_size=32, shuffle=True, num_workers=8)
testloader=torch.utils.data.DataLoader(testset, batch_size=32, shuffle=False, num_workers=8)

Pour la formation, vous venez d'énumérer sur le chargeur de données.

  for i, data in enumerate(trainloader, 0):
    inputs, labels = data    
    inputs, labels = Variable(inputs.cuda()), Variable(labels.cuda())
    # continue training...

NumPy Trucs

Oui. Vous devez les convertir en torch.tensor de numpy l'aide .numpy() méthode à travailler sur elle. Si vous êtes à l'aide de CUDA, vous devez télécharger les données à partir de GPU CPU à la première utilisation de l' .cpu() méthode avant d'appeler .numpy(). Personnellement, venant de MATLAB fond, je préfère le faire la plupart du travail avec torche tenseur, convertir les données numpy seulement pour la visualisation. Aussi garder à l'esprit que la torche stocke les données dans un canal-premier mode tout en numpy et PIL travailler avec canal-dernier. Cela signifie que vous devez utiliser np.rollaxis pour déplacer la chaîne de l'axe à la dernière. Un exemple de code est ci-dessous.

np.rollaxis(make_grid(mynet.ftrextractor(inputs).data, nrow=8, padding=1).cpu().numpy(), 0, 3)

La journalisation

La meilleure méthode que j'ai trouvée pour visualiser la fonctionnalité de cartes à l'aide du tenseur de conseil. Un code est disponible à yunjey/pytorch-tutoriel.

6voto

Jibin Mathew Points 1

Vous pouvez utiliser torch.utils.data

en supposant que vous avez chargé les données de l'annuaire, dans la formation et le test des tableaux numpy, vous pouvez hériter torch.utils.data.Dataset classe pour créer votre objet dataset

class MyDataset(Dataset):
    def __init__(self, x, y):
        super(MyDataset, self).__init__()
        assert x.shape[0] == y.shape[0] # assuming shape[0] = dataset size
        self.x = x
        self.y = y


    def __len__(self):
        return self.y.shape[0]

    def __getitem__(self, index):
        return self.x[index], self.y[index]

Ensuite, créez votre objet dataset

traindata = MyDataset(train_x, train_y)

Enfin, servez - DataLoader pour créer votre mini-lots

trainloader = torch.utils.data.DataLoader(traindata, batch_size=64, shuffle=True)

5voto

Forcetti Points 292

Pas sûr de ce que vous étiez en train de faire. W. r.t. le dosage, vous ne voudriez pas avoir à les convertir à numpy. Vous pouvez simplement utiliser index_select() , par exemple:

for epoch in range(500):
    k=0
    loss = 0
    while k < X_mdl.size(0):

        random_batch = [0]*5
        for i in range(k,k+M):
            random_batch[i] = np.random.choice(N-1)
        random_batch = torch.LongTensor(random_batch)
        batch_xs = X_mdl.index_select(0, random_batch)
        batch_ys = y.index_select(0, random_batch)

        # Forward pass: compute predicted y using operations on Variables
        y_pred = batch_xs.mul(W)
        # etc..

Le reste du code devrait être modifié ainsi que.


Je suppose que, vous souhaitez créer un get_batch fonction qui concatène votre X tenseurs et Y tenseurs. Quelque chose comme:

def make_batch(list_of_tensors):
    X, y = list_of_tensors[0]
    # may need to unsqueeze X and y to get right dimensions
    for i, (sample, label) in enumerate(list_of_tensors[1:]):
        X = torch.cat((X, sample), dim=0)
        y = torch.cat((y, label), dim=0)
    return X, y

Puis au cours de la formation que vous sélectionnez, par exemple, max_batch_size = 32, exemples à travers le découpage.

for epoch:
  X, y = make_batch(list_of_tensors)
  X = Variable(X, requires_grad=False)
  y = Variable(y, requires_grad=False)

  k = 0   
   while k < X.size(0):
     inputs = X[k:k+max_batch_size,:]
     labels = y[k:k+max_batch_size,:]
     # some computation
     k+= max_batch_size

1voto

gary69 Points 477

Créez une classe qui est une sous-classe de torch.utils.data.Dataset et passez-la à torch.utils.data.Dataloader . Voici un exemple pour mon projet.

 class CandidateDataset(Dataset):
    def __init__(self, x, y):
        self.len = x.shape[0]
        if torch.cuda.is_available():
            device = 'cuda'
        else:
            device = 'cpu'
        self.x_data = torch.as_tensor(x, device=device, dtype=torch.float)
        self.y_data = torch.as_tensor(y, device=device, dtype=torch.long)

    def __getitem__(self, index):
        return self.x_data[index], self.y_data[index]

    def __len__(self):
        return self.len

def fit(self, candidate_count):
        feature_matrix = np.empty(shape=(candidate_count, 600))
        target_matrix = np.empty(shape=(candidate_count, 1))
        fill_matrices(feature_matrix, target_matrix)
        candidate_ds = CandidateDataset(feature_matrix, target_matrix)
        train_loader = DataLoader(dataset = candidate_ds, batch_size = self.BATCH_SIZE, shuffle = True)
        for epoch in range(self.N_EPOCHS):
            print('starting epoch ' + str(epoch))
            for batch_idx, (inputs, labels) in enumerate(train_loader):
                print('starting batch ' + str(batch_idx) + ' epoch ' + str(epoch))
                inputs, labels = Variable(inputs), Variable(labels)
                self.optimizer.zero_grad()
                inputs = inputs.view(1, inputs.size()[0], 600)
                # init hidden with number of rows in input
                y_pred = self.model(inputs, self.model.initHidden(inputs.size()[1]))
                labels.squeeze_()
                # labels should be tensor with batch_size rows. Column the index of the class (0 or 1)
                loss = self.loss_f(y_pred, labels)
                loss.backward()
                self.optimizer.step()
                print('done batch ' + str(batch_idx) + ' epoch ' + str(epoch))
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X