PyTorchでの双方向LSTM出力の質問
こんにちは、BI-LSTMモジュールの出力から正しい結果を収集する方法について質問があります。
隠しユニットが100個ある単層LSTMモジュールに10長のシーケンスが供給されているとします。
lstm = nn.LSTM(5, 100, 1, bidirectional=True)
output
は次のような形になります:
[10 (seq_length), 1 (batch), 200 (num_directions * hidden_size)]
# or according to the doc, can be viewed as
[10 (seq_length), 1 (batch), 2 (num_directions), 100 (hidden_size)]
3番目(1インデックス)の入力の出力を両方向(2つの100 dimベクトル)で取得したい場合、どうすれば正しく実行できますか?
知っている output[2, 0]
は200 dimのベクトルを与えます。 この200 dimベクトルは両方向の3番目の入力の出力を表しますか?
気になるのは、逆送りを行うと、3番目(1インデックス)の出力ベクトルが8番目(1インデックス)の入力から計算されるということです。
Pytorchは自動的にこれを処理し、方向を考慮して出力をグループ化しますか?
ありがとう!
BiLSTMを使用する場合、方向の非表示状態は連結されます(中央の後の2番目の部分は、逆の順序でフィードするための非表示状態です)。
そのため、途中で分割しても問題ありません。
整形が右から左の次元に機能するため、2つの方向を分離する際に問題はありません。
ここに小さな例があります:
# so these are your original hidden states for each direction
# in this case hidden size is 5, but this works for any size
direction_one_out = torch.tensor(range(5))
direction_two_out = torch.tensor(list(reversed(range(5))))
print('Direction one:')
print(direction_one_out)
print('Direction two:')
print(direction_two_out)
# before outputting they will be concatinated
# I'm adding here batch dimension and sequence length, in this case seq length is 1
hidden = torch.cat((direction_one_out, direction_two_out), dim=0).view(1, 1, -1)
print('\nYour hidden output:')
print(hidden, hidden.shape)
# trivial case, reshaping for one hidden state
hidden_reshaped = hidden.view(1, 1, 2, -1)
print('\nReshaped:')
print(hidden_reshaped, hidden_reshaped.shape)
# This works as well for abitrary sequence lengths as you can see here
# I've set sequence length here to 5, but this will work for any other value as well
print('\nThis also works for more multiple hidden states in a tensor:')
multi_hidden = hidden.expand(5, 1, 10)
print(multi_hidden, multi_hidden.shape)
print('Directions can be split up just like this:')
multi_hidden = multi_hidden.view(5, 1, 2, 5)
print(multi_hidden, multi_hidden.shape)
出力:
Direction one:
tensor([0, 1, 2, 3, 4])
Direction two:
tensor([4, 3, 2, 1, 0])
Your hidden output:
tensor([[[0, 1, 2, 3, 4, 4, 3, 2, 1, 0]]]) torch.Size([1, 1, 10])
Reshaped:
tensor([[[[0, 1, 2, 3, 4],
[4, 3, 2, 1, 0]]]]) torch.Size([1, 1, 2, 5])
This also works for more multiple hidden states in a tensor:
tensor([[[0, 1, 2, 3, 4, 4, 3, 2, 1, 0]],
[[0, 1, 2, 3, 4, 4, 3, 2, 1, 0]],
[[0, 1, 2, 3, 4, 4, 3, 2, 1, 0]],
[[0, 1, 2, 3, 4, 4, 3, 2, 1, 0]],
[[0, 1, 2, 3, 4, 4, 3, 2, 1, 0]]]) torch.Size([5, 1, 10])
Directions can be split up just like this:
tensor([[[[0, 1, 2, 3, 4],
[4, 3, 2, 1, 0]]],
[[[0, 1, 2, 3, 4],
[4, 3, 2, 1, 0]]],
[[[0, 1, 2, 3, 4],
[4, 3, 2, 1, 0]]],
[[[0, 1, 2, 3, 4],
[4, 3, 2, 1, 0]]],
[[[0, 1, 2, 3, 4],
[4, 3, 2, 1, 0]]]]) torch.Size([5, 1, 2, 5])
これがお役に立てば幸いです! :)
私はoutput [2、0]が200-dimベクトルを与えることを知っています。この200 dimベクトルは、両方向の3番目の入力の出力を表しますか?
答えは[〜#〜] yes [〜#〜]です。
LSTMモジュール出力のoutput
テンソルは、入力シーケンスの対応する位置での前方LSTM出力と後方LSTM出力の連結です。そしてh_n
テンソルは、最後のタイムスタンプでの出力です。これは、前方LSTMのlsatトークンの出力ですが、後方LSTMの最初のトークンです。
In [1]: import torch
...: lstm = torch.nn.LSTM(input_size=5, hidden_size=3, bidirectional=True)
...: seq_len, batch, input_size, num_directions = 3, 1, 5, 2
...: in_data = torch.randint(10, (seq_len, batch, input_size))
...: output, (h_n, c_n) = lstm(in_data)
...:
In [2]: # output of shape (seq_len, batch, num_directions * hidden_size)
...:
...: print(output)
...:
tensor([[[ 0.0379, 0.0169, 0.2539, 0.2547, 0.0456, -0.1274]],
[[ 0.7753, 0.0862, -0.0001, 0.3897, 0.0688, -0.0002]],
[[ 0.7120, 0.2965, -0.3405, 0.0946, 0.0360, -0.0519]]],
grad_fn=<CatBackward>)
In [3]: # h_n of shape (num_layers * num_directions, batch, hidden_size)
...:
...: print(h_n)
...:
tensor([[[ 0.7120, 0.2965, -0.3405]],
[[ 0.2547, 0.0456, -0.1274]]], grad_fn=<ViewBackward>)
In [4]: output = output.view(seq_len, batch, num_directions, lstm.hidden_size)
...: print(output[-1, 0, 0]) # forward LSTM output of last token
...: print(output[0, 0, 1]) # backward LSTM output of first token
...:
tensor([ 0.7120, 0.2965, -0.3405], grad_fn=<SelectBackward>)
tensor([ 0.2547, 0.0456, -0.1274], grad_fn=<SelectBackward>)
In [5]: h_n = h_n.view(lstm.num_layers, num_directions, batch, lstm.hidden_size)
...: print(h_n[0, 0, 0]) # h_n of forward LSTM
...: print(h_n[0, 1, 0]) # h_n of backward LSTM
...:
tensor([ 0.7120, 0.2965, -0.3405], grad_fn=<SelectBackward>)
tensor([ 0.2547, 0.0456, -0.1274], grad_fn=<SelectBackward>)