如何在pytorch中填充超过1维的可变长度序列?



是否有任何干净的方法在pytorch中创建一批3D序列?我有3D序列的形状(sequence_length_lvl1, sequence_length_lvl2, D),序列有不同的值为sequence_length_lvl1和sequence_length_lvl2,但他们都有相同的值为D,我想垫这些序列在第一个和第二个维度,并创建一批它们,但我不能使用pytorch pad_sequence函数,因为它只工作,如果序列只有一个维度的可变长度。我想问一下有没有人知道什么干净利落的方法?

为了更清楚,我提供了一个示例。假设输入序列是这样的:

input1 = [
[[1, 1, 1], [2, 2, 2], [3, 3, 3]], 
[[4, 4, 4], [5, 5, 5]]
]
input2 = [
[[1, 1, 1], [2, 2, 2], [3, 3, 3]], 
[[6, 6, 6]],
[[4, 4, 4], [5, 5, 5]]
]

我想填充[input1, input2]。期望的输出将是:

output = [
[[[1, 1, 1], [2, 2, 2], [3, 3, 3]], 
[[4, 4, 4], [5, 5, 5], [0, 0, 0]],
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]],
[[[1, 1, 1], [2, 2, 2], [3, 3, 3]], 
[[6, 6, 6], [0, 0, 0], [0, 0, 0]],
[[4, 4, 4], [5, 5, 5], [0, 0, 0]]]
]

因此期望输出的形状为(2,3,3,3)。

这适用于您的示例,也许有更快的方法。

input1 = [
[[1, 1, 1], [2, 2, 2], [3, 3, 3]],
[[4, 4, 4], [5, 5, 5]]
]
input2 = [
[[1, 1, 1], [2, 2, 2], [3, 3, 3]],
[[6, 6, 6]],
[[4, 4, 4], [5, 5, 5]]
]
len_max = max(len(input1), len(input2))
output_val = [[], []]
no_val = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
for i in range(len_max):
try:
a = []
a = input1[i]
except Exception:
a = no_val
add_empty = 3 - len(a)
for j in range(add_empty):
a += [[0, 0, 0]]
try:
b = []
b = input2[i]
except Exception:
b = no_val
add_empty = 3 - len(b)
for j in range(add_empty):
b += [[0, 0, 0]]
output_val[0] += [a]
output_val[1] += [b]
print('-------------n', output_val)

您可以使用text2array库来执行这种填充,无论序列嵌套有多深(免责声明:我是作者)。使用pip install text2array安装,然后:

from text2array import Batch
arr = Batch([{'x': input1}, {'x': input2}]).to_array()
print(arr['x'])

将打印

array([[[[1, 1, 1],
[2, 2, 2],
[3, 3, 3]],
[[4, 4, 4],
[5, 5, 5],
[0, 0, 0]],
[[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]],

[[[1, 1, 1],
[2, 2, 2],
[3, 3, 3]],
[[6, 6, 6],
[0, 0, 0],
[0, 0, 0]],
[[4, 4, 4],
[5, 5, 5],
[0, 0, 0]]]])

。输出是一个NumPy数组,但您可以使用torch.from_numpy轻松地将其转换为PyTorch张量。

我不确定pytorch数据结构,但如果它们是list类数据,您可以使用我的解决方案。

这个函数是用0填充每个维度(即宽度,高度和深度)的缺失值,将维度调整为与最大尺寸相同。这可以应用于任意数量的输入,而不仅仅是2。首先,找到所有输入的最大宽度,最大高度和最大深度(例如,input1input2)。之后,用0填充每个输入的缺失单元格,然后将它们连接在一起。

这个方法不需要任何额外的库。

def fill_missing_dimension(inputs):
output = []
# find max width, height, depth among all inputs
max_width = max([len(i) for i in inputs])
max_height = max([len(j) for i in inputs for j in i])
max_depth = max([len(k) for i in inputs for j in i for k in j])
print(max_width, max_height, max_depth)
# fill missing dimension with 0 for all inputs
for input in inputs:
for i in range(len(input)):
for j in range(len(input[i])):
for k in range(len(input[i][j]), max_depth):
input[i][j].append(0)
for j in range(len(input[i]), max_height):
input[i].append([0] * max_depth)
for i in range(len(input), max_width):
input.append([[0] * max_depth] * max_height)
# concate all inputs
output.append(input)
return output

如果你认为上面的代码太长,下面是上面函数的更短、更清晰(列表理解)的版本(但很难阅读和理解):

# comprehension version of fill_missing_dimension
def fill_missing_dimension(inputs):
max_width = max([len(i) for i in inputs])
max_height = max([len(j) for i in inputs for j in i])
max_depth = max([len(k) for i in inputs for j in i for k in j])
return [[[[[input[i][j][k] if k < len(input[i][j]) else 0 for k in range(max_depth)] if j < len(input[i]) else [0] * max_depth for j in range(max_height)] if i < len(input) else [[0] * max_depth] * max_height for i in range(max_width)] for input in inputs]]
示例

input1 = [
[[1, 1, 1], [2, 2, 2], [3, 3, 3]], 
[[4, 4, 4], [5, 5, 5]]
]
input2 = [
[[1, 1, 1], [2, 2, 2], [3, 3, 3]], 
[[6, 6, 6]],
[[4, 4, 4], [5, 5, 5]]
]
output = fill_missing_dimension([input1, input2])

输出:

> output
[[[[1, 1, 1], [2, 2, 2], [3, 3, 3]],
[[4, 4, 4], [5, 5, 5], [0, 0, 0]],
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]],
[[[1, 1, 1], [2, 2, 2], [3, 3, 3]],
[[6, 6, 6], [0, 0, 0], [0, 0, 0]],
[[4, 4, 4], [5, 5, 5], [0, 0, 0]]]]

如果您想使用输出作为numpy array,您可以使用np.array(),如下所示:

import numpy as np
# convert to numpy array
output = np.array(output)
print(output.shape) # (2, 3, 3, 3)

您仍然可以使用pad_sequence执行此操作,但是您必须包含一个初始for循环来平衡倒数第二个维度。

import torch
from torch.nn.utils.rnn import pad_sequence
sequences = [
[
torch.Tensor([[1, 1, 1], [2, 2, 2], [3, 3, 3]]),
torch.Tensor([[4, 4, 4], [5, 5, 5]])
],
[
torch.Tensor([[1, 1, 1], [2, 2, 2], [3, 3, 3]]),
torch.Tensor([[6, 6, 6]]),
torch.Tensor([[4, 4, 4], [5, 5, 5]])
]
]
padded_sequences = []
# Loop through the sequences for the initial padding
for sequence in sequences:
padded_sequences.append(pad_sequence(sequence,
batch_first=True,
padding_value=0))
# The shapes for the tensors in padded_sequences are now:
# (2, 3, 3)
# (3, 3, 3)
padded_sequences = pad_sequence(padded_sequences,
batch_first=True,
padding_value=0)
print(padded_sequences.shape)
print(padded_sequences)

只使用一个for循环,就创建了所需的张量。

输出:

torch.Size([2, 3, 3, 3])
tensor([[[[1., 1., 1.],
[2., 2., 2.],
[3., 3., 3.]],
[[4., 4., 4.],
[5., 5., 5.],
[0., 0., 0.]],
[[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]]],

[[[1., 1., 1.],
[2., 2., 2.],
[3., 3., 3.]],
[[6., 6., 6.],
[0., 0., 0.],
[0., 0., 0.]],
[[4., 4., 4.],
[5., 5., 5.],
[0., 0., 0.]]]])

最新更新