Как получить мини-партии в pytorch в чистом и эффективном режиме?
Я пытался сделать простую вещь, которая тренировала линейную модель с Stochastic Gradient Descent (SGD) с использованием факела:
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])
код работает отлично, и все, хотя мой метод get_batch2
кажется действительно dum/naive, вероятно, потому, что я новичок в pytorch, но я не нашел хорошего места, где они обсуждают, как извлекать пакеты данных. Я просмотрел их учебники (http://pytorch.org/tutorials/beginner/pytorch_with_examples.html) и через набор данных (http://pytorch.org/tutorials/beginner/data_loading_tutorial.html) без везения. В учебниках все, кажется, предполагают, что вначале у него уже есть пакетный и пакетный размер, а затем он начинает тренироваться с этими данными, не меняя его (в частности, посмотрите < а2 > ).
Итак, мой вопрос: действительно ли мне нужно вернуть мои данные в numpy, чтобы я мог получить какую-то случайную выборку, а затем вернуть его обратно в pytorch с помощью Variable, чтобы иметь возможность тренироваться в памяти? Нет ли способа получить мини-партии с факелом?
Я посмотрел несколько функций, которые факел предоставляет, но не повезло:
#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)
несмотря на то, что код, который я предоставил, отлично работает, я обеспокоен тем, что его неэффективная реализация И что если бы я использовал графические процессоры, было бы значительно дальнейшее замедление (потому что я предполагаю, что он помещает вещи в память, а затем извлекает их назад, чтобы поместить их в GPU, как это глупо).
Я внедрил новый, основанный на ответе, который предложил использовать 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)
однако, похоже, что у него проблемы, потому что он не работает, если X,Y
не являются переменными... которые действительно нечетны. Я добавил это на форум pytorch: https://discuss.pytorch.org/t/how-to-get-mini-batches-in-pytorch-in-a-clean-and-efficient-way/10322
В настоящее время я борется за эту работу за gpu. Моя самая последняя версия:
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)
ошибка:
RuntimeError: tried to construct a tensor from a int sequence, but found an item of type numpy.int64 at index (0)
Я не понимаю, мне действительно нужно делать:
ints = [ random.randint(0,N) for i i range(M)]
чтобы получить целые числа?
Было бы также идеально, если бы данные могли быть переменной. Кажется, что torch.index_select
не работает для данных типа Variable
.
этот список целых чисел по-прежнему не работает:
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)
Ответы
Ответ 1
Используйте загрузчики данных.
Набор данных
Сначала вы определяете набор данных. Вы можете использовать наборы данных пакетов в torchvision.datasets
или использовать класс dataset ImageFolder
, который следует за структурой 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)
Трансформация
Преобразования очень полезны для предварительной обработки загруженных данных "на лету". Если вы используете изображения, вам необходимо использовать преобразование ToTensor()
для преобразования загруженных изображений с PIL
в torch.tensor
. Более преобразования могут быть упакованы в композитное преобразование следующим образом.
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))
])
загрузчик данных
Затем вы определяете загрузчик данных, который готовит следующую партию во время обучения. Вы можете установить количество потоков для загрузки данных.
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)
Для обучения вы просто перечисляете загрузчик данных.
for i, data in enumerate(trainloader, 0):
inputs, labels = data
inputs, labels = Variable(inputs.cuda()), Variable(labels.cuda())
# continue training...
NumPy Stuff
Да. Вам нужно преобразовать torch.tensor
в numpy
с помощью метода .numpy()
для работы над ним. Если вы используете CUDA, вам необходимо сначала загрузить данные с GPU на CPU с помощью метода .cpu()
перед вызовом .numpy()
. Лично, исходя из фона MATLAB, я предпочитаю выполнять большую часть работы с тензором факела, а затем преобразовывать данные в numpy только для визуализации. Также имейте в виду, что факел хранит данные в первом канале, а numpy и PIL работают с последним каналом. Это означает, что вам нужно использовать np.rollaxis
для перемещения оси канала до последнего. Ниже приведен пример кода.
np.rollaxis(make_grid(mynet.ftrextractor(inputs).data, nrow=8, padding=1).cpu().numpy(), 0, 3)
Вход
Лучший метод, который я нашел для визуализации карт функций, - использование тензорной доски. Код доступен в yunjey/pytorch-tutorial.
Ответ 2
Если я правильно понимаю ваш код, ваша функция get_batch2
, как представляется, принимает случайные мини-партии из вашего набора данных, не отслеживая, какие индексы вы использовали уже в эпоху. Проблема с этой реализацией заключается в том, что она, вероятно, не будет использовать все ваши данные.
Обычно я делаю пакетную обработку, создавая случайную перестановку всех возможных вершин с помощью torch.randperm(N)
и циклически перемещая их по партиям. Например:
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()
Если вы хотите скопировать и вставить, убедитесь, что вы определили свой оптимизатор, модель и функцию потери где-то до начала цикла эпохи.
Что касается вашей ошибки, попробуйте использовать torch.from_numpy(np.random.randint(0,N,size=M)).long()
вместо torch.LongTensor(np.random.randint(0,N,size=M))
. Я не уверен, что это решит ошибку, которую вы получаете, но она решит будущую ошибку.
Ответ 3
Не уверен, что вы пытались сделать. W.r.t. вам не придется конвертировать в numpy. Вы можете просто использовать index_select(), например:
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..
Остальная часть кода также должна быть изменена.
Мое предположение, вы хотели бы создать функцию get_batch, которая объединяет ваши X-тензоры и Y-тензоры. Что-то вроде:
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
Затем во время обучения вы выбираете, например. max_batch_size = 32, примеры через нарезку.
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
Ответ 4
Создайте класс, который является подклассом torch.utils.data.Dataset
и передайте его torch.utils.data.Dataloader
. Ниже приведен пример для моего проекта.
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))
Ответ 5
Вы можете использовать torch.utils.data
Предполагая, что вы загрузили данные из каталога, в torch.utils.data.Dataset
и тестовые массивы numpy, вы можете наследовать от класса torch.utils.data.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]
Затем создайте свой объект набора данных
traindata = MyDataset(train_x, train_y)
Наконец, используйте DataLoader
для создания мини-пакетов
trainloader = torch.utils.data.DataLoader(traindata, batch_size=64, shuffle=True)