Является ли скрытый и вывод одинаковым для блока GRU в Pytorch? - PullRequest
1 голос
/ 20 июня 2019

Я концептуально понимаю, что должен делать LSTM или GRU (благодаря этому вопросу В чем разница между "скрытым" и "выводом" в PyTorch LSTM? ) НО, когда я проверяю вывод GRU h_n и output НЕ одинаковы, в то время как они должны быть ...

(Pdb) rnn_output
tensor([[[ 0.2663,  0.3429, -0.0415,  ...,  0.1275,  0.0719,  0.1011],
         [-0.1272,  0.3096, -0.0403,  ...,  0.0589, -0.0556, -0.3039],
         [ 0.1064,  0.2810, -0.1858,  ...,  0.3308,  0.1150, -0.3348],
         ...,
         [-0.0929,  0.2826, -0.0554,  ...,  0.0176, -0.1552, -0.0427],
         [-0.0849,  0.3395, -0.0477,  ...,  0.0172, -0.1429,  0.0153],
         [-0.0212,  0.1257, -0.2670,  ..., -0.0432,  0.2122, -0.1797]]],
       grad_fn=<StackBackward>)
(Pdb) hidden
tensor([[[ 0.1700,  0.2388, -0.4159,  ..., -0.1949,  0.0692, -0.0630],
         [ 0.1304,  0.0426, -0.2874,  ...,  0.0882,  0.1394, -0.1899],
         [-0.0071,  0.1512, -0.1558,  ..., -0.1578,  0.1990, -0.2468],
         ...,
         [ 0.0856,  0.0962, -0.0985,  ...,  0.0081,  0.0906, -0.1234],
         [ 0.1773,  0.2808, -0.0300,  ..., -0.0415, -0.0650, -0.0010],
         [ 0.2207,  0.3573, -0.2493,  ..., -0.2371,  0.1349, -0.2982]],

        [[ 0.2663,  0.3429, -0.0415,  ...,  0.1275,  0.0719,  0.1011],
         [-0.1272,  0.3096, -0.0403,  ...,  0.0589, -0.0556, -0.3039],
         [ 0.1064,  0.2810, -0.1858,  ...,  0.3308,  0.1150, -0.3348],
         ...,
         [-0.0929,  0.2826, -0.0554,  ...,  0.0176, -0.1552, -0.0427],
         [-0.0849,  0.3395, -0.0477,  ...,  0.0172, -0.1429,  0.0153],
         [-0.0212,  0.1257, -0.2670,  ..., -0.0432,  0.2122, -0.1797]]],
       grad_fn=<StackBackward>)

они некоторые транспонируют друг друга ... почему?

Ответы [ 2 ]

0 голосов
/ 20 июня 2019

Они не действительно одинаковы.Учтите, что у нас есть следующая однонаправленная модель GRU:

import torch.nn as nn
import torch

gru = nn.GRU(input_size = 8, hidden_size = 50, num_layers = 3, batch_first = True)

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

inp = torch.randn(1024, 112, 8)
out, hn = gru(inp)

Определенно,

torch.equal(out, hn)
False

Один из наиболее эффективных способов, который помог мне понять выходные данные по сравнению со скрытыми состояниями, заключался в том, чтобы рассматривать hn как hn.view(num_layers, num_directions, batch, hidden_size) , где num_directions = 2 для двунаправленных рекуррентных сетей (и еще 1,т.е. наш случай) .Таким образом,

hn_conceptual_view = hn.view(3, 1, 1024, 50)

Как указано в документе (обратите внимание на курсив и жирный шрифт) :

h_n формы (num_layers * num_directions, batch, hidden_size): тензор, содержащий скрытое состояние для t = seq_len (то есть для последнего временного шага)

В нашем случае это содержит скрытый вектор для временного шага t = 112, гдевывод:

формы (seq_len, batch, num_directions * hidden_size): тензор, содержащий выходные элементы h_t из последнего слоя GRU, для каждого t . Если в качестве входных данных указан torch.nn.utils.rnn.PackedSequence, выходные данные также будут упакованными.Для распакованного случая направления могут быть разделены с помощью output.view (seq_len, batch, num_directions, hidden_size), причем forward и backward - направления 0 и 1. соответственно.

Таким образом, следовательно, можноdo:

torch.equal(out[:, -1], hn_conceptual_view[-1, 0, :, :])
True

Объяснение : я сравниваю последнюю последовательность из всех партий в out[:, -1] со скрытыми векторами последнего слоя из hn[-1, 0, :, :]


Для Двунаправленный GRU (сначала требуется чтение однонаправленного):

gru = nn.GRU(input_size = 8, hidden_size = 50, num_layers = 3, batch_first = True bidirectional = True)
inp = torch.randn(1024, 112, 8)
out, hn = gru(inp)

Вид изменяется на (поскольку у нас есть два направления):

hn_conceptual_view = hn.view(3, 2, 1024, 50)

Если вы попытаетесьточный код:

torch.equal(out[:, -1], hn_conceptual_view[-1, 0, :, :])
False

Объяснение : Это потому, что мы даже сравниваем неправильные формы;

out[:, 0].shape
torch.Size([1024, 100])
hn_conceptual_view[-1, 0, :, :].shape
torch.Size([1024, 50])

Помните, что для двунаправленных сетей скрытые состояния объединяютсяна каждом временном шаге, где первый размер hidden_state (т. е. out[:, 0,:50]) - это скрытые состояния для прямой сети, а другой размер hidden_state - дляв обратном направлении (т. е. out[:, 0,50:]).Правильное сравнение для прямой сети будет таким:

torch.equal(out[:, -1, :50], hn_conceptual_view[-1, 0, :, :])
True

Если вы хотите скрытые состояния для обратной сети и с обратной сетиобрабатывает последовательность с временным шагом n ... 1.Вы сравниваете первый временной шаг последовательности , но последний hidden_state размер и меняете направление hn_conceptual_view на 1:

torch.equal(out[:, -1, :50], hn_conceptual_view[-1, 1, :, :])
True

В двух словах, как правило,Говоря:

Однонаправленный :

rnn_module = nn.RECURRENT_MODULE(num_layers = X, hidden_state = H, batch_first = True)
inp = torch.rand(B, S, E)
output, hn = rnn_module(inp)
hn_conceptual_view = hn.view(X, 1, B, H)

Где RECURRENT_MODULE - это ГРУ или LSTM (на момент написания этого поста), B - партияразмер, S длина последовательности и E размер вложения.

torch.equal(output[:, S, :], hn_conceptual_view[-1, 0, :, :])
True

Снова мы использовали S, поскольку rnn_module является прямым (то есть однонаправленным), а последний временной шаг сохраняется надлина последовательности S.

Двунаправленный :

rnn_module = nn.RECURRENT_MODULE(num_layers = X, hidden_state = H, batch_first = True, bidirectional = True)
inp = torch.rand(B, S, E)
output, hn = rnn_module(inp)
hn_conceptual_view = hn.view(X, 2, B, H)

Сравнение

torch.equal(output[:, S, :H], hn_conceptual_view[-1, 0, :, :])
True

Выше приведено прямое сравнение сети, мы использовали :H потому что форвард хранит свой скрытый вектор в первых H элементах для каждого временного шага.

Для обратной сети:

torch.equal(output[:, 0, H:], hn_conceptual_view[-1, 1, :, :])
True

Мы изменили направление в hn_conceptual_view на 1 чтобы получить скрытые векторы для обратной сети.


Для всех примеров мы использовали hn_conceptual_view[-1, ...], потому что мыинтересует только последний слой.

0 голосов
/ 20 июня 2019

Это не транспонирование, вы можете получить rnn_output = hidden [-1], когда уровень lstm равен 1

hidden - это выход каждой ячейки каждого слоя, он должен быть двумерным массивом для конкретного объекта.шаг ввода времени, но lstm возвращает весь шаг времени, поэтому выход слоя должен быть скрыт [-1]

, и эту ситуацию обсуждают, когда пакет равен 1, или необходимо добавить размер вывода и скрытогоодин

...