PaddlePaddle入门——基本概念

最近报了百度的深度学习认证,需要使用Paddle进行编程实现,找了一些基础教程,特意记录下来,加深印象。思维导图如下:

 

一、Paddle的内部执行流程

二、内部详解

1.Variable(变量)

(1)模型中的可学习参数

(2)占位Variable

(3)常量Variable

2.Tensor

3.Lod-Tensor

4.Operator(算子)

5.Program

6.Executor(执行器)

7.命令式编程

8.编程指南


一、Paddle的内部执行流程

Paddle使用一种编译器式的执行流程,分为编译时和运行时两个部分,具体包括:编译器定义Program,创建Executor运行Program。执行流程图如下:

1.编译时,python程序是通过调用Paddle提供的段子,向一段Program中添加变量(Tensor)以及对变量的操作(Operators或者Layers)。用户只需要描述核心的前向计算,不需要关系反向计算、分布式下以及异构设备下如何计算。

2.原始的Program在框架内部转换为中间描述语言:ProgramDesc

3.Transpiler接受一段ProgramDesc,输出一段变化后的ProgramDesc,作为后端Executor最终需要执行的Program。Transpiler并非必需步骤。

4.执行ProgramDesc中定义的Operator(可以类比为程序语言中的指令),在执行过程中会为Operator创建所需的输入输出并进行管理。

二、内部详解

1.Variable(变量)

Variable(变量)可以包含任何类型的值变量,提供的API中用的类型是Tensor。

存在三种Variable:

(1)模型中的可学习参数

例如,神经网络中的权重和偏置会接受优化算法额更新,因此,应该是一个变量类型的数据,表示为:

w = fluid.layers.create_parameter(name="w",shape=[1],dtype='float32')

但是,Paddle中大部分的神经网络都是封装好的,可以直接创建权重和偏置,无需显示调用Parameter相关接口来创建。例如:

y = fluid.layers.fc(input=x, size=128, bias_attr=True)

其中,fluid.layers.fc是全连接神经网络的API。

(2)占位Variable

在声明式编程模式(静态图)中,组网不知道实际输入的信息,此刻需要一个占位的Variable,表示一个待提供输入的Variable。fluid.data表示需要提供输入Tensor的形状信息,当遇到无法确定的维度时,相应维度指定为None,如下:

import paddle.fluid as fluid#定义x的维度为[3,None],其中我们只能确定x的第一的维度为3,第二个维度未知,要在程序执行过程中才能确定
x = fluid.data(name="x", shape=[3,None], dtype="int64")#若图片的宽度和高度在运行时可变,将宽度和高度定义为None。
#shape的三个维度含义分别是:batch_size, channel、图片的宽度、图片的高度
b = fluid.data(name="image",shape=[None, 3,None,None],dtype="float32")

dtype是表示数据类型,int64是有符号的64位整数数据类型,可支持的类型如下:

(3)常量Variable

可以通过fluid.layers.fill_constant来实现常量Variable,并可以指定内部的Tensor形状,数据类型和常量值。

import paddle.fluid as fluid
data = fluid.layers.fill_constant(shape=[1], value=0, dtype='int64')

2.Tensor

Tensor可以简单理解为多维数组,同一个Tensor每个元素的数据类型是一样的,Tensor的形状是Tensor的维度。

Tensorflow中Tensor(张量)与Numpy的ndarray对象相似,每个Tensor都有一个形状和数据类型,Numpy数组和Tensor的区别是:

(1)张量可以有加速器内存(CPU,GPU)支持

(2)张量是不变的

下图直观的表达了1~6维的Tensor:

对于一些任务中batch内样本大小不一致的问题,Paddle提供了两种解决方案:

(1)padding:将大小不一致的样本padding到同样的大小。

(2)Lod-Tensor:记录每一个样本的大小,减少无用的计算量,Lod牺牲灵活性来提升性能。当样本无法通过分桶、排序等方式使得大小接近,可以用Lod-Tensor。

Tensor的API有:

  • create_tensor:创建一个指定数据类型的Lod-Tensor变量
  • create_parameter:创建一个可学习的参数
  • create_grobal_var:创建一个全局的tensor
  • cast:将数据转换为指定类型
  • concat:将输入数据沿指定维度连接
  • sums:对输入数据加和
  • fill_constant:创建一个具有特定形状和类型的tensor,可以通过value设置该变量的初始值
  • assign:复制一个变量
  • argmin:计算输入tensor指定轴上最小元素的索引
  • argmax:计算输入tensor指定轴上最大元素的索引
  • argsort:对输入Tensor在指定轴上进行排序,并返回排序后的数据变量及其对应的索引值
  • ones:创建一个指定大小和数据类型的Tensor,且初始值为1
  • zeros:创建一个指定大小和数据类型的Tensor,且初始值为0
  • reverse:使用reverse沿指定轴反转Tensor

3.Lod-Tensor

在nlp任务中,一个batch中包含N个句子,句子的长度不一,为了解决长度不一致的问题,Paddle提供了两种方案:(1)padding,(2)Lod-Tensor,tensor中同时保存序列的长度信息。

对于padding的方式,会增加框架的计算量,但是可以通过分桶,排序等机制,使得一个batch内的句子长度尽可能接近,能够降低padding的比例,还通过引入mask来记录padding 信息,来移除padding对于训练效果的影响。

但是对于一些nlp任务,例如聊天任务,需要计算query(问题)和多个答案之间的相似度,且答案都是在一个batch中,就可以采用Lod-Tensor方式,Lod-Tensor存储了样本的长度信息,不需要增加padding的词。

Lod-Tensor是将长度不一致的维度拼接为一个大的维度,并引入了一个索引数据结构(LoD)来将张量分割成序列。

4.Operator(算子)

所有对数据的操作都由Operator表示,在paddle.fluid.layers,paddle.fluid.nets模块中。例如:利用paddle.fluid.layers.elementwise_add()实现加法:

import paddle.fluid as fluid
import numpya = fluid.data(name="a",shape=[1],dtype='float32')
b = fluid.data(name="b",shape=[1],dtype='float32')result = fluid.layers.elementwise_add(a,b)# 定义执行器,并且制定执行的设备为CPU
cpu = fluid.core.CPUPlace()
exe = fluid.Executor(cpu)
exe.run(fluid.default_startup_program())x = numpy.array([5]).astype("float32")
y = numpy.array([7]).astype("float32")outs = exe.run(feed={'a':x,'b':y},fetch_list=[a,b,result])
# 打印输出结果,[array([12.], dtype=float32)]
print( outs )

输出结果为:

[array([5.], dtype=float32), array([7.], dtype=float32), array([12.], dtype=float32)]

在程序语言中,控制流(control flow)决定了语句的执行顺序,常见的控制流包括顺序执行、分支和循环等。

(1)If-else条件分支

条件分支,允许对同一个batch的输入,根据给定的条件,分别选择true_block或false_block中的逻辑执行,执行后再将两个分支的输出合并为一个输出。

(2)Switch

多分支选择结构,如同程序语言中常见的switch-case声明,其根据输入表达式的取值不同,选择不同的分支执行。Fluid所定义的控制流的特性有:

  • case的条件是个bool类型的值,即在Program中是一个张量类型的Variable;
  • 依次检查逐个case,选择第一个满足条件的case执行,完成执行后即退出所属的block;
  • 如果所有case均不满足条件,会选择默认的case进行执行

(3)while

While 循环,当条件判断为真时,循环执行 While 控制流所属 block 内的逻辑,条件判断为假时退出循环。与之相关的API有

  • increment :累加API,通常用于对循环次数进行计数;
  • array_read :从 LOD_TENSOR_ARRAY 中指定的位置读入Variable,进行计算;
  • array_write :将 Variable 写回到 LOD_TENSOR_ARRAY 指定的位置,存储计算结果。

(4)DynamicRNN

动态RNN可处理一个batch不等长的序列数据,其接受lod_level=1的Variable作为输入,在DynamicRNNde block内,用户需自定义RNN的单步计算逻辑。在每一时间步,用户可将需记忆的状态写入到DynamicRNN的merory中,并将需要的输出写出到其output中。

(5)StaticRNN

静态RNN,只能处理固定长度的序列数据,接受 lod_level=0 的 Variable 作为输入。与 DynamicRNN 类似,在RNN的每单个时间步,用户需自定义计算逻辑,并可将状态和输出写出

5.Program

用Program的形式动态描述整个计算过程。这种描述方式,兼具网络结构修改的灵活性和模型搭建的便捷性,在保证性能的同时极大地提高了框架对模型的表达能力。

用户定义的Operator会被顺序的放入Program中,在网络搭建过程中,由于不能使用python的控制流,Paddle通过同时提供分布和循环两类控制流op结构的支持,让用户可以通过组合描述任意复杂的模型。

顺序执行:

x = fluid.data(name='x',shape=[None, 13], dtype='float32')
y_predict = fluid.layers.fc(input=x, size=1, act=None)
y = fluid.layers.data(name='y', shape=[1], dtype='float32')
cost = fluid.layers.square_error_cost(input=y_predict, label=y)

Fluid中还有switch,if else类来实现条件选择。

Program设计思想

用户完成网络定义后,一段Paddle程序中通常会存在2个Program:

1.fluid.default_startup_program:定义了模型参数初始化、优化器参数初始化、reader初始化等各种操作。

2.fluid.default_main_program:定义了神经网络模型、前向反向计算,以及模型参数更新、优化器参数更新等各种操作。

Program的基本结构

Paddle的Program的基本结构是一些嵌套的blocks,形式上类似一段C++或Java程序。blocks的概念和通用程序一样。blocks中包含:(1)本地变量的定义,(2)一系列的Operator

import paddle.fluid as fluidx = fluid.data(name='x', shape=[1], dtype='int64') # block 0
y = fluid.data(name='y', shape=[1], dtype='int64') # block 0def true_block():return fluid.layers.fill_constant(dtype='int64', value=1, shape=[1]) # block 1def false_block():return fluid.layers.fill_constant(dtype='int64', value=0, shape=[1]) # block 2condition = fluid.layers.less_than(x, y) # block 0out = fluid.layers.cond(condition, true_block, false_block) # block 0

BlocksDesc和ProgramDesc

用户描述的block和Program信息在Paddle中以protobuf格式保存,在framework.proto中,在Paddle中被称为BlockDesc和ProgramDesc.BlockDesc和ProgramDesc的概念类似于一个抽象语法树。

BlocksDesc中包含本地变量的定义vars,和一系列的Operator ops:

message BlockDesc {required int32 idx = 1;required int32 parent_idx = 2;repeated VarDesc vars = 3;repeated OpDesc ops = 4;
}

parent_idx表示父块,因此block中的操作符可以引用本地定义的变量,也可以 引用祖先块中的定义

Program中的每层block都被压平并存储在数组中。并通过Blocks ID进行索引。

message ProgramDesc {repeated BlockDesc blocks = 1;
}

Blocks的Operator

If-else的Operator包含两个block——True分支和false分支。

6.Executor(执行器)

Paddle的设计思想类似于高级编程语言C++和java等,程序的执行过程被分为编译过程和执行过程。用户完成对Program的定义后,Executor接受这段Program并转换为C++后端真正可执行的FluidProgram,这个叫编译。编译过后需要用Executor进行执行编译好的FluidProgram。

Executor 在运行时将接受一个ProgramDesc、一个block_id和一个ScopeProgramDescblock的列表,每一项包含block中所有参数和operatorprotobuf定义;block_id指定入口块;Scope是所有变量实例的容器。

其中 Scope 包含了 nameVariable 的映射,所有变量都被定义在 Scope 里。大部分API会默认使用 global_scope ,例如 Executor.run ,您也可以指定网络运行在某个特定的 Scope 中,一个网络可以在不同的 Scope内运行,并在该 Scope 内更新不同的 Variable

完成的编译执行的具体过程如下图所示:

  1. Executor 为每一个block创建一个Scope,Block是可嵌套的,因此Scope也是可嵌套的。
  2. 创建所有Scope中的变量。
  3. 创建并执行所有operator。

(1)创建Executor

#使用CPU
cpu=fluid.CPUPlace()
#创建执行器
exe = fluid.Executor(cpu)

(2)运行Executor

正式训练之前,需先执行参数初始化。

 #参数初始化exe.run(fluid.default_startup_program())

由于传入数据和传出数据存在多列,因此Paddle通过feed映射定义数据的传输数据,通过fetch_list取出结果。

x = numpy.random.random(size=(10, 1)).astype('float32')
outs = exe.run(feed={'X': x},fetch_list=[loss.name])

7.命令式编程

从编程范式上说,飞浆兼容支持声明式编程(静态图)和命令式编程(动态图),在飞浆的设计中,把一个神经网络定义成一段类似程序的描述,也就是用户在写程序的过程中,就定义了模型表达及计算。在静态图的控制流实现方面,飞浆用了自己的控制流OP,这使得在飞浆中的定义的program即一个网络模型,可以有一个内部表达,是可以全局优化编译执行的,同时也提供了Python原生控制流,并通过解释方式执行,这就是命令式编程模式。(是不是没看懂,我也没)

声明式编程:先编译后执行,用户需预先定义完整的网络结构,再对网络结构进行编译优化,然后执行获得结果。

命令式编程:解析式执行,每写一行代码,即可获得计算结果

声明式编程存在的问题:

  • 采用先编译后执行的方式,组网阶段和执行阶段割裂,导致调试不方便。
  • 属于一种符号化的编程方式,要学习新的编程方式,有一定的入门门槛。
  • 网络结构固定,对于一些树结构的任务支持的不够好。

命令式编程的优势:

  • 代码运行完成后,可以立马获取结果,支持使用 IDE 断点调试功能,使得调试更方便。
  • 属于命令式的编程方式,与编写Python的方式类似,更容易上手。
  • 网络的结构在不同的层次中可以变化,使用更灵活。

两者之间的代码对比:

import numpy as np
import paddle.fluid as fluid
from paddle.fluid.dygraph.base import to_variablemain_program = fluid.Program()
startup_program = fluid.Program()
with fluid.program_guard(main_program=main_program, startup_program=startup_program):# 利用np.ones函数构造出[2*2]的二维数组,值为1data = np.ones([2, 2], np.float32)# 静态图模式下,使用layers.data构建占位符用于数据输入x = fluid.layers.data(name='x', shape=[2], dtype='float32')print('In static mode, after calling layers.data, x = ', x)# 静态图模式下,对Variable类型的数据执行x=x+10操作x += 10# 在静态图模式下,需要用户显示指定运行设备# 此处调用fluid.CPUPlace() API来指定在CPU设备上运行程序place = fluid.CPUPlace()# 创建“执行器”,并用place参数指明需要在何种设备上运行exe = fluid.Executor(place=place)# 初始化操作,包括为所有变量分配空间等,比如上面的‘x’,在下面这行代码执行后才会被分配实际的内存空间exe.run(fluid.default_startup_program())# 使用执行器“执行”已经记录的所有操作,在本例中即执行layers.data、x += 10操作# 在调用执行器的run接口时,可以通过fetch_list参数来指定要获取哪些变量的计算结果,这里我们要获取‘x += 10’执行完成后‘x’的结果;# 同时也可以通过feed参数来传入数据,这里我们将data数据传递给‘fluid.layers.data’指定的‘x’。data_after_run = exe.run(fetch_list=[x], feed={'x': data})# 此时我们打印执行器返回的结果,可以看到“执行”后,Tensor中的数据已经被赋值并进行了运算,每个元素的值都是11print('In static mode, data after run:', data_after_run)# 开启动态图模式
with fluid.dygraph.guard():# 动态图模式下,将numpy的ndarray类型的数据转换为Variable类型x = fluid.dygraph.to_variable(data)print('In DyGraph mode, after calling dygraph.to_variable, x = ', x)# 动态图模式下,对Variable类型的数据执行x=x+10操作x += 10# 动态图模式下,调用Variable的numpy函数将Variable类型的数据转换为numpy的ndarray类型的数据print('In DyGraph mode, data after run:', x.numpy())

静态图结果输出:

In static mode, after calling layers.data, x =  name: "x"
type {type: LOD_TENSORlod_tensor {tensor {data_type: FP32dims: -1dims: 2}lod_level: 0}
}
persistable: falseIn static mode, data after run: [array([[11., 11.],[11., 11.]], dtype=float32)]

动态图结果输出:

In DyGraph mode, after calling dygraph.to_variable, x =  name generated_var_0, dtype: VarType.FP32 shape: [2, 2] 	lod: {}dim: 2, 2layout: NCHWdtype: floatdata: [1 1 1 1]In DyGraph mode, data after run: [[11. 11.][11. 11.]]

8.编程指南

展示一个完成的编程实例。

# 加载库
import paddle.fluid as fluid
import numpy# 定义输入数据
train_data=numpy.array([[1.0],[2.0],[3.0],[4.0]]).astype('float32')
y_true = numpy.array([[2.0],[4.0],[6.0],[8.0]]).astype('float32')# 组建网络
x = fluid.data(name="x",shape=[None, 1],dtype='float32')
y = fluid.data(name="y",shape=[None, 1],dtype='float32')
y_predict = fluid.layers.fc(input=x,size=1,act=None)# 定义损失函数
cost = fluid.layers.square_error_cost(input=y_predict,label=y)
avg_cost = fluid.layers.mean(cost)# 选择优化方法
sgd_optimizer = fluid.optimizer.SGD(learning_rate=0.01)
sgd_optimizer.minimize(avg_cost)# 网络参数初始化
cpu = fluid.CPUPlace()
exe = fluid.Executor(cpu)
exe.run(fluid.default_startup_program())# 开始训练,迭代100次
for i in range(100):outs = exe.run(feed={'x':train_data, 'y':y_true},fetch_list=[y_predict, avg_cost])# 输出训练结果
print (outs)

输出结果为:

[array([[2.2075021], [4.1005487], [5.9935956], [7.8866425]], dtype=float32), array([0.01651453], dtype=float32)]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/466756.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

回答一个微信好友的创业问题

ps:很喜欢这种有烟火气息的照片— — 提问:我最近要创业,打算跟一个朋友合伙,但是我朋友不会技术,所以他只投入钱,也不会参与公司的管理。我们启动资金是10万,他打算投入7万,想占股65%。因为没有…

百度深度学习初级认证——已过

开头先放图,百度深度学习初级工程师认证已通过,记录一下备战和考试细节!!! 1.报考 当时是通过百度的AI Studio看到深度学习的认证了,价格是800,然后阴差阳错从百度技术学院的链接看到深度学习…

哦,这是桶排序

漫画:什么是桶排序?要了解桶排序之前,可以先看看上面小灰的那篇文章,我觉得是比较不错的。桶排序也可以理解为分类排序,把不同的数据归类,归类之后再重新排序,每个桶里面的内容就是一类数据&…

如何防御光缆窃听

很多年前,人们就认识到采用铜缆传输信息很容易通过私搭电缆的方式被窃取。对于一个网络和安全管理人员来说,要么对铜缆采用更严格的安全防护措施,要么就使用光缆。因为很多人都认为光纤可以很好地防止***通过窃听手段截获网络数据。但是实际上…

Linux字符设备驱动实例

globalmem看 linux 设备驱动开发详解时,字符设备驱动一章,写的测试代码和应用程序,加上自己的操作,对初学者我觉得非常有帮助。写这篇文章的原因是因为我看了我之前发表的文章,还没有写过字符设备相关的,至…

8-[函数]-嵌套函数,匿名函数,高阶函数

1.嵌套函数 (1)多层函数套用 name "Alex"def change_name():name "Alex2"def change_name2():name "Alex3"print("第3层打印", name)change_name2() # 调用内层函数print("第2层打印", name)chan…

c语言画谢宾斯基三角形

谢宾斯基三角形是一个有意思的图形,(英语:Sierpinski triangle)是一种分形,由波兰数学家谢尔宾斯基在1915年提出,它是一种典型的自相似集。先画一个三角形,然后呢,取三角形的中点,组…

进程间的通信——无名管道

进程间的通信——无名管道 宗旨:技术的学习是有限的,分享的精神是无限的。 一、进程间的通信 (1)同主机进程间数据交互机制:无名管道(PIPE),有名管道(FIFO)…

sklearn官网-多分类问题

sklearn实战-乳腺癌细胞数据挖掘(博主亲自录制视频) https://study.163.com/course/introduction.htm?courseId1005269003&utm_campaigncommission&utm_sourcecp-400000000398149&utm_mediumshare 1.12.6. Multioutput classification Mult…

剖析C语言是如何画出这样的三角形的

哈哈,就是喜欢这些有意思的C语言上篇文章是这样写的c语言画谢宾斯基三角形那篇文章写的有点不直接,然后再查了下资料,看到了下面这些,我觉得解释更加好,这里主要是运用了光栅法,至于光栅法,可以…

NILMTK在Windows下的安装教程

近期,要进行负荷辨识,找到NILMTK安装包,特意将过程记录下来。 (1)Windows安装 本机已安装了Anaconda,环境是Python3,NILMTK包的项目地址为:https://github.com/nilmtk/nilm_metada…

修改cmdline 把内存改成512MB

#添加cmdline的方式— — 在BoardConfig.mk中修改device/mediateksample/aiv8167sm3_bsp/BoardConfig.mk BOARD_KERNEL_CMDLINE bootopt64S3,32N2,32N2 mem512MB— — 在dts 里面修改kernel-4.4\arch\arm\boot\dts\*.dts / {model "Atmel AT91SAM9M10G45-EK";compa…

NILMTK——经典数据集REDD介绍和使用

配置了NILMTK包的环境之后,想找数据测试一下,在NILMTK官网的API Docs里边发现dataset_converters模块中有内置的数据集处理函数,如图: 将数据转换成HDF文件,这些数据都是比较优秀的,其中,常用的…

上拉电阻的作用

1、介绍使用微控制器(MCUs) 或任何数字逻辑设备时,上拉电阻器非常常见。本教程将解释何时何地使用上拉电阻器,然后我们将做一个简单的计算,以显示为什么上拉很重要。2、上拉电阻是什么?假设您有一个将一个引…

NILMTK——深扒组合优化(CO)和FHMM细节

前面的博客讲了具体实现,现在深究算法代码实现细节!!! 1.CO (1)关于train 从以下代码可知,CO首先是对各个电器的功率数据做了train,为了了解其原生实现对代码进行了深究: classifiers {CO:…

深圳工资指导价出炉!最高月薪6万!你拖同行后腿了吗?

2020 年只剩下不到一个月了,年初立的 flag 有没有实现呢?我想多数人面临的尴尬是升职、加薪、赢取白富美、走上人生巅峰可能一步都没实现~对比周围混得风生水起的小伙伴感觉自己也不差啥啊,怎么就莫名其妙被甩了八条街?想一探究竟…

NILMTK——因子隐马尔可夫之隐马尔可夫

因子隐马尔可夫(FHMM)由Ghahramani在1997年提出,是一种多链隐马尔可夫模型,适合动态过程时间序列的建模,并具有强大的时序模型的分类能力,特别适合非平稳、再现性差的序列的分析。 1. 马尔可夫链 随机过程的研究对象是随时间演变…

CodeForces 903D Almost Difference

题目描述 Lets denote a function You are given an array aa consisting of nn integers. You have to calculate the sum of d(a_{i},a_{j})d(ai​,aj​) over all pairs (i,j)(i,j) such that 1<i<j<n1<i<j<n . 输入输出格式 输入格式&#xff1a; The fi…

据悉,深圳某工程师沦为C语言笔试枪手

事情是这样的&#xff0c;昨晚晚上&#xff0c;有个网友发消息给我&#xff0c;说他有几道C语言笔试题不会写&#xff0c;所以&#xff0c;就出现了解题的这一幕。文章中&#xff0c;我只讲解了一部分&#xff0c;有一些题目觉得没必要讲&#xff0c;然后我在pdf上做了注释&…

大数据工具使用——安装Hadoop(多台服务器)和Hive、Hbase

1.配置环境版本 资料上传百度云&#xff0c;自取&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1evVp5Zk0_X7VdjKlHGkYCw 提取码&#xff1a;ypti 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 &#xff08;之前安装的是apache版本的Hadoop2.6.4,在启…