本篇博客参考:
pytorch之数据:pack_padded_sequence()与pad_packed_sequence()
Pytorch学习笔记05---- pack_padded_sequence和pad_packed_sequence理解
首先,packed是包装好的的意思;padded是填充的意思;pack有包装、压紧的意思;pad有填充的意思。
pack_padded_sequence
即压紧封装填充好的句子
pad_packed_sequence
即填充被压紧封装好的句子
示意图:
pack_padded_sequence
pad_packed_sequence就是上边的那个过程反过来
pack_padded_sequence
#官方函数
torch.nn.utils.rnn.pack_padded_sequence(input, lengths, batch_first=False)
#↓'返回'
return PackedSequence #一个PackedSequence对象
功能:将一个填充过的变长序列压紧。返回PackedSequence对象。
参数
- input:要压缩的数据。当batch_first是False时候,shape的输入格式是[B,S * ],其中B是batch_size,S是seq_len(该batch中最长序列的长度)* 可以是任何维度。如果batch_first是True时候,相应的的数据格式必须是[S,B,* ]。input必须按序列长度的长短排序,长的在前面,短的在后面,第一个时间步的数据必须是最长的数据。
- lengths:输入数据的每个序列的长度。
- batch_first:当为True,数据格式必须[B, S, * ],反之,默认是False。
pad_packed_sequence
torch.nn.utils.rnn.pad_packed_sequence(sequence, batch_first=False) → tuple
#↓'返回'
return (sequence_pad , list) # 这个元组包含被填充后的序列 , 和batch中序列的长度列表。
功能:上面提到的函数的功能是将一个填充后的变长序列压紧。这个操作和pack_padded_sequence()是相反的。把压紧的序列再填充回来,默认按返回中list最大的数字填充。
参数
- sequence:将要被填充的batch,是一个PackedSequence对象。
- batch_first:作用同上pack_padded_sequence的batch_size,但是影响的是输出的数据格式。
例子
import torch
batch_size = 3 # 这个batch有3个序列
max_len = 6 # 最长序列的长度是6
embedding_size = 8 # 嵌入向量大小8
hidden_size = 16 # 隐藏向量大小16
vocab_size = 20 # 词汇表大小20
input_seq = [[3, 5, 12, 7, 2, ], [4, 11, 14, ], [18, 7, 3, 8, 5, 4]]
lengths = [5, 3, 6] # batch中每个seq的有效长度。
# embedding
embedding = torch.nn.Embedding(vocab_size, embedding_size, padding_idx=0)
# LSTM的RNN循环神经网络
lstm = torch.nn.LSTM(embedding_size, hidden_size)
因为pack_padded_sequence中要求input必须按序列长度的长短排序,长的在前面,短的在后面,第一个时间步的数据必须是最长的数据。
所以先对数据进行排序。
zip放在补充
#由大到小排序
input_seq = sorted(input_seq, key = lambda tp: len(tp), reverse=True)
lengths = sorted(lengths, key = lambda tp: tp, reverse=True)
print(input_seq)
print(lengths)
PAD_token = 0 # 填充下标是0
def pad_seq(seq, seq_len, max_length):
seq = seq
seq += [PAD_token for _ in range(max_length - seq_len)]
return seq
pad_seqs = [] # 填充后的数据
for i,j in zip(input_seq, lengths):
pad_seqs.append(pad_seq(i, j, max_len))
print(pad_seqs)
[[18, 7, 3, 8, 5, 4], [3, 5, 12, 7, 2], [4, 11, 14]]
[6, 5, 3]
[[18, 7, 3, 8, 5, 4], [3, 5, 12, 7, 2, 0], [4, 11, 14, 0, 0, 0]]
pad过后的句子是如下格式
[[18, 7, 3, 8, 5, 4],
[3, 5, 12, 7, 2, 0],
[4, 11, 14, 0, 0, 0]]
即[batch_size,seq_len,* ],所以pack_padded_sequence中的batch_first=True
LSTM的输出维度:
其中如果LSTM是双向则num_directions=2,否则num_directions=1
参数 | 维度1 | 维度2 | 维度3 |
---|---|---|---|
output | seq_len | batch | num_directions×hidden_size |
pad_seqs = torch.tensor(pad_seqs)
embeded = embedding(pad_seqs)
# 压缩,设置batch_first为true
pack = torch.nn.utils.rnn.pack_padded_sequence(embeded, lengths, batch_first=True)
#这里如果不写batch_first,你的数据必须是[s,b,e],不然会报错lenghth错误
# 利用lstm循环神经网络测试结果
state = None
pade_outputs, _ = lstm(pack, state)
# 设置batch_first为true;你可以不设置为true,为false时候只影响结构不影响结果
pade_outputs, others = torch.nn.utils.rnn.pad_packed_sequence(pade_outputs, batch_first=True)
# 查看输出的元祖
print(pade_outputs.shape)
print(others)
torch.Size([3, 6, 16])
tensor([6, 5, 3])
补充
import numpy as np
a=[1,2,3,4,5]
b=(1,2,3,4,5)
c=np.arange(5)
d="zhang"
zz=zip(a,b,c,d)
print(list(zz))
[(1, 1, 0, 'z'), (2, 2, 1, 'h'), (3, 3, 2, 'a'), (4, 4, 3, 'n'), (5, 5, 4, 'g')]