首页 Paddle框架 帖子详情
Paddle Tensor
收藏
快速回复
Paddle框架 问答模型训练 5422 4
Paddle Tensor
收藏
快速回复
Paddle框架 问答模型训练 5422 4

声明:本文章只代表个人观点,加之个人对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,并执行查看。

0
收藏
回复
全部评论(4)
时间顺序
goJhou
#2 回复于2019-01

你这是把文档中心里的给复制出来了啊

0
回复
busyboxs
#3 回复于2019-01
goJhou #2
你这是把文档中心里的给复制出来了啊

可以这样说吧。因为LoD-Tensor是Fluid特有的,完全没有其他的参考资料。我就是相当于将文档归档一下,另外通过一些代码来对一些概念进行讲解,这样更容易理解。中间也有部分自己的总结。

0
回复
goJhou
#4 回复于2019-01
可以这样说吧。因为LoD-Tensor是Fluid特有的,完全没有其他的参考资料。我就是相当于将文档归档一下,另外通过一些代码来对一些概念进行讲解,这样更容易理解。中间也有部分自己的总结。

哦哦哦

0
回复
l
lucywsq
#5 回复于2019-01

感谢分享~

0
回复
需求/bug反馈?一键提issue告诉我们
发现bug?如果您知道修复办法,欢迎提pr直接参与建设飞桨~
在@后输入用户全名并按空格结束,可艾特全站任一用户