Comment obtenir des mini-lots en pytorch d'une manière propre et efficace?

j'essayais de faire une chose simple qui était d'entraîner un modèle linéaire avec une descente en pente Stochastique (SGD) en utilisant 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 bien et tout mon get_batch2 la méthode semble vraiment doum / naïve, c'est probablement parce que je suis nouveau à pytorch mais je n'ai pas trouvé un bon endroit où ils discutent de la façon de récupérer des lots de données. Je suis allé à travers leurs tutoriels (http://pytorch.org/tutorials/beginner/pytorch_with_examples.html) et par le biais de l'ensemble de données (http://pytorch.org/tutorials/beginner/data_loading_tutorial.html) sans succès. Les tutoriels semblent tous supposer que l'on a déjà le lot et la taille du lot au début et procède ensuite à former avec ces données sans les changer (spécifiquement regarder http://pytorch.org/tutorials/beginner/pytorch_with_examples.html#pytorch-variables-and-autograd).

alors ma question Est de savoir si j'ai vraiment besoin de retourner mes données en numpy pour que je puisse récupérer un échantillon aléatoire de celui-ci, puis le retourner à pytorch avec Variable pour pouvoir s'entraîner en mémoire? Il n'y a pas moyen d'obtenir des mini-lots avec torch?

j'ai regardé quelques fonctions que fournit torch mais sans succès:

#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 bien, je m'inquiète que sa mise en œuvre ne soit pas efficace et que si je devais utiliser des GPU, il y aurait un ralentissement considérable (parce que je pense qu'il met les choses en mémoire et puis les récupérer pour les mettre GPU comme c'est idiot).


j'en ai implémenté un nouveau basé sur la réponse qui suggérait 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 au forum de pytorch: https://discuss.pytorch.org/t/how-to-get-mini-batches-in-pytorch-in-a-clean-and-efficient-way/10322

dès maintenant ce qui me pose problème, c'est de faire en sorte que ça marche pour le gpu. Ma version la plus récente:

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)

l'erreur:

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

je ne comprends 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 qu'il torch.index_select ne fonctionne pas pour les Variable données de type.

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)
21
demandé sur Charlie Parker 2017-07-15 03:22:09

3 réponses

utiliser des chargeurs de données.

Jeu De Données

vous définissez d'Abord un ensemble de données. Vous pouvez utiliser les ensembles de données torchvision.datasets ou utiliser ImageFolder classe d'ensemble de données qui suit la structure D'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

les transformations sont très utiles pour le prétraitement des données chargées à la volée. Si vous utilisez des images, vous devez utiliser le ToTensor() transformation pour convertir des images chargées à partir de PILtorch.tensor. Plus de formes peuvent être emballées dans un composition 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))
])

Chargeur De Données

Puis 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...

Num PY Stuff

Oui. Vous devez convertir torch.tensornumpy en utilisant .numpy() méthode de travail. Si vous utilisez CUDA vous devez télécharger les données de GPU à CPU en utilisant d'abord le .cpu() méthode avant d'appeler .numpy(). Personnellement, venant de MATLAB background, je préfère faire la plupart du travail avec torch tensor, puis convertir les données en numpy seulement pour la visualisation. Gardez également à l'esprit que torch stocke les données en mode canal-premier alors que numpy et PIL travaillent avec canal-dernier. Cela signifie que vous devez utiliser np.rollaxis pour déplacer l'axe du canal vers la fin. Un exemple de code est ci-dessous.

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

Journalisation

Le meilleur la méthode que j'ai trouvé pour visualiser les cartes de fonctionnalité est en utilisant le panneau tensor. Un code est disponible à yunjey / pytorch-tutorial.

17
répondu Mo Hossny 2017-11-24 11:44:50

si je comprends votre code correctement, votre get_batch2 la fonction semble prendre des mini-lots aléatoires de votre ensemble de données sans suivre les indices que vous avez déjà utilisés dans une é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 je fais habituellement batching est de créer une permutation aléatoire de tous les sommets possibles en utilisant 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 le souhaitez pour copier et coller, assurez-vous de définir votre optimizer, model, et lossfunction quelque part avant le début de la boucle epoch.

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 que cela résoudra l'erreur que vous obtenez, mais il résoudra une erreur future.

13
répondu saetch_g 2017-11-27 05:43:26

Je ne suis pas sûr de ce que vous essayiez de faire. W. r. T. tu n'aurais pas à te convertir en pépites. Vous pouvez simplement utiliser index_select () , e.g.:

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.


a mon avis, vous aimeriez créer une fonction get_batch qui concaténate vos tenseurs X et Y. 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 pendant la formation vous sélectionnez, Par exemple max_batch_size = 32, exemples par le biais de la découper.

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
3
répondu Forcetti 2017-11-27 11:47:39