python - instalar - ¿Cómo obtener mini-lotes en pytorch de una manera limpia y eficiente?
pytorch tutorial (3)
Estaba tratando de hacer una cosa simple que era entrenar un modelo lineal con Estocástica Gradiente Descenso (SGD) utilizando antorcha:
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])
el código funciona bien y, a pesar de que mi método get_batch2
parece realmente get_batch2
/ ingenuo, es probable que sea porque soy nuevo en Pytorch pero no he encontrado un buen lugar donde discutan cómo recuperar lotes de datos. Revisé sus tutoriales ( http://pytorch.org/tutorials/beginner/pytorch_with_examples.html ) y el conjunto de datos ( http://pytorch.org/tutorials/beginner/data_loading_tutorial.html ) sin suerte. Todos los tutoriales parecen asumir que uno ya tiene el lote y el tamaño del lote al principio y luego procede a entrenar con esos datos sin cambiarlos (consulte específicamente http://pytorch.org/tutorials/beginner/pytorch_with_examples.html#pytorch-variables-and-autograd ).
Entonces, mi pregunta es ¿realmente necesito volver a convertir mis datos en números para poder obtener una muestra aleatoria de ellos y luego convertirlos en pytorch con Variable para poder entrenar en la memoria? ¿No hay manera de obtener mini-lotes con antorcha?
Miré algunas funciones que proporciona la antorcha pero sin suerte:
#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)
a pesar de que el código que proporcioné funciona bien, me preocupa que no sea una implementación eficiente Y que si utilizara las GPU habría una considerable desaceleración adicional (porque supongo que se guardan las cosas en la memoria y luego se las recupera para ponerlas) La GPU así es tonta).
Implementé una nueva basada en la respuesta que sugería usar 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)
sin embargo, esto parece tener problemas porque no funciona si X,Y
NO son variables ... lo que es realmente extraño. Agregué esto al foro de pytorch: https://discuss.pytorch.org/t/how-to-get-mini-batches-in-pytorch-in-a-clean-and-efficient-way/10322
Ahora mismo con lo que estoy luchando es hacer que esto funcione para gpu. Mi versión más actual:
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)
el error:
RuntimeError: tried to construct a tensor from a int sequence, but found an item of type numpy.int64 at index (0)
No lo entiendo, realmente tengo que hacer:
ints = [ random.randint(0,N) for i i range(M)]
para obtener los enteros?
También sería ideal si los datos pudieran ser una variable. Parece que torch.index_select
no funciona para datos de tipo Variable
.
Esta lista de enteros todavía no funciona:
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)
No estoy seguro de lo que estabas tratando de hacer. Por lotes escritos no tendrías que convertir a numpy. Puedes usar index_select() , por ejemplo:
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..
El resto del código también tendría que ser cambiado.
Supongo que le gustaría crear una función get_batch que concatene sus tensores X y tensores Y. Algo como:
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
Luego, durante el entrenamiento, selecciona, por ejemplo, max_batch_size = 32, ejemplos a través del corte.
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
Si entiendo su código correctamente, su función get_batch2
parece estar tomando mini lotes aleatorios de su conjunto de datos sin rastrear qué índices ya ha utilizado en una época. El problema con esta implementación es que probablemente no hará uso de todos sus datos.
La forma en que normalmente hago el procesamiento por lotes es creando una permutación aleatoria de todos los vértices posibles usando torch.randperm(N)
y torch.randperm(N)
en lotes. Por ejemplo:
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 te gusta copiar y pegar, asegúrate de definir tu optimizador, modelo y función de pérdida en algún lugar antes del inicio del ciclo de la época.
Con respecto a su error, intente usar torch.from_numpy(np.random.randint(0,N,size=M)).long()
lugar de torch.LongTensor(np.random.randint(0,N,size=M))
. No estoy seguro de si esto solucionará el error que está recibiendo, pero solucionará un error futuro.
Utilizar cargadores de datos.
Conjunto de datos
Primero se define un conjunto de datos. Puede usar conjuntos de datos de paquetes en torchvision.datasets
o usar la clase de conjunto de datos ImageFolder
que sigue la estructura 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)
Transforma
Las transformaciones son muy útiles para preprocesar los datos cargados sobre la marcha. Si está utilizando imágenes, tiene que usar la transformación ToTensor()
para convertir las imágenes cargadas de PIL
a torch.tensor
. Más transformaciones se pueden empaquetar en una transformación de composición de la siguiente manera.
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))
])
Cargador de datos
A continuación, define un cargador de datos que prepara el siguiente lote durante el entrenamiento. Puede establecer el número de subprocesos para la carga de datos.
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)
Para el entrenamiento, simplemente se enumera en el cargador de datos.
for i, data in enumerate(trainloader, 0):
inputs, labels = data
inputs, labels = Variable(inputs.cuda()), Variable(labels.cuda())
# continue training...
NumPy Stuff
Sí. Tienes que convertir torch.tensor
a numpy
usando el método .numpy()
para trabajar en él. Si está utilizando CUDA, primero debe descargar los datos de GPU a CPU utilizando el método .cpu()
antes de llamar a .numpy()
. Personalmente, proveniente de los antecedentes de MATLAB, prefiero hacer la mayor parte del trabajo con el tensor de la antorcha, luego convertir los datos a números solo para visualización. También tenga en cuenta que la antorcha almacena los datos en un modo de primer canal, mientras que el numpy y el PIL funcionan con el último canal. Esto significa que necesita usar np.rollaxis
para mover el eje del canal al último. A continuación se muestra un código de ejemplo.
np.rollaxis(make_grid(mynet.ftrextractor(inputs).data, nrow=8, padding=1).cpu().numpy(), 0, 3)
Explotación florestal
El mejor método que encontré para visualizar los mapas de características es usar un tablero tensor. Un código está disponible en yunjey/pytorch-tutorial .