forked from PaddlePaddle/docs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
update the content of basic_concept and prepare_data (PaddlePaddle#1133)
Unify the guide content structure.
- Loading branch information
Showing
10 changed files
with
936 additions
and
184 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,374 @@ | ||
############ | ||
基本概念 | ||
############ | ||
.. _cn_user_guide_lod_tensor: | ||
|
||
本文介绍Fluid版本基本使用概念: | ||
################## | ||
LoD-Tensor使用说明 | ||
################## | ||
|
||
- `LoD-Tensor使用说明 <lod_tensor.html>`_ : LoD-Tensor是Fluid中特有的概念,它在Tensor基础上附加了序列信息,支持处理变长数据。 | ||
LoD(Level-of-Detail) Tensor是Fluid中特有的概念,它在Tensor基础上附加了序列信息。Fluid中可传输的数据包括:输入、输出、网络中的可学习参数,全部统一使用LoD-Tensor表示。 | ||
|
||
.. toctree:: | ||
:hidden: | ||
阅读本文档将帮助您了解 Fluid 中的 LoD-Tensor 设计思想,以便您更灵活的使用这一数据类型。 | ||
|
||
变长序列的挑战 | ||
================ | ||
|
||
大多数的深度学习框架使用Tensor表示一个mini-batch。 | ||
|
||
例如一个mini-batch中有10张图片,每幅图片大小为32x32,则这个mini-batch是一个10x32x32的 Tensor。 | ||
|
||
或者在处理NLP任务中,一个mini-batch包含N个句子,每个字都用一个D维的one-hot向量表示,假设所有句子都用相同的长度L,那这个mini-batch可以被表示为NxLxD的Tensor。 | ||
|
||
上述两个例子中序列元素都具有相同大小,但是在许多情况下,训练数据是变长序列。基于这一场景,大部分框架采取的方法是确定一个固定长度,对小于这一长度的序列数据以0填充。 | ||
|
||
在Fluid中,由于LoD-Tensor的存在,我们不要求每个mini-batch中的序列数据必须保持长度一致,因此您不需要执行填充操作,也可以满足处理NLP等具有序列要求的任务需求。 | ||
|
||
Fluid引入了一个索引数据结构(LoD)来将张量分割成序列。 | ||
|
||
|
||
LoD 索引 | ||
=========== | ||
|
||
为了更好的理解LoD的概念,本节提供了几个例子供您参考: | ||
|
||
**句子组成的 mini-batch** | ||
|
||
假设一个mini-batch中有3个句子,每个句子中分别包含3个、1个和2个单词。我们可以用(3+1+2)xD维Tensor 加上一些索引信息来表示这个mini-batch: | ||
|
||
.. code-block :: text | ||
3 1 2 | ||
| | | | | | | ||
上述表示中,每一个 :code:`|` 代表一个D维的词向量,数字3,1,2构成了 1-level LoD。 | ||
|
||
**递归序列** | ||
|
||
让我们来看另一个2-level LoD-Tensor的例子:假设存在一个mini-batch中包含3个句子、1个句子和2个句子的文章,每个句子都由不同数量的单词组成,则这个mini-batch的样式可以看作: | ||
|
||
.. code-block:: text | ||
3 1 2 | ||
3 2 4 1 2 3 | ||
||| || |||| | || ||| | ||
表示的LoD信息为: | ||
|
||
.. code-block:: text | ||
[[3,1,2]/*level=0*/,[3,2,4,1,2,3]/*level=1*/] | ||
**视频的mini-batch** | ||
|
||
在视觉任务中,时常需要处理视频和图像这些元素是高维的对象,假设现存的一个mini-batch包含3个视频,分别有3个,1个和2个帧,每个帧都具有相同大小:640x480,则这个mini-batch可以被表示为: | ||
|
||
.. code-block:: text | ||
3 1 2 | ||
口口口 口 口口 | ||
最底层tensor大小为(3+1+2)x640x480,每一个 :code:`口` 表示一个640x480的图像 | ||
|
||
**图像的mini-batch** | ||
|
||
在传统的情况下,比如有N个固定大小的图像的mini-batch,LoD-Tensor表示为: | ||
|
||
.. code-block:: text | ||
1 1 1 1 1 | ||
口口口口 ... 口 | ||
在这种情况下,我们不会因为索引值都为1而忽略信息,仅仅把LoD-Tensor看作是一个普通的张量: | ||
|
||
.. code-block:: text | ||
口口口口 ... 口 | ||
**模型参数** | ||
|
||
模型参数只是一个普通的张量,在Fluid中它们被表示为一个0-level LoD-Tensor。 | ||
|
||
LoDTensor的偏移表示 | ||
===================== | ||
|
||
为了快速访问基本序列,Fluid提供了一种偏移表示的方法——保存序列的开始和结束元素,而不是保存长度。 | ||
|
||
在上述例子中,您可以计算基本元素的长度: | ||
|
||
.. code-block:: text | ||
3 2 4 1 2 3 | ||
将其转换为偏移表示: | ||
|
||
.. code-block:: text | ||
0 3 5 9 10 12 15 | ||
= = = = = = | ||
3 2+3 4+5 1+9 2+10 3+12 | ||
所以我们知道第一个句子是从单词0到单词3,第二个句子是从单词3到单词5。 | ||
|
||
类似的,LoD的顶层长度 | ||
|
||
.. code-block:: text | ||
3 1 2 | ||
可以被转化成偏移形式: | ||
|
||
.. code-block:: text | ||
0 3 4 6 | ||
= = = | ||
3 3+1 4+2 | ||
因此该LoD-Tensor的偏移表示为: | ||
|
||
.. code-block:: text | ||
0 3 4 6 | ||
3 5 9 10 12 15 | ||
LoD-Tensor | ||
============= | ||
一个LoD-Tensor可以被看作是一个树的结构,树叶是基本的序列元素,树枝作为基本元素的标识。 | ||
|
||
在 Fluid 中 LoD-Tensor 的序列信息有两种表述形式:原始长度和偏移量。在 Paddle 内部采用偏移量的形式表述 LoD-Tensor,以获得更快的序列访问速度;在 python API中采用原始长度的形式表述 LoD-Tensor 方便用户理解和计算,并将原始长度称为: :code:`recursive_sequence_lengths` 。 | ||
|
||
以上文提到的一个2-level LoD-Tensor为例: | ||
|
||
.. code-block:: text | ||
3 1 2 | ||
3 2 4 1 2 3 | ||
||| || |||| | || ||| | ||
- 以偏移量表示此 LoD-Tensor:[ [0,3,4,6] , [0,3,5,9,10,12,15] ], | ||
- 以原始长度表达此 Lod-Tensor:recursive_sequence_lengths=[ [3-0 , 4-3 , 6-4] , [3-0 , 5-3 , 9-5 , 10-9 , 12-10 , 15-12] ]。 | ||
|
||
|
||
以文字序列为例: [3,1,2] 可以表示这个mini-batch中有3篇文章,每篇文章分别有3、1、2个句子,[3,2,4,1,2,3] 表示每个句子中分别含有3、2、4、1、2、3个字。 | ||
|
||
recursive_seq_lens 是一个双层嵌套列表,也就是列表的列表,最外层列表的size表示嵌套的层数,也就是lod-level的大小;内部的每个列表,对应表示每个lod-level下,每个元素的大小。 | ||
|
||
下面三段代码分别介绍如何创建一个LoD-Tensor,如何将LoD-Tensor转换成Tensor,如何将Tensor转换成LoD-Tensor: | ||
|
||
* 创建 LoD-Tensor | ||
|
||
.. code-block:: python | ||
#创建lod-tensor | ||
import paddle.fluid as fluid | ||
import numpy as np | ||
a = fluid.create_lod_tensor(np.array([[1],[1],[1], | ||
[1],[1], | ||
[1],[1],[1],[1], | ||
[1], | ||
[1],[1], | ||
[1],[1],[1]]).astype('int64') , | ||
[[3,1,2] , [3,2,4,1,2,3]], | ||
fluid.CPUPlace()) | ||
#查看lod-tensor嵌套层数 | ||
print (len(a.recursive_sequence_lengths())) | ||
# output:2 | ||
#查看最基础元素个数 | ||
print (sum(a.recursive_sequence_lengths()[-1])) | ||
# output:15 (3+2+4+1+2+3=15) | ||
* LoD-Tensor 转 Tensor | ||
|
||
.. code-block:: python | ||
import paddle.fluid as fluid | ||
import numpy as np | ||
# 创建一个 LoD-Tensor | ||
a = fluid.create_lod_tensor(np.array([[1.1], [2.2],[3.3],[4.4]]).astype('float32'), [[1,3]], fluid.CPUPlace()) | ||
def LodTensor_to_Tensor(lod_tensor): | ||
# 获取 LoD-Tensor 的 lod 信息 | ||
lod = lod_tensor.lod() | ||
# 转换成 array | ||
array = np.array(lod_tensor) | ||
new_array = [] | ||
# 依照原LoD-Tensor的层级信息,转换成Tensor | ||
for i in range(len(lod[0]) - 1): | ||
new_array.append(array[lod[0][i]:lod[0][i + 1]]) | ||
return new_array | ||
new_array = LodTensor_to_Tensor(a) | ||
# 输出结果 | ||
print(new_array) | ||
* Tensor 转 LoD-Tensor | ||
|
||
.. code-block:: python | ||
import paddle.fluid as fluid | ||
import numpy as np | ||
def to_lodtensor(data, place): | ||
# 存储Tensor的长度作为LoD信息 | ||
seq_lens = [len(seq) for seq in data] | ||
cur_len = 0 | ||
lod = [cur_len] | ||
for l in seq_lens: | ||
cur_len += l | ||
lod.append(cur_len) | ||
# 对待转换的 Tensor 降维 | ||
flattened_data = np.concatenate(data, axis=0).astype("int64") | ||
flattened_data = flattened_data.reshape([len(flattened_data), 1]) | ||
# 为 Tensor 数据添加lod信息 | ||
res = fluid.LoDTensor() | ||
res.set(flattened_data, place) | ||
res.set_lod([lod]) | ||
return res | ||
# new_array 为上段代码中转换的Tensor | ||
lod_tensor = to_lodtensor(new_array,fluid.CPUPlace()) | ||
# 输出 LoD 信息 | ||
print("The LoD of the result: {}.".format(lod_tensor.lod())) | ||
# 检验与原Tensor数据是否一致 | ||
print("The array : {}.".format(np.array(lod_tensor))) | ||
代码示例 | ||
=========== | ||
|
||
本节代码将根据指定的级别y-lod,扩充输入变量x。本例综合了LoD-Tensor的多个重要概念,跟随代码实现,您将: | ||
|
||
- 直观理解Fluid中 :code:`fluid.layers.sequence_expand` 的实现过程 | ||
- 掌握如何在Fluid中创建LoD-Tensor | ||
- 学习如何打印LoDTensor内容 | ||
|
||
|
||
|
||
**定义计算过程** | ||
|
||
layers.sequence_expand通过获取 y 的 lod 值对 x 的数据进行扩充,关于 :code:`fluid.layers.sequence_expand` 的功能说明,请先阅读 :ref:`cn_api_fluid_layers_sequence_expand` 。 | ||
|
||
序列扩充代码实现: | ||
|
||
.. code-block:: python | ||
x = fluid.layers.data(name='x', shape=[1], dtype='float32', lod_level=1) | ||
y = fluid.layers.data(name='y', shape=[1], dtype='float32', lod_level=2) | ||
out = fluid.layers.sequence_expand(x=x, y=y, ref_level=0) | ||
*说明*:输出LoD-Tensor的维度仅与传入的真实数据维度有关,在定义网络结构阶段为x、y设置的shape值,仅作为占位,并不影响结果。 | ||
|
||
**创建Executor** | ||
|
||
.. code-block:: python | ||
place = fluid.CPUPlace() | ||
exe = fluid.Executor(place) | ||
exe.run(fluid.default_startup_program()) | ||
**准备数据** | ||
|
||
这里我们调用 :code:`fluid.create_lod_tensor` 创建 :code:`sequence_expand` 的输入数据,通过定义 y_d 的 LoD 值,对 x_d 进行扩充。其中,输出值只与 y_d 的 LoD 值有关,y_d 的 data 值在这里并不参与计算,维度上与LoD[-1]一致即可。 | ||
|
||
:code:`fluid.create_lod_tensor()` 的使用说明请参考 :ref:`cn_api_fluid_create_lod_tensor` 。 | ||
|
||
实现代码如下: | ||
|
||
.. code-block:: python | ||
x_d = fluid.create_lod_tensor(np.array([[1.1],[2.2],[3.3],[4.4]]).astype('float32'), [[1,3]], place) | ||
y_d = fluid.create_lod_tensor(np.array([[1.1],[1.1],[1.1],[1.1],[1.1],[1.1]]).astype('float32'), [[1,3], [2,1,2,1]],place) | ||
**执行运算** | ||
|
||
在Fluid中,LoD>1的Tensor与其他类型的数据一样,使用 :code:`feed` 定义数据传入顺序。此外,由于输出results是带有LoD信息的Tensor,需在exe.run( )中添加 :code:`return_numpy=False` 参数,获得LoD-Tensor的输出结果。 | ||
|
||
.. code-block:: python | ||
results = exe.run(fluid.default_main_program(), | ||
feed={'x':x_d, 'y': y_d }, | ||
fetch_list=[out],return_numpy=False) | ||
**查看LodTensor结果** | ||
|
||
由于LoDTensor的特殊属性,无法直接print查看内容,常用操作时将LoD-Tensor作为网络的输出fetch出来,然后执行 numpy.array(lod_tensor), 就能转成numpy array: | ||
|
||
.. code-block:: python | ||
np.array(results[0]) | ||
输出结果为: | ||
|
||
.. code-block:: text | ||
array([[1.1],[2.2],[3.3],[4.4],[2.2],[3.3],[4.4],[2.2],[3.3],[4.4]]) | ||
**查看序列长度** | ||
|
||
可以通过查看序列长度得到 LoDTensor 的递归序列长度: | ||
|
||
.. code-block:: python | ||
results[0].recursive_sequence_lengths() | ||
输出结果为: | ||
|
||
.. code-block:: text | ||
[[1L, 3L, 3L, 3L]] | ||
**完整代码** | ||
|
||
您可以运行下列完整代码,观察输出结果: | ||
|
||
.. code-block:: python | ||
#加载库 | ||
import paddle | ||
import paddle.fluid as fluid | ||
import numpy as np | ||
#定义前向计算 | ||
x = fluid.layers.data(name='x', shape=[1], dtype='float32', lod_level=1) | ||
y = fluid.layers.data(name='y', shape=[1], dtype='float32', lod_level=2) | ||
out = fluid.layers.sequence_expand(x=x, y=y, ref_level=0) | ||
#定义运算场所 | ||
place = fluid.CPUPlace() | ||
#创建执行器 | ||
exe = fluid.Executor(place) | ||
exe.run(fluid.default_startup_program()) | ||
#创建LoDTensor | ||
x_d = fluid.create_lod_tensor(np.array([[1.1], [2.2],[3.3],[4.4]]).astype('float32'), [[1,3]], place) | ||
y_d = fluid.create_lod_tensor(np.array([[1.1],[1.1],[1.1],[1.1],[1.1],[1.1]]).astype('float32'), [[1,3], [1,2,1,2]], place) | ||
#开始计算 | ||
results = exe.run(fluid.default_main_program(), | ||
feed={'x':x_d, 'y': y_d }, | ||
fetch_list=[out],return_numpy=False) | ||
#输出执行结果 | ||
print("The data of the result: {}.".format(np.array(results[0]))) | ||
#输出 result 的序列长度 | ||
print("The recursive sequence lengths of the result: {}.".format(results[0].recursive_sequence_lengths())) | ||
#输出 result 的 LoD | ||
print("The LoD of the result: {}.".format(results[0].lod())) | ||
总结 | ||
======== | ||
|
||
至此,相信您已经基本掌握了LoD-Tensor的概念,尝试修改上述代码中的 x_d 与 y_d,观察输出结果,有助于您更好的理解这一灵活的结构。 | ||
|
||
更多LoDTensor的模型应用,可以参考新手入门中的 `词向量 <../../../beginners_guide/basics/word2vec/index.html>`_ 、`个性化推荐 <../../../beginners_guide/basics/recommender_system/index.html>`_、`情感分析 <../../../beginners_guide/basics/understand_sentiment/index.html>`_ 等指导教程。 | ||
|
||
更高阶的应用案例,请参考 `模型库 <../../../user_guides/models/index_cn.html>`_ 中的相关内容。 | ||
|
||
lod_tensor.rst |
Oops, something went wrong.