1. pad_sequence
pad_sequence 函数通过填充的方式,将同一个 batch 中的 sequences 通过默认填充 0 的方式,变成最长 sequence 的长度,如下所示:

上图中,有 3 个句子,我们用数字代表某个词或者字,第一个序列长度为 5,第二个序列长度为 3,第三个序列长度为 4,通过 pad_sequence 函数,对其进行补 0 操作,如下所示:

函数使用代码如下:
import torch
import torch.nn as nn
from torch.nn.utils.rnn import pad_sequence
def test():
# 1.1 填充一维张量
a = torch.tensor([10, 20, 30, 40, 50])
b = torch.tensor([10, 20, 30])
c = torch.tensor([10, 20, 30, 40])
# 将三个张量放在列表中,传入到 pad_sequence 函数中
data = pad_sequence([a, b, c], batch_first=True)
print(data)
# 1.2 填充二维张量
a = torch.randn(2, 3)
b = torch.randn(4, 3)
c = torch.randn(3, 3)
data = pad_sequence([a, b, c], batch_first=True)
print(data)
if __name__ == '__main__':
test()
程序输出结果:
tensor([[10, 20, 30, 40, 50],
[10, 20, 30, 0, 0],
[10, 20, 30, 40, 0]])
tensor([[[ 1.1548, 0.0590, 1.2352],
[ 1.1051, 0.3413, -1.4559],
[ 0.0000, 0.0000, 0.0000],
[ 0.0000, 0.0000, 0.0000]],
[[-1.3324, -0.8635, 1.3135],
[ 0.5169, -0.8685, -2.8555],
[-0.0589, 0.0805, 1.1203],
[ 0.4198, 0.8386, -0.0064]],
[[-0.2444, -0.8604, -0.3829],
[-0.9048, -1.3597, -1.8759],
[ 0.4579, -0.7069, -0.5881],
[ 0.0000, 0.0000, 0.0000]]])
2. pack_sequence 函数
pack_sequence 将同 batch 中长度不同序列封装成 PackedSequence 对象的方式来处理的,需要注意的是,pack_sequence 函数要求将不同的 tensor 放在列表中,并且按照不同 tensor 的长度降序排列。
下面是示例代码:
import torch
import torch.nn as nn
from torch.nn.utils.rnn import pack_sequence
def test():
# 1.1 填充一维张量
a = torch.tensor([10, 20, 30, 40, 50])
b = torch.tensor([10, 20, 30])
c = torch.tensor([10, 20, 30, 40])
# 先把 [a, b, c] 按照长度从大到小
sort_data = sorted([a, b, c], key=lambda x: len(x), reverse=True)
# 构建压缩后的表示
data = pack_sequence(sort_data)
print(data)
# 1.2 填充二维张量
a = torch.randn(2, 3)
b = torch.randn(4, 3)
c = torch.randn(3, 3)
# 先把 [a, b, c] 按照长度从大到小
sort_data = sorted([a, b, c], key=lambda x: len(x), reverse=True)
# 构建压缩后的表示,输入形状 [L, *]
data = pack_sequence(sort_data)
print(data)
if __name__ == '__main__':
test()
程序输出结果:
PackedSequence(data=tensor([10, 10, 10, 20, 20, 20, 30, 30, 30, 40, 40, 50]), batch_sizes=tensor([3, 3, 3, 2, 1]), sorted_indices=None, unsorted_indices=None)
PackedSequence(data=tensor([[-0.4226, 0.1640, -1.4525],
[-0.6334, -0.4643, -0.2134],
[-1.3638, -0.5982, -0.3764],
[-0.2297, -1.4002, -0.0083],
[ 1.7213, 0.5896, -0.6520],
[-0.4708, 1.6265, 1.0932],
[ 1.9799, 0.2921, 0.7135],
[ 1.5097, -0.5596, -0.3654],
[-0.7631, 1.5645, -1.2764]]), batch_sizes=tensor([3, 3, 2, 1]), sorted_indices=None, unsorted_indices=None)
- PackedSequence 中的 data 表示压缩的数据
- batch_sizes 表示数据的个数,例如:[3, 3, 3, 2, 1] 中的 3, 表示 data 中 [10, 10, 10, 20, 20, 20, 30, 30, 30, 40, 40, 50] 的前 3 个数字是第一个时间步的输入,第二个 3 接着的 3 个数字作为第二个时间步的输入。
在 PyTorch 中的 RNN、LSTM、GRU 支持输入 PackedSequence 类型的数据作为输入。
3. pad 和 pack 在应用时的区别
import torch
import torch.nn as nn
from torch.nn.utils.rnn import pad_sequence
from torch.nn.utils.rnn import pack_sequence
from torch.nn.utils.rnn import pack_padded_sequence
# 固定随机数种子
torch.manual_seed(0)
# 1. 使用 pad_sequence
def test01():
inputs = [torch.tensor([10, 20, 30, 40, 50]),
torch.tensor([10, 20, 30]),
torch.tensor([10, 20, 30, 40])]
# 使用 pad_sequence 对 batch 数据长度处理
inputs = pad_sequence(inputs)
# print(inputs.shape)
# 经过词嵌入层
embed = nn.Embedding(num_embeddings=100, embedding_dim=2)
inputs = embed(inputs)
# print(inputs.shape)
gru = nn.GRU(input_size=2, hidden_size=4)
# GRP 输入形状: [sentence_length, batch_size, input_size]
output, h = gru(inputs)
print(h.data)
# 2. 使用 pack_padded_sequence
def test02():
inputs = [torch.tensor([10, 20, 30, 40, 50]),
torch.tensor([10, 20, 30]),
torch.tensor([10, 20, 30, 40])]
# 先对 inputs 根据长度排序
inputs.sort(key=lambda x: len(x), reverse=True)
# 先对输入序列进行0填充
inputs = pad_sequence(inputs)
# 经过词嵌入层,由于很多0经过,词嵌入之后也有很多0
embed = nn.Embedding(num_embeddings=100, embedding_dim=2, padding_idx=0)
inputs = embed(inputs)
# print(inputs.shape)
# print(inputs.transpose(0, 1))
# 接下来通过 pack_padded_sequence 对输入进行压缩
inputs = pack_padded_sequence(inputs, lengths=[5, 4, 3])
# print(inputs)
gru = nn.GRU(input_size=2, hidden_size=4)
# GRP 输入形状: [sentence_length, batch_size, input_size]
output, h = gru(inputs)
print(h.data)
if __name__ == '__main__':
test01()
print('-' * 50)
test02()
程序输出结果:
tensor([[[ 0.0466, -0.4807, -0.0078, -0.0605],
[-0.0605, -0.3388, 0.1844, -0.2294],
[ 0.0828, -0.3468, 0.0599, -0.2231]]])
--------------------------------------------------
tensor([[[-0.2108, 0.3736, 0.0056, -0.4251],
[-0.0305, 0.0509, -0.1880, -0.6150],
[ 0.4270, -0.0893, 0.0460, -0.5448]]])
上述函数中,test01 和 test02 函数中先使用 pad_sequence 函数进行 0 填充,变成相同长度的张量,然后经过 Embedding 转换为词嵌入。
在 test01 中直接将词嵌入结果送入 GRU 进行序列信息提取,过程如下图所示:

在 test02 中词嵌入的结果使用 pack_padded_sequence 进行压缩,然后送入 GRU 中进行序列信息提取,过程如下:

也就是说,通过 pack_padded_sequence 这种方式可以避免送入很多的 0 到循环神经网络中,进行某种层面上的无意义的计算。
3. pad_sequences 函数
pad_sequences 函数可以将文本截断、填充到指定长度.
from keras_preprocessing.sequence import pad_sequences sentence_content = pad_sequences(sentence_content, maxlen=100, padding='post')

冀公网安备13050302001966号