Построение РНС с нуля в pytorch - PullRequest
0 голосов
/ 16 июня 2020

Я пытаюсь создать RNN с нуля с помощью pytorch, и я следую этому руководству , чтобы создать его.

import torch
import torch.nn as nn
import torch.nn.functional as F

class BasicRNN(nn.Module):
    def __init__(self, n_inputs, n_neurons):
        super(BasicRNN, self).__init__()

        self.Wx = torch.randn(n_inputs, n_neurons) # n_inputs X n_neurons
        self.Wy = torch.randn(n_neurons, n_neurons) # n_neurons X n_neurons

        self.b = torch.zeros(1, n_neurons) # 1 X n_neurons

    def forward(self, X0, X1):
        self.Y0 = torch.tanh(torch.mm(X0, self.Wx) + self.b) # batch_size X n_neurons

        self.Y1 = torch.tanh(torch.mm(self.Y0, self.Wy) +
                            torch.mm(X1, self.Wx) + self.b) # batch_size X n_neurons

        return self.Y0, self.Y1


class CleanBasicRNN(nn.Module):

    def __init__(self, batch_size, n_inputs, n_neurons):
        super(CleanBasicRNN, self).__init__()

        self.rnn = BasicRNN(n_inputs, n_neurons)
        self.hx = torch.randn(batch_size, n_neurons) # initialize hidden state

    def forward(self, X):
        output = []

        # for each time step
        for i in range(2):
            self.hx = self.rnn(X[i], self.hx)
            output.append(self.hx)

        return output, self.hx


FIXED_BATCH_SIZE = 4 # our batch size is fixed for now
N_INPUT = 3
N_NEURONS = 5

X_batch = torch.tensor([[[0,1,2], [3,4,5], 
                         [6,7,8], [9,0,1]],
                        [[9,8,7], [0,0,0], 
                         [6,5,4], [3,2,1]]
                       ], dtype = torch.float) # X0 and X1

model = CleanBasicRNN(FIXED_BATCH_SIZE,N_INPUT,N_NEURONS)

a1,a2 = model(X_batch)

Выполнение этого кода возвращает эту ошибку

RuntimeError: несоответствие размера, m1: [4 x 5], m2: [3 x 5] в /pytorch/..

После некоторого рытья я обнаружил, что эта ошибка возникает при передаче скрытого состояний к модели BasicRNN

N_INPUT = 3 # number of features in input
N_NEURONS = 5 # number of units in layer
X0_batch = torch.tensor([[0,1,2], [3,4,5], 
                         [6,7,8], [9,0,1]],
                        dtype = torch.float) #t=0 => 4 X 3

X1_batch = torch.tensor([[9,8,7], [0,0,0], 
                         [6,5,4], [3,2,1]],
                        dtype = torch.float) #t=1 => 4 X 3

test_model = BasicRNN(N_INPUT,N_NEURONS)
a1,a2 = test_model(X0_batch,X1_batch)
a1,a2 = test_model(X0_batch,torch.randn(1,N_NEURONS)) # THIS LINE GIVES ERROR

Что происходит в скрытых состояниях и как я могу решить эту проблему?

Ответы [ 2 ]

1 голос
/ 16 июня 2020

BasicRNN - это не реализация соты RNN, а скорее полная RNN, фиксированная для двух временных шагов. Он изображен на изображении учебника:

BasicRNN

Где Y0, первый временной шаг, не включает предыдущее скрытое состояние (технически ноль) и Y0 также h0, который затем используется для второго временного шага, Y1 или h1.

Ячейка RNN является одним из отдельных временных шагов, особенно второй, так как он должен включать скрытое состояние предыдущего временного шага.

Следующее скрытое состояние вычисляется, как описано в nn.RNNCell документации :

RNNCell formula

В вашем BasicRNN есть только один член смещения, но у вас все еще есть вес Wx для ввода и вес Wy для скрытого состояния, что вместо этого, вероятно, следует называть Wh. Что касается прямого метода, его аргументы становятся входом и предыдущим скрытым состоянием, а не двумя входами с разными временными шагами. Это также означает, что у вас есть только одно вычисление, соответствующее формуле nn.RNNCell, которое было вычислением для Y1, за исключением того, что оно использует скрытое состояние, которое было передано прямому методу.

class BasicRNN(nn.Module):
    def __init__(self, n_inputs, n_neurons):
        super(BasicRNN, self).__init__()

        self.Wx = torch.randn(n_inputs, n_neurons) # n_inputs X n_neurons
        self.Wh = torch.randn(n_neurons, n_neurons) # n_neurons X n_neurons

        self.b = torch.zeros(1, n_neurons) # 1 X n_neurons

    def forward(self, x, hidden):
        return torch.tanh(torch.mm(x, self.Wx) + torch.mm(hidden, self.Wh) + self.b)

В учебном курсе они решили использовать nn.RNNCell напрямую вместо реализации ячейки.

Примечание: члены умножения матриц указаны в другом порядке, потому что веса обычно переносятся при сравнении к вашим весам, и формула предполагает, что входное и скрытое состояние являются векторами (а не партиями). Технически пакетные входные данные и скрытые состояния должны быть транспонированы, а выходные данные должны быть перенесены обратно, чтобы он мог работать с пакетами. Проще просто использовать транспонированный вес, так как результат такой же из-за свойства транспонирования матричного умножения:

Matrix Multiplication Transpose Property

1 голос
/ 16 июня 2020

Возможно, учебник ошибочен: torch.mm(X1, self.Wx) умножает 3 x 5 и 4 x 5 тензор, что не работает. Даже если вы заставите его работать, переписав его как torch.mm(self.Wx, X1.t()), вы ожидаете, что он выведет тензор 4 x 5, но результатом будет тензор 4 x 3.

...