उपर्युक्त उत्तरों ने इस प्रश्न को संबोधित किया कि क्यों बहुत अच्छी तरह से। मैं केवल बेहतर उपयोग को समझने के लिए एक उदाहरण जोड़ना चाहता हूं pack_padded_sequence
।
एक उदाहरण लेते हैं
नोट: pack_padded_sequence
बैच में क्रमबद्ध अनुक्रम की आवश्यकता होती है (अनुक्रम लंबाई के अवरोही क्रम में)। नीचे दिए गए उदाहरण में, अनुक्रम बैच को पहले ही कम अव्यवस्था के लिए हल किया गया था। यात्रा इस सार लिंक पूर्ण कार्यान्वयन के लिए।
सबसे पहले, हम नीचे के रूप में अलग-अलग अनुक्रम लंबाई के 2 अनुक्रमों का एक बैच बनाते हैं। हमारे पास पूरी तरह से बैच में 7 तत्व हैं।
- प्रत्येक अनुक्रम में 2 का एम्बेडिंग आकार है।
- पहले अनुक्रम की लंबाई है: 5
- दूसरे अनुक्रम की लंबाई है: 2
import torch
seq_batch = [torch.tensor([[1, 1],
[2, 2],
[3, 3],
[4, 4],
[5, 5]]),
torch.tensor([[10, 10],
[20, 20]])]
seq_lens = [5, 2]
हम seq_batch
5 के बराबर लंबाई (बैच में अधिकतम लंबाई) के साथ दृश्यों के बैच प्राप्त करने के लिए पैड करते हैं। अब, नए बैच में पूरी तरह से 10 तत्व हैं।
padded_seq_batch = torch.nn.utils.rnn.pad_sequence(seq_batch, batch_first=True)
"""
>>>padded_seq_batch
tensor([[[ 1, 1],
[ 2, 2],
[ 3, 3],
[ 4, 4],
[ 5, 5]],
[[10, 10],
[20, 20],
[ 0, 0],
[ 0, 0],
[ 0, 0]]])
"""
फिर, हम पैक करते हैं padded_seq_batch
। यह दो टेनर्स का टपल देता है:
- अनुक्रम बैच में सभी तत्वों सहित पहला डेटा है।
- दूसरा वह है
batch_sizes
जो बताएगा कि चरणों द्वारा एक दूसरे से संबंधित तत्व कैसे हैं।
packed_seq_batch = torch.nn.utils.rnn.pack_padded_sequence(padded_seq_batch, lengths=seq_lens, batch_first=True)
"""
>>> packed_seq_batch
PackedSequence(
data=tensor([[ 1, 1],
[10, 10],
[ 2, 2],
[20, 20],
[ 3, 3],
[ 4, 4],
[ 5, 5]]),
batch_sizes=tensor([2, 2, 1, 1, 1]))
"""
अब, हम packed_seq_batch
ट्यून को आरएनटीएन, एलएसटीएम जैसे पाइटोरेक में आवर्तक मॉड्यूल में पास करते हैं । इसके लिए केवल 5 + 2=7
पुनरावर्ती मॉड्यूल में संगणना की आवश्यकता होती है ।
lstm = nn.LSTM(input_size=2, hidden_size=3, batch_first=True)
output, (hn, cn) = lstm(packed_seq_batch.float())
"""
>>> output # PackedSequence
PackedSequence(data=tensor(
[[-3.6256e-02, 1.5403e-01, 1.6556e-02],
[-6.3486e-05, 4.0227e-03, 1.2513e-01],
[-5.3134e-02, 1.6058e-01, 2.0192e-01],
[-4.3123e-05, 2.3017e-05, 1.4112e-01],
[-5.9372e-02, 1.0934e-01, 4.1991e-01],
[-6.0768e-02, 7.0689e-02, 5.9374e-01],
[-6.0125e-02, 4.6476e-02, 7.1243e-01]], grad_fn=<CatBackward>), batch_sizes=tensor([2, 2, 1, 1, 1]))
>>>hn
tensor([[[-6.0125e-02, 4.6476e-02, 7.1243e-01],
[-4.3123e-05, 2.3017e-05, 1.4112e-01]]], grad_fn=<StackBackward>),
>>>cn
tensor([[[-1.8826e-01, 5.8109e-02, 1.2209e+00],
[-2.2475e-04, 2.3041e-05, 1.4254e-01]]], grad_fn=<StackBackward>)))
"""
हमें output
आउटपुट के गद्देदार बैच में बदलने की आवश्यकता है :
padded_output, output_lens = torch.nn.utils.rnn.pad_packed_sequence(output, batch_first=True, total_length=5)
"""
>>> padded_output
tensor([[[-3.6256e-02, 1.5403e-01, 1.6556e-02],
[-5.3134e-02, 1.6058e-01, 2.0192e-01],
[-5.9372e-02, 1.0934e-01, 4.1991e-01],
[-6.0768e-02, 7.0689e-02, 5.9374e-01],
[-6.0125e-02, 4.6476e-02, 7.1243e-01]],
[[-6.3486e-05, 4.0227e-03, 1.2513e-01],
[-4.3123e-05, 2.3017e-05, 1.4112e-01],
[ 0.0000e+00, 0.0000e+00, 0.0000e+00],
[ 0.0000e+00, 0.0000e+00, 0.0000e+00],
[ 0.0000e+00, 0.0000e+00, 0.0000e+00]]],
grad_fn=<TransposeBackward0>)
>>> output_lens
tensor([5, 2])
"""
इस प्रयास की तुलना मानक तरीके से करें
मानक तरीका में, हम केवल उत्तीर्ण करने की आवश्यकता padded_seq_batch
करने के लिए lstm
मॉड्यूल। हालाँकि, इसके लिए 10 संगणनाओं की आवश्यकता होती है। इसमें पैडिंग तत्वों पर कई गणना शामिल हैं जो कम्प्यूटेशनल रूप से अक्षम होंगे।
ध्यान दें कि यह गलत अभ्यावेदन का नेतृत्व नहीं करता है , लेकिन सही अभ्यावेदन निकालने के लिए और अधिक तर्क की आवश्यकता है।
- केवल आगे की दिशा के साथ LSTM (या किसी भी आवर्तक मॉड्यूल) के लिए, यदि हम किसी अनुक्रम के लिए प्रतिनिधित्व के रूप में अंतिम चरण के छिपे हुए वेक्टर को निकालना चाहते हैं, तो हमें T (th) चरण से छिपे हुए वैक्टर को चुनना होगा, जहां T इनपुट की लंबाई है। अंतिम प्रतिनिधित्व को उठाना गलत होगा। ध्यान दें कि बैच में अलग-अलग इनपुट के लिए T अलग होगा।
- द्वि-दिशात्मक LSTM (या किसी भी आवर्तक मॉड्यूल) के लिए, यह और भी अधिक बोझिल है, क्योंकि एक को दो आरएनएन मॉड्यूल बनाए रखना होगा, एक वह जो इनपुट की शुरुआत में पैडिंग के साथ और एक इनपुट के अंत में पैडिंग के साथ काम करता है, और अंत में ऊपर बताए अनुसार छिपे हुए वैक्टर को निकालना और समेटना।
आइए देखें अंतर:
output, (hn, cn) = lstm(padded_seq_batch.float())
"""
>>> output
tensor([[[-3.6256e-02, 1.5403e-01, 1.6556e-02],
[-5.3134e-02, 1.6058e-01, 2.0192e-01],
[-5.9372e-02, 1.0934e-01, 4.1991e-01],
[-6.0768e-02, 7.0689e-02, 5.9374e-01],
[-6.0125e-02, 4.6476e-02, 7.1243e-01]],
[[-6.3486e-05, 4.0227e-03, 1.2513e-01],
[-4.3123e-05, 2.3017e-05, 1.4112e-01],
[-4.1217e-02, 1.0726e-01, -1.2697e-01],
[-7.7770e-02, 1.5477e-01, -2.2911e-01],
[-9.9957e-02, 1.7440e-01, -2.7972e-01]]],
grad_fn= < TransposeBackward0 >)
>>> hn
tensor([[[-0.0601, 0.0465, 0.7124],
[-0.1000, 0.1744, -0.2797]]], grad_fn= < StackBackward >),
>>> cn
tensor([[[-0.1883, 0.0581, 1.2209],
[-0.2531, 0.3600, -0.4141]]], grad_fn= < StackBackward >))
"""
उपरोक्त परिणाम बताते हैं कि hn
, cn
दो तरीकों output
से अलग हैं जबकि दो तरीकों से पैडिंग तत्वों के लिए अलग-अलग मूल्य हैं।