LSTMで長さの異なるデータを扱う
長さの異なるデータの扱い方
- Padding + Masking(Transformerでよく使われる)
- Packing sequences(RNN, LSTM, GRU向け)
- トリミング or サンプリング(長さを揃える)
この記事では2を扱う。
Packing sequencesとは?(RNN, LSTM, GRU向け)
PyTorchの以下のメソッドを使い、多次元テンソルを、PackedSequenceインスタンスへ変換する。
pack_sequence テンソルをPacekdSequenceに変換する。
pack_padded_sequence paddingされたテンソルをPackedSequenceに変換する。後述するpack_sequenceでpaddingされたテンソルを作ることが出来る。
LSTM(RNN, GRU)はPackedSequenceインスタンスを引数として取ることができる。
pack_sequence テンソルにpaddingを加える。
pad_packed_sequence PackedSequenceインスタンスをpaddingされたテンソルへ変換する。
具体例
import torch
from torch.nn.utils.rnn import pad_sequence, pack_padded_sequence, pad_packed_sequence
from torch import nn
# ----- データ作成 -----
sequences = [
torch.tensor([1, 2, 3]),
torch.tensor([4, 5]),
torch.tensor([6])
]
lengths = torch.tensor([3, 2, 1]) # 元の長さを指定
# パディング
padded = pad_sequence(sequences, batch_first=True, padding_value=0)
print("padded:\n", padded.shape, padded)
# ----- パッキング -----
packed = pack_padded_sequence(padded, lengths, batch_first=True, enforce_sorted=False)
print("packed: ", packed.data.shape, packed)
# ----- パディングに戻す -----
output, output_lengths = pad_packed_sequence(packed, batch_first=True)
print("origin:\n", output, output.shape, output_lengths)
padded:
torch.Size([3, 3]) tensor([[1, 2, 3],
[4, 5, 0],
[6, 0, 0]])
packed: torch.Size([6]) PackedSequence(data=tensor([1, 4, 6, 2, 5, 3]), batch_sizes=tensor([3, 2, 1]), sorted_indices=tensor([0, 1, 2]), unsorted_indices=tensor([0, 1, 2]))
origin:
tensor([[1, 2, 3],
[4, 5, 0],
[6, 0, 0]]) torch.Size([3, 3]) tensor([3, 2, 1])
なぜpackされるとshapeが変わる?
通常のTensor:
(バッチサイズ, シーケンス長, 埋め込み次元)
→ ちゃんと3次元。
packed:
PackedSequence って特別な形式になる!
shape は見た目が違う
埋め込み次元はあるけど、バッチサイズやシーケンス長はなくなる。
packする = パディングされたデータを取り除くため、
- イメージ図:
1 ✅ ✅ ✅
2 ✅ ✅ ❌
3 ✅ ❌ ❌
✅ のところだけ抜き出して縦に並べる!
3 + 2 + 1 = 6
shapeが(6, 1)になる。
Discussion