คำตอบข้างต้นตอบคำถามว่าทำไมดีมาก ฉันต้องการเพิ่มตัวอย่างเพื่อให้เข้าใจการใช้งาน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
ไปยังโมดูลที่เกิดซ้ำใน Pytorch เช่น RNN, LSTM สิ่งนี้ต้องการ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 ครั้ง มันเกี่ยวข้องกับการคำนวณหลายอย่างเพิ่มเติมเกี่ยวกับองค์ประกอบ padding ซึ่งจะไม่มีประสิทธิภาพในการคำนวณ
โปรดทราบว่ามันไม่ได้นำไปสู่การแทนค่าที่ไม่ถูกต้องแต่ต้องใช้ตรรกะมากกว่านี้เพื่อดึงข้อมูลที่ถูกต้อง
- สำหรับ LSTM (หรือโมดูลที่เกิดซ้ำ) ที่มีทิศทางไปข้างหน้าเท่านั้นหากเราต้องการแยกเวกเตอร์ที่ซ่อนอยู่ของขั้นตอนสุดท้ายเพื่อเป็นตัวแทนของลำดับเราจะต้องเลือกเวกเตอร์ที่ซ่อนอยู่จากขั้นตอน T (th) โดยที่ T คือความยาวของอินพุต การหยิบการแสดงครั้งสุดท้ายจะไม่ถูกต้อง โปรดทราบว่า T จะแตกต่างกันสำหรับอินพุตที่แตกต่างกันในชุดงาน
- สำหรับ Bi-directional LSTM (หรือโมดูลที่เกิดขึ้นซ้ำ ๆ ) จะยิ่งยุ่งยากกว่าเดิมเนื่องจากต้องมีโมดูล RNN สองโมดูลซึ่งทำงานร่วมกับช่องว่างที่จุดเริ่มต้นของอินพุตและอีกโมดูลที่มีช่องว่างภายในตอนท้ายของอินพุต ในที่สุดก็แยกและเชื่อมเวกเตอร์ที่ซ่อนอยู่ตามที่อธิบายไว้ข้างต้น
มาดูความแตกต่างกัน:
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
จากสองวิธีที่นำไปสู่ค่าที่แตกต่างกันสำหรับองค์ประกอบ padding