Почему очень простая модель PyTorch LSTM не изучается? - PullRequest
3 голосов
/ 12 февраля 2020

Я пытаюсь сделать очень простое обучение, чтобы лучше понять, как работают PyTorch и LSTM. С этой целью я пытаюсь вывести отображение из входного тензора в выходной тензор (той же формы), который в два раза больше значения. Так что [1 2 3] в качестве входных данных должен выучить [2 4 6] в качестве выходных. Для этого у меня есть dataloader:

class AudioDataset(Dataset):
    def __init__(self, corrupted_path, train_set=False, test_set=False):
        torch.manual_seed(0)
        numpy.random.seed(0)

    def __len__(self):
        return len(self.file_paths)

    def __getitem__(self, index):
        random_tensor = torch.rand(1, 5) * 2
        random_tensor = random_tensor - 1

        return random_tensor, random_tensor * 2

Сам мой LSTM довольно прост:

class MyLSTM(nn.Module):
    def __init__(self, input_size=4000):
        super(MyLSTM, self).__init__()

        self.lstm = nn.LSTM(input_size=input_size, hidden_size=input_size,
                            num_layers=2)

    def forward(self, x):
        y = self.lstm(x)
        return y

Моя тренировка выглядит так:

    train_loader = torch.utils.data.DataLoader(
        train_set, batch_size=1, shuffle=True, **kwargs)

    model = MyLSTM(input_size=5)
    optimizer = optim.Adam(model.parameters(), lr=0.01, weight_decay=0.0001)
    loss_fn = torch.nn.MSELoss(reduction='sum')

    for epoch in range(300):
        for i, data in enumerate(train_loader):
            inputs = data[0]
            outputs = data[1]

            print('inputs', inputs, inputs.size())
            print('outputs', outputs, outputs.size())
            optimizer.zero_grad()

            pred = model(inputs)
            print('pred', pred[0], pred[0].size())

            loss = loss_fn(pred[0], outputs)

            model.zero_grad()

            loss.backward()
            optimizer.step()

После 300 эпох мой loss выглядит как tensor(1.4892, grad_fn=<MseLossBackward>). Который, кажется, не очень хорош.

Случайно смотрит на некоторые из входов / выходов и предсказаний:

inputs tensor([[[0.5050, 0.4669, 0.8310,  ..., 0.0659, 0.5043, 0.8885]]]) torch.Size([1, 1, 4000])
outputs tensor([[[1.0100, 0.9338, 1.6620,  ..., 0.1319, 1.0085, 1.7770]]]) torch.Size([1, 1, 4000])
pred tensor([[[ 0.6930,  0.0231, -0.6874,  ..., -0.5225,  0.1096,  0.5796]]],
       grad_fn=<StackBackward>) torch.Size([1, 1, 4000])

Мы видим, что он совсем не многому научился. Я не могу понять, что я делаю неправильно; если кто-то может направить меня, это будет с благодарностью.

1 Ответ

1 голос
/ 13 февраля 2020

LSTM сделаны из нейронов, которые генерируют внутреннее состояние на основе обратной связи l oop из предыдущих обучающих данных. Каждый нейрон имеет четыре внутренних элемента, которые принимают несколько входов и генерируют несколько выходов. Это один из более сложных нейронов для работы и понимания, и я не достаточно опытен, чтобы дать исчерпывающий ответ.

То, что я вижу в вашем примере кода, - это отсутствие понимания того, как они работать, и кажется, что вы предполагаете, что они работают как линейный слой. Я говорю это, потому что ваш метод forward не обрабатывает внутреннее состояние 1006 * и вы не изменяете выходные данные.

Вы определяете LSTM следующим образом:

     self.lstm = nn.LSTM(input_size=input_size, hidden_size=input_size, num_layers=2)

hidden_size относится к тому, как память и функции работают с воротами.

Документация PyTorch гласит следующее:

hidden_size - Количество функций в скрытом состоянии h

Это относится к размеру скрытого состояния, используемого для обучения внутренних ворот для долговременной и кратковременной памяти. Ворота являются функцией скрытых функций, которые хранят предыдущих выходов гейта. Каждый раз, когда нейрон обучается, скрытое состояние обновляется и снова используется для следующих обучающих данных.

Так почему это так важно?

Вы выбрасываете данные о скрытом состоянии во время тренировки, и я не знаю, что произойдет, если вы не определите скрытое состояние. Я предполагаю, что LSTM работает так, как будто истории никогда не существует.

Функция пересылки должна выглядеть примерно так:

    def forward(self, x, hidden):
        lstm_output, hidden = self.lstm(x, hidden)
        return lstm_output, hidden

Во время обучения вы должны самостоятельно отслеживать скрытое состояние.

for i in range(epochs):
   hidden = (torch.zeros(num_layers, batch_size, num_hidden),
             torch.zeros(num_layers, batch_size, num_hidden))

   for x, y in generate_batches(...):
        # missing code....
        lstm_output, hidden = model.forward(x, hidden)

Обратите внимание на форму для скрытого состояния. Это отличается от того, что вы обычно делаете с линейными слоями.

Существует несколько шагов, пропущенных выше, которые относятся к сбросу скрытого состояния, но я не могу вспомнить, как эта часть работает.

LSTM сами по себе описывают только функции во многом как слои свертки Маловероятно, что вы будете использовать выходные данные LSTM.

Большинство моделей, использующих LSTM или свертки, будут иметь нижнюю часть полностью подключенных слоев (например: nn.Linear()). Эти слои будут обучаться на функциях , чтобы предсказать интересующие вас результаты.

Проблема здесь в том, что выходы от LSTM имеют неправильную форму, и вы должны изменить форму тензоры, чтобы их можно было использовать в линейном слое.

Вот пример функции пересылки LSTM, которую я использовал:

    def forward(self, x, hidden):
        lstm_output, hidden = self.lstm(x, hidden)
        drop_output = self.dropout(lstm_output)
        drop_output = drop_output.contiguous().view(-1, self.num_hidden)
        final_out = self.fc_linear(drop_output)
        return final_out, hidden

LSTM, безусловно, являются продвинутой версией c в машинном обучении, и PyTorch - не простая библиотека для изучения с самого начала. Я бы порекомендовал прочитать LSTM с использованием документации TensorFlow и онлайн-блогов, чтобы лучше понять, как они работают.

...