Paddle 的 LoDTensor

LoD (Level-of-Detail) Tensor 是 Paddle 的高级特性,是对 Tensor 的一种扩充。关于 LoDTensor 的官方介绍请见 LoDTensor 基本概念LoDTensor Api

在 NLP 中,典型的应用场景为一个 batch 内包含多个句子,但是每个句子长度不一致。一般的做法是进行 padding,将所有句子补齐为同样的长度,但是例如 LSTM 这种网络可能会需要保持句子的原始长度。LoDTensor 专门用来解决这种问题。LoDTensor 存储了样本的长度信息,不需要增加 padding 的词,能给大幅减少计算量,从而提高训练的速度。

LoDTensor 将长度不一致的维度拼接为一个大的维度,并引入了一个索引数据结构(LoD)来将张量分割成序列。LoDTensor 进行了维度拼接之后,rank 大小和之前 padding 的方式不一致,在一些运算(如 dot attention)逻辑比 padding 方式要复杂。

LoD 索引

例子

句子组成的 mini-batch

假设一个 mini-batch 中有 3 个句子,每个句子中分别包含 3、1 和 2 个单词。我们可以用 (3+1+2)xD 维 Tensor 加上一些索引信息来表示这个 mini-batch:

1
2
3       1   2
| | | | | |

上述表示中,每一个 | 代表一个 D 维的词向量,数字 3,1,2 构成了 1-level LoD。

递归序列

LoDTensor 可以存在嵌套的关系。假设存在一个 mini-batch 中包含 3、1 和 2 个句子的文章,每个句子都由不同数量的单词组成,则这个 mini-batch 的样式可以看作:

1
2
3
3            1 2
3 2 4 1 2 3
||| || |||| | || |||

则它的 LoD 信息为:

1
[[312]/*level=0*/,[324123]/*level=1*/]

当然,这样的 LoD level 可以存在任意层。

recursive_sequence_length

Paddle 中每个 LoDTensor 有一个 recursive_sequence_length 成员,它是一个双层的嵌套列表。最外层列表的大小表示 lod-level 的个数;内部的每个列表,表示每个 lod-level 下,每个元素的大小。上述两个例子的 LoDTensor 对应的 recursive_sequence_length 分别为 [[3, 1, 2]][[3,1,2],[3,2,4,1,2,3]]

LoDTensor 的偏移表示

为了快速访问基本序列,Paddle 提供了一种偏移表示的方法——保存序列的开始和结束元素,而不是保存长度。上述两个例子对应的偏移表示分别为 [[0, 3, 4, 6]][[0, 3, 4, 6], [0, 3, 5, 9, 10, 12, 15]]

lod

每个 LodTensor 都有一个 lod 成员,该接口返回 LoDTensor 的偏移表示 LoD。

LoDTensor

以下是类 paddle.fluid.LoDTensor 的成员:

  • LoDTensor.has_valid_recursive_sequence_lengths():该接口检查 LoDTensor 的 LoD 的正确性。
  • LoDTensor.lod():该接口返回 LoDTensor 的 LoD。
  • LoDTensor.recursive_sequence_lengths():该接口返回与 LoDTensor 的 LoD 对应的递归序列长度。
  • LoDTensor.set(array, place):该接口根据输入的 numpy array 和设备 place,设置 LoDTensor 的数据。
  • LoDTensor.set_lod(lod):该接口设置 LoDTensor 的 LoD。
  • LoDTensor.set_recursive_sequence_lengths(recursive_sequence_lengths):该接口根据递归序列长度设置 LoDTensor 的 LoD。
  • LoDTensor.shape():该接口返回 LoDTensor 的 shape。

可以通过 numpy.array(LoDTensor) 来将 LoDTensor 转换为 numpy.ndarray。