声明:本文章只代表个人观点,加之个人对paddle理解不是很透彻,可能有一些错误,求轻指
由于后文部分Lod-Tensor需要进行展示,为了更好观看效果,请移步:http://aistudio.baidu.com/?hmsr=aibanner&hmpl=aistudio#/projectdetail/39189 并执行
本章我们主要对 PaddlePaddle 中的张量进行讲解。首先介绍 Fluid 中的 Tensor,然后介绍 LoD-Tensor 的相关知识。
PaddlePaddle 中使用的 Tensor 为 LoD-Tensor(Level-of-Detail Tensor)。它是 Fluid 中特有的概念,在普通 Tensor 的基础上附加了序列信息。Fluid中可传输的数据包括:输入、输出、网络中的可学习参数,全部统一使用 LoD-Tensor 表示。
在神经网络中传递的数据都是 Tensor, Tensor 可以简单理解成一个多维数组,一般而言可以有任意多的维度。不同的Tensor可以具有自己的数据类型和形状,同一 Tensor 中每个元素的数据类型是一样的,Tensor 的形状就是 Tensor 的维度。
在 Fluid 中存在三种特殊的 Tensor:
- 模型中的可学习参数
- 输入输出Tensor
- 常量 Tensor
Fluid 中的 Tensor
模型中的可学习参数
模型中的可学习参数(包括网络权重、偏置等)生存期和整个训练任务一样长,会接受优化算法的更新,在 Fluid 中以 Variable 的子类 Parameter 表示。
在Fluid中可以通过 fluid.layers.create_parameter 来创建可学习参数,例如通过下面程序来创建一个权重参数:
import paddle
import paddle.fluid as fluid
w = fluid.layers.create_parameter(name='w', shape=[1], dtype='float32')
print(w)
一般情况下,不需要自己来创建网络中的可学习参数,Fluid 为大部分常见的神经网络基本计算模块都提供了封装。以最简单的全连接模型为例,下面的代码片段会直接为全连接层创建连接权值(W)和偏置( bias )两个可学习参数,无需显式地调用 Parameter 相关接口来创建。
var_program = fluid.Program()
# define a Program to sa-ve fc operator
with fluid.program_guard(var_program, fluid.Program()):
x = fluid.layers.data(name='x', shape=[13], dtype='float32')
print('variables before fc operation.')
params = [v for v in var_program.list_vars() if isinstance(v, fluid.framework.Parameter)]
print(params)
y = fluid.layers.fc(input=x, size=128, bias_attr=True)
print('-'*30)
print('variables after fc operation')
params = [v for v in var_program.list_vars() if isinstance(v, fluid.framework.Parameter)]
print(params)
# 输出结果
variables before fc operation.
[]
------------------------------
variables after fc operation
[name: "fc_0.w_0"
type {
type: LOD_TENSOR
lod_tensor {
tensor {
data_type: FP32
dims: 13
dims: 128
}
}
}
persistable: true
, name: "fc_0.b_0"
type {
type: LOD_TENSOR
lod_tensor {
tensor {
data_type: FP32
dims: 128
}
}
}
persistable: true
]
从输出结果中,可以看到通过调用 fluid.layers.fc(input=x, size=128, bias_attr=True) 后,会自动生成该层对应的可学习参数。其中 fc_0.w_0 是全连接层权重参数的名字,shape 为 (13, 128),fc_0.b_0 是全连接层偏置参数的名字,shape 为 (128)。
输入输出 Tensor
整个神经网络的输入数据也是一个特殊的 Tensor,在这个 Tensor 中,一些维度的大小在定义模型时无法确定(通常包括:batch size,如果 mini-batch 之间数据可变,也会包括图片的宽度和高度等),在定义模型时需要占位。
Fluid 中使用 fluid.layers.data 来接收输入数据, fluid.layers.data 需要提供输入 Tensor 的形状信息,当遇到无法确定的维度时,相应维度指定为 None ,如下面的代码片段所示:
# 定义 input_x 的维度为 [3,None],其中我们只能确定 input_x 的第一的维度为3,第二个维度未知,要在程序执行过程中才能确定
input_x = fluid.layers.data(name="x", shape=[3, None], dtype="int64")
print('shape of input_x: {}'.format(input_x.shape))
# batch size无需显示指定,框架会自动补充第0维为batch size,并在运行时填充正确数值
input_a = fluid.layers.data(name="a", shape=[3, 4], dtype='int64')
print('shape of input_a: {}'.format(input_a.shape))
# 若图片的宽度和高度在运行时可变,将宽度和高度定义为 None。
# shape的三个维度含义分别是:channel、图片的高度、图片的宽度
input_b = fluid.layers.data(name="image", shape=[3, None, None], dtype="float32")
print('shape of input_b: {}'.format(input_b.shape))
# 输出结果
shape of input_x: (3L, -1L)
shape of input_a: (-1L, 3L, 4L)
shape of input_b: (3L, -1L, -1L)
常量 Tensor
Fluid 通过 fluid.layers.fill_constant 来实现常量 Tensor,用户可以指定 Tensor 的形状,数据类型和常量值。例如:
constant_data = fluid.layers.fill_constant(shape=[1], value=0, dtype='int64')
print(constant_data)
ones_data = fluid.layers.ones(shape=[1], dtype='int64')
print(ones_data)
zeros_data = fluid.layers.zeros(shape=[1], dtype='int64')
print(zeros_data)
# 输出结果
name: "fill_constant_0.tmp_0"
type {
type: LOD_TENSOR
lod_tensor {
tensor {
data_type: INT64
dims: 1
}
}
}
persistable: false
name: "fill_constant_1.tmp_0"
type {
type: LOD_TENSOR
lod_tensor {
tensor {
data_type: INT64
dims: 1
}
}
}
persistable: false
name: "fill_constant_2.tmp_0"
type {
type: LOD_TENSOR
lod_tensor {
tensor {
data_type: INT64
dims: 1
}
}
}
persistable: false
需要注意的是,上述定义的 tensor 并不具有值,它们表示将要执行的op,具体输出数值将在 Executor 运行时得到。
c_tensor = fluid.layers.fill_constant(shape=[2, 4], value=2, dtype='float32')
exe = fluid.Executor(fluid.CPUPlace())
constant_value = exe.run(fetch_list=[c_tensor])
print(constant_value)
# 结果输出
[array([[2., 2., 2., 2.],
[2., 2., 2., 2.]], dtype=float32)]
LoD-Tensosr
大多数的深度学习框架使用 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 中的序列数据必须保持长度一致,因此您不需要执行填充op,也可以满足处理NLP等具有序列要求的任务需求。
Fluid 引入了一个索引数据结构(LoD)来将张量分割成序列。
LoD 索引
1. 句子组成的 mini-batch
假设一个 mini-batch 中有3个句子,每个句子中分别包含3个、1个和2个单词。我们可以用 (3+1+2)xD 维Tensor 加上一些索引信息来表示这个mini-batch:
3 1 2
| | | | | |
上述表示中,每一个|代表一个D维的词向量,数字3,1,2构成了 1-level LoD,即 lod-level=1 的Tensor。
2. 递归序列
假设存在一个 mini-batch 中包含3个句子、1个句子和2个句子的文章,每个句子都由不同数量的单词组成,则这个 mini-batch 的样式可以看作:
3 1 2 # 句子数
3 2 4 1 2 3 # 词数
||| || |||| | || |||
表示的LoD信息为:
[[3,1,2]/*level=0*/,[3,2,4,1,2,3]/*level=1*/]
3. 视频的mini-batch
在视觉任务中,时常需要处理视频和图像这些元素是高维的对象,假设现存的一个nimi-batch包含3个视频,分别有3个,1个和2个帧,每个帧都具有相同大小:640x480,则这个mini-batch可以被表示为:
3 1 2
口口口 口 口口
最底层 tensor 大小为(3+1+2)x640x480,每一个 口 表示一个640x480的图像
4. 图像的mini-batch
在传统的情况下,比如有N个固定大小的图像的mini-batch,LoD-Tensor表示为:
1 1 1 1 1
口口口口 ... 口
在这种情况下,我们不会因为索引值都为1而忽略信息,仅仅把LoD-Tensor看作是一个普通的张量:
口口口口 ... 口
在Fluid中它们被表示为一个0-level LoD-Tensor。
LoDTensor的偏移表示
为了快速访问基本序列,Fluid提供了一种偏移表示的方法——保存序列的开始和结束元素,而不是保存长度。
由于内容较多,这里就不全写了,感兴趣的可以移步:http://aistudio.baidu.com/?hmsr=aibanner&hmpl=aistudio#/projectdetail/39189,并执行查看。
你这是把文档中心里的给复制出来了啊
可以这样说吧。因为LoD-Tensor是Fluid特有的,完全没有其他的参考资料。我就是相当于将文档归档一下,另外通过一些代码来对一些概念进行讲解,这样更容易理解。中间也有部分自己的总结。
哦哦哦
感谢分享~