PaddlePaddle概述

PaddlePaddle简介

  • PaddlePaddle(Parallel Distributed Deep Learning,中文名飞桨)是百度公司推出的开源、易学习、易使用的分布式深度学习平台

PaddlePaddle优点

  • 易用性。语法简洁,API的设计干净清晰
  • 丰富的模型库。借助于其丰富的模型库,可以非常容易的复现一些经典方法
  • 全中文说明文档。首家完整支持中文文档的深度学习平台
  • 运行速度快。充分利用 GPU 集群的性能,为分布式环境的并行计算进行加速

PaddlePaddle缺点

  • 教材少
  • 学习难度大、曲线陡峭

行业应用

QQ截图20230726133503.jpg

学习资源

体系结构

体系结构

总体架构

QQ截图20230726134206.jpg

编译与执行过程

  1. 用户编写的python程序通过调用 Paddle 提供的算子,向Program中添加变量(Tensor)以及对变量的操作(Operators 或者 Layers)
  2. 原始Program在框架内部转换为中间描述语言:ProgramDesc
  3. Transpiler 接受一段 ProgramDesc ,输出一段变化后的ProgramDesc,作为后端Executor 最终需要执行的 Program
  4. 执行 ProgramDesc 中定义的 Operator(可以类比为程序语言中的指令),在执行过程中会为 Operator 创建所需的输入输出并进行管理

QQ截图20230726134247.jpg

三个重要术语

  • Fluid:定义程序执行流程
  • Program:对用户来说一个完整的程序
  • Executor:执行器,执行程序

快速开始

# helloworld示例
import paddle.fluid as fluid

# 创建两个类型为int64, 形状为1*1张量
x = fluid.layers.fill_constant(shape=[1], dtype="int64", value=5)
y = fluid.layers.fill_constant(shape=[1], dtype="int64", value=1)
z = x + y # z只是一个对象,没有run,所以没有值

# 创建执行器
place = fluid.CPUPlace() # 指定在CPU上执行
exe = fluid.Executor(place) # 创建执行器
result = exe.run(fluid.default_main_program(),fetch_list=[z]) #返回哪个结果
print(result) # result为多维张量

基本概念与操作

基本概念

张量

  • 张量(Tensor): 多维数组或向量,同其它主流深度学习框架一样,PaddlePaddle使用张量来承载数据

QQ截图20230726134754.jpg

  • 张量示例:灰度图像为二维张量(矩阵),彩色图像为三维张量

QQ截图20230726134818.jpg

LoDTensor

  • LoD(Level-of-Detail) Tensor是Paddle的高级特性,是对Tensor的一种扩充。LoDTensor通过牺牲灵活性来提升训练的效率。
  • LoDTensor用来处理变长数据信息,将长度不一致的维度拼接为一个大的维度,并引入了一个索引数据结构(LoD)来将张量分割成序列。
  • 假设一个mini-batch中有3个句子,每个句子中分别包含3个、1个和2个单词,我们可以用(3+1+2)xD维Tensor 加上一些索引信息来表示这个mini-batch:

QQ截图20230726134916.jpg

  • 假设存在一个mini-batch中包含3个句子、1个句子和2个句子的文章,每个句子都由不同数量的单词组成,则这个mini-batch的可以表示为2-Level的LoDTensor:

QQ截图20230726134947.jpg

Layer

  • 表示一个独立的计算逻辑,通常包含一个或多个operator(操作),如layers.relu表示ReLU计算;layers.pool2d表示pool操作。Layer的输入和输出为Variable。

Variable

  • 表示一个变量,在paddle中,Variable 基本等价于Tensor。Variable进入Layer计算,然后Layer返回Variable。创建变量方式:

QQ截图20230726135027.jpg

  • Paddle 中存在三种 Variable:
  1. 模型中的可学习参数:包括网络权重、偏置,生存期和整个训练任务一样长。通过 fluid.layers.create_parameter 来创建可学习参数
  2. 占位 Variable:Paddle 中使用 fluid.data 来接收输入数据,fluid.data 需要提供输入 Tensor 的形状信息,当遇到无法确定的维度时,相应维度指定为 None
  3. 常量 Variable:通过 fluid.layers.fill_constant 来实现常量Variable

Program

  • Program包含Variable定义的多个变量和Layer定义的多个计算,是一套完整的计算逻辑。从用户角度来看,Program是顺序、完整执行的。program 的作用是存储网络结构,但不存储参数

QQ截图20230726135349.jpg

  • 用户完成网络定义后,一段 Paddle 程序中通常存在2 个Program
  1. fluid.default_startup_program:定义了模型参数初始化、优化器参数初始化、reader初始化等各种操作。该program可以由框架自动生成,使用时无需显式地创建
  2. fluid.default_main_program :定义了神经网络模型,前向反向计算,以及模型参数更新、优化器参数更新等各种操作

Scope

  • scope 在 paddle 里可以看作变量空间,存储fluid创建的变量。变量存储于unordered_map 数据结构中,该结构类似于python中的dict,键是变量的名字,值是变量的指针。
  • 一 个 p a d d l e 程 序 有 一 个 默 认的全局s c o pe (可以通过fluid.global_scope() 获取)。如果没有主动创建scope并且通过fluid.scope_guard() 替换当前 scope,那么所有参数都在全局scope中。参数创建的时机不是在组网时,而是在executor.run() 执行时。
  • program 和 scope 配合,才能表达完整模型(模型=网络结构+参数)

Executor

  • Executor用来接收并执行Program,会一次执行Program中定义的所有计算。通过feed来传入参数,通过fetch_list来获取执行结果。
place = fluid.CPUPlace()  # 指定在CPU上执行
exe = fluid.Executor(place)  # 创建执行器
outs = exe.run(fluid.default_main_program(),  # 默认程序上执行
               feed=params,  # 喂入参数
               fetch_list=[result])  # 获取结果

Place

  • PaddlePaddle可以运行在Intel CPU,Nvidia GPU,ARMCPU和更多嵌入式设备上,可以通过Place用来指定执行的设备(CPU或GPU)。
place = fluid.CPUPlace()  # 指定在CPU上执行
place = fluid.CUDAPlace()  # 指定在GPU上执行

Optimizer

  • 优化器,用于优化网络,一般用来对损失函数做梯度下降优化,从而求得最小损失值

程序执行步骤

QQ截图20230726135917.jpg

执行两个张量计算

import paddle.fluid as fluid
import numpy

# 创建x, y两个2行3列,类型为float32的变量(张量)
x = fluid.layers.data(name="x", shape=[2, 3], dtype="float32")
y = fluid.layers.data(name="y", shape=[2, 3], dtype="float32")

x_add_y = fluid.layers.elementwise_add(x, y)  # 两个张量按元素相加
x_mul_y = fluid.layers.elementwise_mul(x, y)  # 两个张量按元素相乘

place = fluid.CPUPlace()  # 指定在CPU上执行
exe = fluid.Executor(place)  # 创建执行器
exe.run(fluid.default_startup_program())  # 初始化网络

a = numpy.array([[1, 2, 3],
                 [4, 5, 6]])  # 输入x, 并转换为数组
b = numpy.array([[1, 1, 1],
                 [2, 2, 2]])  # 输入y, 并转换为数组

params = {"x": a, "y": b}
outs = exe.run(fluid.default_main_program(),  # 默认程序上执行
               feed=params,  # 喂入参数
               fetch_list=[x_add_y, x_mul_y])  # 获取结果
for i in outs:
    print(i)

编写简单线性回归

  • 给出输入样本
  • 给出实际输出样本
  • 找出 y = wx+b 公式中的 w 和 b
  • 定义输入数据、实际输出结果
  • 将数据送入神经网络进行训练(全连接网络,即分类器)
  • 根据实际输出、预测输出之间的损失值,进行梯度下降,直到收敛到极小值为止
  • 神经网络,选择fluid.layers.fc(),该函数在神经网络中建立一个全连接层。接收多个输入,为每个输入分配一个权重w, 并维护一个偏置值b;预测时产生一个输出
  • 损失函数:回归问题,选择均方差fluid.layers.square_error_cost()fluid.layers.mean()作为损失函数
  • 优化器:随机梯度下降优化器fluild.SGD,做梯度下降计算
# paddle实现线性回归
import paddle
import paddle.fluid as fluid
import numpy as np
import matplotlib.pyplot as plt

# 1. 准备数据
train_data = np.array([[0.5],
                       [0.6],
                       [0.8],
                       [1.1],
                       [1.4]]).astype('float32')
y_true = np.array([[5.0],
                   [5.5],
                   [6.0],
                   [6.8],
                   [6.8]]).astype('float32')

# 2. 定义模型,损失函数,优化器
x = fluid.layers.data(name='x',
                      shape=[1], # 一维
                      dtype='float32') # 类型
y = fluid.layers.data(name='y',
                      shape=[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) # 均方误差

# 优化器
optimzer = fluid.optimizer.SGD(learning_rate=0.01) # 学习率
optimzer.minimize(avg_cost) # 指定优化的目标函数

# 3. 训练
# 执行器
place = fluid.CPUPlace() # CPU
exe = fluid.Executor(place) # 执行器
exe.run(fluid.default_startup_program()) # 初始化

costs = [] # 存放损失值
iters = [] # 存放迭代次数
params = {'x':train_data,'y':y_true}# 参数字典

for i in range(50):
    out = exe.run(
        program=fluid.default_main_program(),
        feed=params, # 参数字典
        fetch_list=[y_predict,avg_cost]) # 要返回的值
    iters.append(i) # 存放迭代次数
    costs.append(out[1][0]) # 记录损失值
    print("i: %d,cost: %f"%(i,out[1][0]))

# 4. 可视化
# 线性模型可视化
tmp = np.random.rand(10, 1)  # 生成10行1列的均匀随机数组
tmp = tmp * 2  # 范围放大到0~2之间
tmp.sort(axis=0)  # 排序
print("10个x:", tmp)
x_test = np.array(tmp).astype("float32")
params = {"x": x_test, "y": x_test}  # y参数不参加计算,只需传一个参数避免报错
y_out = exe.run(feed=params,
                fetch_list=[y_predict.name])  # 预测
y_test = y_out[0]
print("预测的10个y值:", y_test)

# 损失函数可视化
plt.figure("Trainging")
plt.title("Training Cost", fontsize=24)
plt.xlabel("Iter", fontsize=14)
plt.ylabel("Cost", fontsize=14)
plt.plot(iters, costs, color="red", label="Training Cost")  # 绘制损失函数曲线
plt.grid()  # 绘制网格线
plt.savefig("train.png")  # 保存图片

# 线性模型可视化
plt.figure("Inference")
plt.title("Linear Regression", fontsize=24)
plt.plot(x_test, y_test, color="red", label="inference")  # 绘制模型线条
plt.scatter(train_data, y_true)  # 原始样本散点图

plt.legend()
plt.grid()  # 绘制网格线
plt.savefig("infer.png")  # 保存图片
plt.show()  # 显示图片

fluid API结构图

QQ截图20230726140815.jpg

数据准备

数据准备

深度学习数据读取要求

  • 从文件读入数据。因为程序无法保存大量数据,数据一般保存到文件中,所以需要单独的数据读取操作
  • 批量快速读入。深度学习样本数据量较大,需要快速、高效读取(批量读取模式)
  • 随机读入。为了提高模型泛化能力,有时需要随机读取数据(随机读取模式)

使用reader

import paddle

def reader_creator(filepath):
    def reader():
        with open(filepath,"r") as f:
            lines = f.readlines()
            for line in lines:
                yield line
    return reader

reader = reader_creator('test.txt')
#从上一个reader中以随机方式读取数据
shuffle = paddle.reader.shuffle(reader, # 原读取器
                                10) # 打乱间隔
#从上一个随机读取器中,分批次读取数据
batch = paddle.batch(shuffle,
                     3) # 每个批次3个
for data in batch():# 从原始顺序读取器读取
    print(data,end='')

实现多元回归

  • 数据量:506笔
  • 特征数量:13个(见右图)
  • 标签:价格中位数
  • 任务:根据样本数据,预测房价中位数(回归问题)

QQ截图20230726142830.jpg

思路

QQ截图20230726142925.jpg

波士顿房价预测

import paddle
import numpy as np
import os
import paddle.fluid as fluid
import matplotlib.pyplot as plt

# 1.准备数据
BUF_SIZE = 500  # 样本随机处理笔数
BATCH_SIZE = 20  # 批次大小

# boston_reader
boston_reader = paddle.dataset.uci_housing.train()  # 训练集读取器
# 随机读取器
random_reader = paddle.reader.shuffle(boston_reader,
                                      buf_size=BUF_SIZE)  # 间隔多少个样本打乱一次
# 批量读取器
train_reader = paddle.batch(random_reader,
                            batch_size=BATCH_SIZE)  # 每个批次有多少个样本
# 打印查看
# for i in train_reader():
#     print(i)
#     break

# 2.定义模型损失函数优化器
# 占位符
x = fluid.layers.data(name='x',  # 名称
                      shape=[13],  # 形状 一维,13个值
                      dtype='float32')  # 数据类型
y = fluid.layers.data(name='y',  # 名称
                      shape=[1],  # 形状 一维,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)  # 均方误差

# 优化器
optimzer = fluid.optimizer.SGD(learning_rate=0.001)  # 学习率
optimzer.minimize(avg_cost)  # 指定优化的目标函数

# 3.训练保存模型
# 执行器
place = fluid.CPUPlace()  # CPU
exe = fluid.Executor(place)  # 执行器
exe.run(fluid.default_startup_program())  # 初始化
# feeder
feeder = fluid.DataFeeder(feed_list=[x, y],  # 喂入哪些参数
                          place=place)  # 在哪个设备上运行

iter = 0
iters = []  # 存放迭代次数
train_costs = []  # 存放损失值

for epoch in range(220):  # 外层循环控制轮次
    i = 0
    for data in train_reader():  # 内层循环控制批次
        i += 1
        c = exe.run(
            program=fluid.default_main_program(),
            feed=feeder.feed(data),  # feed()方法返回字典
            fetch_list=[avg_cost])  # 要返回的值
        if i % 10 == 0:
            print("epoch: %d,cost: %f" % (epoch, c[0][0]))
        iter = iter + BATCH_SIZE
        iters.append(iter)  # 存放迭代次数
        train_costs.append(c[0][0])  # 记录损失值

# 训练过程可视化
plt.figure("Training Cost")
plt.title("Training Cost", fontsize=24)
plt.xlabel("iter", fontsize=14)
plt.ylabel("cost", fontsize=14)
plt.plot(iters, train_costs, color="red", label="Training Cost")
plt.grid()
plt.savefig("train.png")

# 4.加载推理模型

## 测试集reader
infer_reader = paddle.batch(
    paddle.dataset.uci_housing.test(),  # 测试集
    batch_size=200)  # 只有102笔数据,实际是一次性读取
test_data = next(infer_reader())  # 读取一批数据

# 取出输入标签部分
test_x = np.array([d[0] for d in test_data]).astype('float32')
test_y = np.array([d[1] for d in test_data]).astype('float32')

# 参数字典
params = {'x': test_x,'y': test_y}
# 执行推理
results  = exe.run(
    feed=params,  # 喂入的参数
    fetch_list=[y_predict])  # 要返回的值
# print(results)
#
infer_result = []  # 预测值列表
ground_truths = []  # 真实值列表

# 取出每一个样本存入列表
for i in results[0]:
    infer_result.append(i)
for i in test_y:
    ground_truths.append(i)

## 绘图
plt.figure("Infer Result")
plt.title("Infer Result")
plt.xlabel("Ground Truth")
plt.ylabel("Infer Result")
x = np.arange(1, 30)  # 产生一个均匀数组,作为斜线x坐标
y = x  # 斜线y坐标
plt.plot(x, y)  # 绘制y=x斜线
plt.scatter(ground_truths, infer_result,
            color="green", label="Test")
plt.grid()  # 网格线
plt.legend()  # 图例
plt.savefig("predict.png")  # 图片保存到文件

plt.show()

执行结果

QQ截图20230726142943.jpg

模型保存与加载

import paddle
import numpy as np
import os
import paddle.fluid as fluid
import matplotlib.pyplot as plt
# 1.准备数据
BUF_SIZE = 500 # 样本随机处理笔数
BATCH_SIZE = 20 # 批次大小

#boston_reader
boston_reader = paddle.dataset.uci_housing.train() # 训练集读取器
# 随机读取器
random_reader = paddle.reader.shuffle(boston_reader,
                                      buf_size=BUF_SIZE)# 间隔多少个样本打乱一次
# 批量读取器
train_reader = paddle.batch(random_reader,
                     batch_size=BATCH_SIZE) # 每个批次有多少个样本
# 打印查看
# for i in train_reader():
#     print(i)
#     break

# 2.定义模型损失函数优化器
# 占位符
x = fluid.layers.data(name='x', # 名称
                      shape=[13], # 形状 一维,13个值
                      dtype='float32') # 数据类型
y = fluid.layers.data(name='y', # 名称
                      shape=[1], # 形状 一维,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) # 均方误差

# 优化器
optimzer = fluid.optimizer.SGD(learning_rate=0.001) # 学习率
optimzer.minimize(avg_cost) # 指定优化的目标函数


# 3.训练保存模型
# 执行器
place = fluid.CPUPlace() # CPU
exe = fluid.Executor(place) # 执行器
exe.run(fluid.default_startup_program()) # 初始化
# feeder
feeder = fluid.DataFeeder(feed_list=[x,y], # 喂入哪些参数
                          place=place)# 在哪个设备上运行


iter = 0
iters = [] # 存放迭代次数
train_costs = [] # 存放损失值


for epoch in range(10): # 外层循环控制轮次
    i = 0
    for data in train_reader(): # 内层循环控制批次
        i += 1
        c = exe.run(
            program=fluid.default_main_program(),
            feed=feeder.feed(data), # feed()方法返回字典
            fetch_list=[avg_cost]) # 要返回的值
        if i % 10 == 0:
            print("epoch: %d,cost: %f"%(epoch,c[0][0]))
        iter = iter + BATCH_SIZE
        iters.append(iter) # 存放迭代次数
        train_costs.append(c[0][0]) # 记录损失值

# 保存模型
model_save_dir = 'model/' # 模型保存目录
if not os.path.exists(model_save_dir):
    os.mkdir(model_save_dir) # 不存在就创建

# 保存用于可以增量训练的模型
# fluid.io.save_persistables()
# 保存用于推理的模型
fluid.io.save_inference_model(model_save_dir, # 模型保存目录
                              ['x'], # 模型预测时需要喂入的参数名称
                              [y_predict], # 模型预测结果
                              exe) # 执行器

# 训练过程可视化
plt.figure("Training Cost")
plt.title("Training Cost", fontsize=24)
plt.xlabel("iter", fontsize=14)
plt.ylabel("cost", fontsize=14)
plt.plot(iters, train_costs, color="red", label="Training Cost")
plt.grid()
plt.savefig("train.png")

# 4.加载推理模型
infer_exe = fluid.Executor(place) # 用于推理的执行器
# 加载模型
# infer_prog 专门用于推理的program
# feed_vars 执行推理时需要传入的参数列表
# fetch_targets 预测结果从这里获取

# model_save_dir 保存路径
# infer_exe 执行器

infer_prog,feed_vars,fetch_targets = fluid.io.load_inference_model(model_save_dir,infer_exe)

## 测试集reader
infer_reader = paddle.batch(
    paddle.dataset.uci_housing.test(),#测试集
    batch_size=200) # 只有102笔数据,实际是一次性读取
test_data = next(infer_reader())#读取一批数据

# 取出输入标签部分
test_x = np.array([d[0] for d in test_data]).astype('float32')
test_y = np.array([d[1] for d in test_data]).astype('float32')

# 参数字典
params = {feed_vars[0]:test_x}
# 执行推理
results = infer_exe.run(
    program=infer_prog,
    feed=params,  # 喂入的参数
    fetch_list=[fetch_targets])  # 要返回的值
# print(results)
#
infer_result = [] # 预测值列表
ground_truths = [] # 真实值列表

# 取出每一个样本存入列表
for i in results[0]:
    infer_result.append(i)
for i in test_y:
    ground_truths.append(i)

## 绘图
plt.figure("Infer Result")
plt.title("Infer Result")
plt.xlabel("Ground Truth")
plt.ylabel("Infer Result")
x = np.arange(1, 30) # 产生一个均匀数组,作为斜线x坐标
y = x # 斜线y坐标
plt.plot(x, y) # 绘制y=x斜线
plt.scatter(ground_truths, infer_result,
            color="green", label="Test")
plt.grid() # 网格线
plt.legend() # 图例
plt.savefig("predict.png")#图片保存到文件

plt.show()