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 | 3 1 2 |
上述表示中,每一个 |
代表一个 D 维的词向量,数字 3,1,2 构成了 1-level LoD。
递归序列
LoDTensor 可以存在嵌套的关系。假设存在一个 mini-batch 中包含 3、1 和 2 个句子的文章,每个句子都由不同数量的单词组成,则这个 mini-batch 的样式可以看作:
1 | 3 1 2 |
则它的 LoD 信息为:
1 | [[3,1,2]/*level=0*/,[3,2,4,1,2,3]/*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。