web-dev-qa-db-ja.com

クリーンで効率的な方法でpytorchでミニバッチを取得する方法は?

私はトーチを使用して確率的勾配降下法(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メソッドは本当に馬鹿げているように見えますが、おそらくpytorchが初めてなのでしょうが、データバッチを取得する方法を議論する適切な場所を見つけられなかったためです。チュートリアル( http://pytorch.org/tutorials/beginner/pytorch_with_examples.html )とデータセット( http://pytorch.org/tutorials/beginner /data_loading_tutorial.html )運がありません。チュートリアルはすべて、最初に既にバッチとバッチサイズがあり、それを変更せずにそのデータでトレーニングすることを前提としているようです(具体的には http://pytorch.org/tutorials/beginner/ pytorch_with_examples.html#pytorch-variables-and-autograd )。

だから私の質問は、データをランダムにサンプルにフェッチして変数でpytorchに戻してメモリでトレーニングできるように、本当にデータをnumpyに戻す必要があるのですか?トーチでミニバッチを取得する方法はありませんか?

私はトーチが提供するいくつかの機能を見ましたが、運はありません:

#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を使用するとさらにかなり遅くなることが心配です(メモリに物を入れてから戻すためにフェッチするのでそれらの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_selectVariable型のデータでは機能しないようです。

この整数のリストはまだ機能しません:

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)
31
Charlie Parker

コードを正しく理解している場合、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))を使用してみてください。これでエラーが解決するかどうかはわかりませんが、将来のエラーは解決します。

26
saetch_g

データローダーを使用します。

データセット

最初に、データセットを定義します。 torchvision.datasetsでパッケージデータセットを使用するか、Imagenetの構造に従うImageFolderデータセットクラスを使用できます。

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のもの

はい。 .numpy()メソッドを使用して作業するには、torch.tensornumpyに変換する必要があります。 CUDAを使用している場合は、.cpu()を呼び出す前に.numpy()メソッドを使用してGPUからCPUにデータをダウンロードする必要があります。個人的に、MATLABのバックグラウンドから来て、私はほとんどの仕事をトーチテンソルで行い、視覚化のためだけにデータをnumpyに変換することを好みます。また、numpyとPILはchannel-lastで動作しますが、トーチはデータをチャネル優先モードで保存します。つまり、np.rollaxisを使用して、チャネル軸を最後に移動する必要があります。サンプルコードは次のとおりです。

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

ロギング

機能マップを視覚化するために見つけた最良の方法は、テンソルボードを使用することです。コードは yunjey/pytorch-tutorial で入手できます。

26
Mo Hossny

何をしようとしていたのかわかりません。 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..

ただし、残りのコードも同様に変更する必要があります。


私の推測では、XテンソルとYテンソルを連結するget_batch関数を作成したいと思います。何かのようなもの:

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
5
Forcetti

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))
0
gary69

torch.utils.dataを使用できます

numpy配列のtrainおよびtestでディレクトリからデータをロードしたと仮定すると、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)
0
Jibin Mathew