[笔记]90分钟快速入门PaddleFluid

[笔记]90分钟快速入门PaddleFluid

课程出处,请点击这里


文章目录

  • [笔记]90分钟快速入门PaddleFluid
    • 安装指南
    • 编程入门
      • Paddle Fluid整体架构
      • Paddle Fluid使用基本概念
      • 基于Paddle Fluid编写手写数字识别模型
    • 如何准备数据
      • 数据准备基础知识
      • MNIST数据集简介
      • 使用Paddle Fluid准备数据的步骤
    • 训练神经网络
      • 使用Paddle Fluid进行单机训练
        • 总结
      • 使用Paddle Fluid进行多机训练
        • 分布式训练概述
        • parameter server模式介绍
        • Collective模式介绍
      • 如何保存模型与恢复训练
        • FAQ
    • 模型评估与调试
      • 评估指标
      • VisualDL可视化与分析
      • 模型评估
    • 服务器端预测部署
      • 预测库介绍
      • 预测库编译
    • PaddlePaddle模型库介绍
      • 模型库整体介绍
      • CV方向特色模型
      • NLP方向特色模型
      • 模型库使用示例
        • 手写数字识别
        • Transformer
      • 评估翻译结果

安装指南

  • Paddle Fluid的安装方案简介
    • 安装配置好的Paddle Fluid
      • 使用pip安装
      • 使用配置好的Paddle Fluid的docker容器
    • 按个人需求编译Paddle Fluid
      • 基于封装好环境的docker容器内编译
      • 直接基于本地环境编译Paddle Fluid
  • 使用pip在Ubuntu 16.04下安装GPU版本的Paddle Fluid
    • 什么是pip,为什么要使用pip
      pip是一个现代的,通用的Python包管理工具,提供对Python包的查找、下载、安装、卸载功能
    • 使用pip安装需要注意的环境问题,以及如何查看
      使用pip安装是要注意的环境:
      • 操作系统
      • Python和pip版本
      • GPU相关
      • CPU对AVX的指令集支持
      • 使用的数学库支持
    • 使用pip安装对应版本的Paddle Fluid
    • 如何验证安装成功
  • 关于其他安装方式
  • 一些问题
  • 总结

编程入门

Paddle Fluid整体架构

[笔记]90分钟快速入门PaddleFluid_第1张图片

(Credit https://aistudio.baidu.com/aistudio/education/lessonvideo/530426)

Paddle Fluid使用基本概念

  • Place
    • Paddle可以运行在Intel CPU,Nvidia GPU,ARM CPU和更多嵌入式设备上。训练是可以通过Place来指定执行的设备。指定GPU实例:place=fluid.CUDAPlace(0)
    • CompiledProgram
      • Program可以转换成一个有向无环图,经过一系列的优化,生成一个执行的图
      • CompiledProgram.with_data_parallel,生成一个可以多卡并发的训练图
      • CompiledProgram.with_optimized_inference,生成一个优化后的预测图
      • 编译后的program可以通过Executor执行:Executor.run(compiled_program)

基于Paddle Fluid编写手写数字识别模型

import paddle
from paddle import fluid

# Add to input variables to program to accept data inputs.
images = fluid.layers.data(name='pixel', shape=[1,28,28], dtyp='float32')
label = fluid.layers.data(name='label', shape=[1], dtype='int64')

# constrct the model with input variables.
conv_pool_1 = fluid.nets.simple_img_conv_pool(
    input=images, filter_size=5, num_filters=20,
    pool_size=2, pool_stride=2, act='relu'
)
conv_pool_2 = fluid.nets.simple_img_conv_pool(
    input=conv_pool_1, filter_size=5, num_filters=50,
    pool_size=2, pool_stride=2, act='relu'
)

SIZE = 10
input_shape = conv_pool_2.shape
param_shape = [reduce(lambda a,b: a*b, input[1:], 1)] + [SIZE]
scale = (2.0 / (param_shape[0]**2*SIZE))**0.5

predict = fluid.layers.fc(
    input=conv_pool_2, size=SIZE, act='softmax',
    param_attr=fluid.param_attr.ParamAttr(
        initializer=fluid.initializer.Normallnitializer(
            loc=0.0, scale=scale
        )
    )
)

# Calculate cost and use optimize to minimize it.
cost = fluid.layers.cross_entropy(input=predict, label=label)
avg_cost = fluid.layers.mean(x=cost)
opt = fluid.optimizer.Adamptimizer(
    learning_rate=0.001, beta1=0.9, beta2=0.999
)
opt.minimize(avg_cost)

# read data in batches.
reader = paddle.dataset.minist.train()
batched_reader = paddle.batch(reader, batch_size=32)

# Create execute. Use fluid.CPUPlace() if gpu is not available.
place = fluid.CUDAPlace(0)
exe = fluid.Executor(place)
feeder = fluid.DataFeeder(feed_list=[images, label], place=place)

# initialize the model.
exe.run(fluid.default_startup_program())

# feed data and execute the train program. We see cost going down, Yeah.
for i, data in zip(xrange(10), batched_reader()):
    loss = exe.run(feed=feeder.feed(data), fetch_list=[avg_cost])
    print(loss)

如何准备数据

数据准备基础知识

  • Batch
    多个样本数据组成的一份训练(预测)数据称为一个batch,每个batchs
  • Epoch
    每次遍历全体数据集进行训练(预测)的过程称为一轮epoch
  • 数据增强是训练神经网络的有效手段,增强方式:
    • Shuffle
    • 随机剪裁(Random Crop)
    • 图像翻转(Flip)
    • 光照、色彩变换
    • 随机加噪

MNIST数据集简介

使用Paddle Fluid准备数据的步骤

  • step 1: 用户自定义数据读取reader

    • 用户可用过如下方式定义读取MNIST训练数据集的reader
      Reader是一个Python生成器(generator),每次通过yield返回一个样本数据,全体数据集遍历完毕后reader退出

      import numpy as np
      def mnist_reader(image_file, label_file):
          def __reader__():
              with open(image_file) as f:
                  f.seek(16)
                  images = np.reshape(np.fromfile(f, dtype='unit8'), [-1, 28, 28])
                  images = images / 255.0 * 2.0 - 1.0 # 图像灰度范围 [-1.0, 1.0]
      
              with open(label_file) as f:
                  f.seek(8)
                  labels = np.fromfile(f, dtype='unit8') # 标签数字范围 [0, 9]
      
              for idx in range(len(labels)):
                  yield images[idex, :], labels[idex]
      
          return __reader__
      
      # 训练用reader
      train_reader = mnist_reader('train-images.idx3-ubtye', 'train-labels.idx1-ubyte')
      
    • Paddle提供官方的MNIST Reader API,用于自动下载并读取MNIST训练数据集

      import paddle
      train_reader = paddle.dataset.mnist.train()
      
    • 用户可在reader中定义数据增强逻辑,例如:

      import paddle
      import paddle.fluid as fluid
      import numpy as np
      
      # 左右翻转
      def random_filpped_reader(reader):
          def __reader__():
              for image, label in reader():
                  if np.random.random_integers(2) ==1:
                      image = p.fliplr(image)
                  yield image, label
          return __reader__
      
      # 随机加噪
      def random_nosied_reader(reader):
          def __reader__():
              for image, label in reader():
                  image += np.random.normal((0, 0.01, size=image.shape))
                  yield image, label
          return __reader__
      
      train_reader = paddle.dataset.mnist.train()
      train_reader = random_nosied_reader(random_filpped_reader(train_reader))
      
    • 对reader返回数据进行随机shuffle

      shuffled_reader = paddle.reader.shuffle(train_reader, buf_size=64)
      
    • 调用paddle.batch组成一个batch的训练数据

      batch_reader = paddle.batch(shuffled_reader, batch_size=32)
      
  • step 2: 使用feed方法或py_reader接口送入训练数据

    • Feed方式
      用户先将reader数据用过data feeder转换为Paddle可识别的Tensor格式数据,传入执行器进行训练

      # 第一步:准备data layer
      # 定义data layer的数据类型、尺寸等
      images = fluid.layers.data(name='image', shape=[28,28], dtype='float32')
      labels = fluid.layers.data(name='label', shape=[1], dtype='int64')
      
      # 第二步:定义data feeder对象
      # GPU训练
      data_feeder = fluid.DataFeeder(feed_list=[images, labels], place=fluid.CUDAPlace(0))
      # CPU训练
      data_feeder = fluid.DataFeeder(feed_list=[images, labels], place=fluid.CPUPlace())
      
      # 第三步:训练网络
      exe.run(fluid.default_startup_program())
      for epoch_id in range(epoch_num):
          for batch_data in batch_reader():
              # DataFeeder.feed()将用户定义的batch_reader数据
              # 转换为paddle可识别的Tensor格式数据
              exe.run(feed=data_feeder.feed(batch_data), ...)
      
    • 使用py_reader接口送入训练数据

      • py_reader接口
        Python端开启线程往Paddle backend持续送入训练数据,模型训练线程与Python数据送入线程并行执行
      • 与feed方法不同
        使用py_reader接口数据读取和模型训练过程是异步进行的,效率更高,适合于告诉工业训练场景;而feed方式API更为简单,适用于入门和调试使用
      # 第一步:定义准备py_reader对象
      # 定义准备py_reader对象
      py_reader = fluid.layers.py_reader(
        capacity=8, # capacity为数据队列容量
        shapes=([28,28], [1]),
        dtypes=('float32', 'int64')
      )
      
      # 第二步:调用read_file读取数据
      # 调用read_file接口从py_reader中读取数据。
      # read_file返回值对应feed方式下的data layer
      images, labels = fluid.layers.read_file(py_reader)
      
      # 第三步:传入用户自定义reader
      # 将用户自定义reader传入py_reader
      py_reader.decorate_paddle_reader(batch_reader)
      
      # 第四步:训练网络
      exe.run(fluid.default_startup_program())
      for epoch_id in range(epoch_num):
          py_reader.start()
          while True:
              try:
                  exe.run(...)
              except fluid.core.EOFException:
                  # 每轮epoch结束后,C++ Backed跑出EOF异常,用户需捕获该异常,
                  # 并调用py_reader.reset()重置py_reader,以启动下轮epoch训练
                  py_reader.reset()
                  break
      

训练神经网络

使用Paddle Fluid进行单机训练

概括

  • 神经网络模型简介
    神经网络模型可分为两个部分:网络结构和模型参数。训练神经网络的本质是通过某种优化算法(如:随机梯度下降)找到一组模型参数,使得神经网络模型在训练集和测试集上得到Loss尽可能的小,准确率尽可能的高。

  • 配置模型结构

    import paddle
    import paddle.fluid as fluid
    
    # Define Input for model
    image = fluid.layers.data(name='pixel', shape=[1,28,28], dtype='float32')
    label = fluid.layers.data(name='label', shape=[1], dtype='int64')
    
    # Define the model
    conv1 = fluid.layers.conv2d(input=image, filter_size=5, num_filers=20)
    relu1 = fluid.layers.relu(conv1)
    pool1 = fluid.layers.pool2d(input=relu1, pool_size=2, pool_stride=2)
    conv2 = fluid.layers.conv2d(input=pool1, filter_size=5, num_filers=50)
    relu2 = fluid.layers.relu(conv2)
    pool2 = fluid.layers.pool2d(input=relu2, pool_size=2, pool_stride=2)
    
    predict = fluid.layers.fc(input=pool2, size=10, act='softmax')
    
    # Get the loss
    cost = fluid.layers.cross_entropy(input=predict, label=label)
    

    注意:Fluid提供了多种loss函数,比如cross_entropy, linear_chain_crd, bpr_oss, edit_distance, warpctc, dice_loss, mean_iou, log_loss, huber_loss等。不同人物可能需要选择不同的loss函数。

    对于cross_entropy有如下参数

    • soft_label=True:
      C o s t [ i ] = ∑ j − L a b e l [ i , j ] ∗ log ⁡ ( P r e d i c t [ i , j ] ) Cost[i] = \sum_{j}{-Label[i,j] * \log(Predict[i,j])} Cost[i]=jLabel[i,j]log(Predict[i,j])
    • soft_label=False:
      C o s t [ i ] = − log ⁡ ( P r e d i c t [ i , L a b e l [ i ] ] ) Cost[i] = -\log(Predict[i,Label[i]]) Cost[i]=log(Predict[i,Label[i]])
    avg_cost = fluid.layers.mean(x=cost)
    

    注意: Fluid提供了多种优化算法,比如:SGD, Momentum, Adagrad, Adam, Adamx, DecayedAdagrad, Ftrl, Adadelta, RMSProp, LarsMomentum等

    opt = fluid.optimizer.AdamOptimizer()
    

    m t = β 1 m t − 1 + ( 1 − β 1 ) ∂ L t ∂ W t v t = β 2 m t − 1 + ( 1 − β 2 ) ( ∂ L t ∂ W t ) 2 m ^ t = m t 1 − β 1 t v ^ t = v t 1 − β 2 t W ← W − η m ^ t v ^ t + ε m_t = \beta_1 m_{t-1} + (1-\beta_1) \frac{\partial L_t}{\partial W_t}\\ v_t = \beta_2 m_{t-1} + (1-\beta_2) (\frac{\partial L_t}{\partial W_t})^2\\ \hat{m}_t = \frac{m_t}{1 - \beta_{1}^{t}}\\ \hat{v}_t = \frac{v_t}{1 - \beta_{2}^{t}}\\ W \leftarrow W - \eta \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \varepsilon} mt=β1mt1+(1β1)WtLtvt=β2mt1+(1β2)(WtLt)2m^t=1β1tmtv^t=1β2tvtWWηv^t +εm^t

    # Add operations(backward operators) to minimize avg_loss
    opt.minimize(avg_cost)
    

    模型结构配置完成之后会得到两个fluid.Program

    • startup_program:模型参数的初始化操作
    • main_program:模型网络结构

    如果没有特殊制定,模型情况下使用的是全局fluid.Program,即:fluid.default_startup_prgram(), fluid.default_main_program()

  • 初始化模型参数

    • 配置完模型结构后,参数初始化操作会被写入fluid.default_startup_program()中,模型训练之前需要对参数进行初始化,且只需要执行一次初始化操作:

      place = fluid.CPUPlace() # fluid.CUDAPlace(0)
      exe = fluid.Executor(place)
      exe.run(fluid.default_startup_program)
      
    • 如果希望参数初始化在GPU上进行,将place设置为fluid.CUDAPlace(0)即可

    • 初始化后的参数存放在fluid.global_scope()中,用户可通过参数名从该scope中获取参数

  • 训练模型

    • 单机单卡

      • 单卡训练可以使用fluid.Executor()中的run()方法,运行fluid.Program即可
      • 在运行时,需要通过run(feed=…)参数传入训练数据,通过run(fetch=…)获取计算得到的结果数据
      train_reader = paddle.batch(
        paddle.dataset.mnist.train(), batch_size=128
      )
      
      for epoch_id in range(5):
          for batch_id, data in enumerate(train_reader()):
              img_data = np.array([x[0].reshape([1,28,28]) for x in data]).astype('float32')
              y_data = np.array([x[1] for x in data]).reshape([len(img_data), 1]).astype('int64')
              loss, acc = exe.run(
                fluid.default_main_program(),
                feed={'pixel':img_data, 'label':y_data},
                fetch_list=[avg_loss, batch_acc]
              )
              print (
                "epoch: %d, batch = %d, Loss = %f, Accuracy = %f"
                %(epoch_id, batch_id, loss, acc)
              )
      
    • 单机多卡

      • Fluid通过数据并行的方式进行多卡训练,即:多个节点上分别执行对应的Program
      • 使用多卡训练时,首先需要将构架的Program编译为数据并行模式的Program,然后通过执行器fluid.Executor进行模型训练
      • 用户可以通过设置环境变量来指定使用的GPU卡,如:export CUDA_VISIBLE_DEVICES=0,1,2,3
      from paddle.fluid import compiler
      
      compiled_program = compiler.CompliedProgram(fliud.default_main_program())
      compiled_program.with_data_parallel(loss_name=avg_cost.name)
      

      训练时,只需将fluid.default_main_program()改为compiled_program

      for epoch_id in range(5):
          for batch_id, data in enumerate(train_reader()):
              img_data = np.array([x[0].reshape([1,28,28]) for x in data]).astype('float32')
              y_data = np.array([x[1] for x in data]).reshape([len(img_data), 1]).astype('int64')
              loss, acc = exe.run(
                compiled_program,
                feed={'pixel':img_data, 'label':y_data},
                fetch_list=[avg_loss, batch_acc]
              )
              print (
                "epoch: %d, batch = %d, Loss = %f, Accuracy = %f"
                %(epoch_id, batch_id, loss, acc)
              )
      
    • CPU多线程

总结

  • 对于CPU训练,如何制定模型用几个线程训练呢?
    可以通过设置环境变量:export CPU_NUM=4

  • 如果需要多个模型,应该如何切换Program?

    place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace()
    exe = fluid.Executor(place)
    
    # Define Program1
    main_program_1 = fluid.Program()
    startup_program_1 = fluid.Program()
    
    with fluid.program_guard(main_program_1, startup_program_1):
      in_data_1, label_1, loss_1 = model1()
    
    exe.run(startup_program_1)
    for batch_id, data in enumerate(train_reader1()):
      img_data, y_data = ...
      loss = exe.run(
        main_program_1,
        feed={in_data_1.name: img_data, 'label':label},
        betch_list=[loss_1]
      )
      print ("batch = %d, Loss = %s" %(batch_id, loss))
    
    # Define Program2
    main_program_2 = fluid.Program()
    startup_program_2 = fluid.Program()
    
    with fluid.program_guard(main_program_2, startup_program_2):
      in_data_2, label_2, loss_2 = model2()
    
    exe.run(startup_program_2)
    for batch_id, data in enumerate(train_reader2()):
      img_data, y_data = ...
      loss = exe.run(
        main_program_2,
        feed={in_data_2.name: img_data, 'label':label},
        betch_list=[loss_2]
      )
      print ("batch = %d, Loss = %s" %(batch_id, loss))
    
  • 如何是多个Program之间共享参数?
    Paddle采用变量名区分不同变量,切变量名是根据unique_name模块中的计数器自动生成的,没生成一个变量名计数值加1。fluid.unique_name.guard()的作用是重置unique_name模块中的计数器,保证多次调用fluid.unique_name.guard()配置网络时对应变量的变量名相同,从而实现参数共享。

使用Paddle Fluid进行多机训练

分布式训练概述

  • 深度学习分布式训练有2种模式
    • 模型并行
      分布式系统中不同机器或设备(CPU/GPU)负责网络的不同部分,计算和参数都可能分配在不同的节点上
    • 数据并行
      分布式系统中不同机器都有一份完整的模型副本,每台机器分配到不同的数据,将所有机器的运算结果按某种方式合并
  • Paddle支持的分布式训练模式
    • Paddle目前主要支持数据并行的训练方式
    • Paddle的数据并行有2种模式
      • parameter server模式
        Paddle基于RPC通信实现了parameter server结构的数据并行训练
      • Collective模式
        Paddle基于Nvidia NCCL2实现了Collective模式的数据并行训练

parameter server模式介绍

有多个pserver进程和多个trainer进程

  • 每个pserver集成会保存一部分模型参数,接收从trainer发送的梯度并更新这些模型参数
  • 每个trainer进程会保存一份完整的模型,并使用一部分数据进行训练,然后向pserver发送梯度,最后从pserver拉取更新后的参数

Collective模式介绍

使用Collective(NCCL2)模式进行分布式训练,不需要启动pserver进程,每个trainer进程都保存一份完整的模型参数,完成计算梯度之后通过trainer之间的相互通信,Reduce梯度数据到所有节点,然后每个节点在各自完成参数更新
目前使用NCCL2模式支持同步训练方式,更适合模型体积较大,并需要使用同步训练和GPU训练,如果硬件设备支持RDMA和GPU Direct,可以达到很高的分布式训练性能
Collective(NCCL2)模式注意事项

  • 使用NCCL2模式分布式训练时,需要确保每个节点训练等量的数据,防止在最后一轮训练中任务不退出。通常由2种方式:
    • 随机采样一些数据,补全分配到较少数据的节点上【推荐】
    • 在python代码中,每个节点每个pass只训练固定batch数,如果这个节点数据较多,则不训练这些多出来的数据
  • 如果系统中有多个网络设备,需要手动指定NCCL2使用的设备,假设需要使用eth2为通信设备,需要设定如下环境变量:export NCCL_SOCKET_IFNAME=eth2
  • NCCL2提供了其他的开关环境变量,比如指定是否开启GPU Direct,是否使用RDMA等,详情参考ncclknobs

两种分布式模型的对比

特性 parameter server Collective(NCCL2)
支持的硬件设备 CPU/GPU 只支持GPU
速度 更快
模型并行 支持 不支持
同步训练 支持 支持
异步训练 支持 不支持
容错训练 支持 不支持

各种模式如何选择

模式 支持硬件 特点
parameter server同步模式 CPU/GPU 通用性强,支持所有场景,速度相对最慢
parameter server异步模式 CPU/GPU 速度比同步模式快,适合大规模(服务器较多)训练场景,模型收敛性需要重新调参
Collective(NCCL2)同步模式 GPU 适合GPU卡较多,模型相对较复杂的场景,可以提高GPU卡之间的通信速度,只支持同步训练
  • 分布式任务相关配置接口和参数配置

    • 分布式程序转换器(DistributeTranspiler)
      使用Paddle Fluid进行分布式寻来你非常简单,用户只需要配置单机训练所需要的网络支配,Paddle Fluid提供一个分布式程序转换器(目前叫DistributeTranspiler)来将单机网络转换为分布式训练网络

    单机训练

    # 1. 定义单机网络
    def model():
    	y = fluid.layers.data(name='y', shape=[1], dtype='float32')
    	x = fluid.layers.data(name='x', shape=[13], dtype='float32')
    	y_predict = fluid.layers.fc(input=x, size=1, act=None)
    
    	loss = fluid.layers.square_error_cost(input=y_predict, label=y)
    	avg_loss = fluid.layers.mean(loss)
    	opt = fluid.optimizer.SGD(learning_rate=0.001)
    	opt.minimize(avg_loss)
    
    # 2. 定义训练流程
    def train_loop():
    	train_reader = paddle.batch(
    	paddel.reader.shuffle(
      		paddle.dataset.uci_housing.train(), buf_size=500
    	),
     	batch_size=BATCH_SIZE
    )
    
    # 单机训练
    # 1. 定义网络
    model()
    # 2. 训练过程
    train_loop()
    

    分布式训练

    # 1. 定义单机网络
    model()
    
    # 2. 配置分布式程序转换器
    t = fluid.DistributeTranspiler()
    t.transpile(
    	trainer_id=trainer_id,
    	pservers=pserver_endpoints,
    	trainers=trainers
    )
    
    # 3. 根据自己角色启动parameter server
    if training_role == "PSERVER":
    	pserver_prog = t.get_pserver_program(current_endpoint)
    	startup_prog = t.get_startup_program(current_endpoint, pserver_prog)
    	exe.run(startup_prog)
    	exe.run(pserver_prog)
    
    # 4. 根据自己角色启动trainer
    elif training_role == "TRAINER":
    	trainer_prog = t.get_trainer_program()
    	exe.run(fluid.default_startup_program())
    	train_loop()
    

    parameter server模式配置参数

    参考:https://www.paddlepaddle.org.cn/documentation/docs/zh/api_cn/transpiler_cn/DistributeTranspiler_cn.html

    使用DistributeTranspiler可以把单机可以执行的程序快速转变成可以分布式执行的程序。在不同的服务器节点上,通过传给transpiler对应的参数,以获取当前节点需要执行的Program

    import paddle.fluid as fluid
    
    role = "PSERVER"
    trainer_id = 0 # get actual trainer id from cluster
    pserver_endpoints = "192.168.1.1:6170,192.168.1.2:6170"
    current_endpoint = "192.168.1.1:6170" # get acutal current endpoint
    trainers = 4
    t = fluid.DistributeTranspiler()
    t.transpile(trainer_id, trainers=trainers, pservers=pserver_endpoints)
    
    if role == "PSERVER":
      pserver_prog = t.get_pserver_programs(current_endpoint)
      pserver_startup = t.get_startup_program(
        current_endpoint,pserver_program
      )
      exe.run(pserver_startup)
      exe.run(pserver_prog)
    elif role == "TRAINER":
      train_loop(t.get_trainer_program())
    

    Collective(NCCL2)模式配置参数

    NCCL2模式的分布式训练,由于没有parameter server角色,是itrainer之间互相通信,使用时注意:

    • 配置fluid.DistributeTranspilerConfig中model=“nccl2”
    • 调用transpile时,trainers传入所有trainer节点的endpoint,并且传入参数current_endpoint
    • 初始化ParallelExecutor时传入num_trainers和trainer_id
import paddle.fluid as fluid

trainer_id = 0 # get actual trainer id from cluster
trainers = "192.168.1.1:6170,192.168.1.2:6170"
current_endpoint="192.168.1.1:6170"
config = fluid.DistributeTranspilerConfig()
config.mode = "nccl2"
t = fluid.DistributeTranspiler(config=config)
t.transpile(trainer_id, trainers=trainers, current_endpoint=current_endpoint)
exe = fluid.ParallelExecutor(
    use_cuda=True,
    loss_name='loss',
    num_trainers=len(trainers.split(',')),
    trainer_id=trainer_id
)

如何保存模型与恢复训练

概括

  • 保存与加载的模型变量
    Fluid中保存和加载的内容包括模型参数模型结构优化器中间状态等长期变量,梯度或其他临时变量目前不会保存和加载值

    Fluid中模型变量类型

    • 长期变量
      在真个训练过程中持续存在、不会因为一个迭代的结束而被销毁的变量,动态调节的全局学习率、模型参数、优化器的中间状态等都属于长期变量。在PaddlePaddle Fluid中,长期变量通过将fluid.Variable()persistable属性设置为True来表示,目前模型参数都是长期变量。
    • 临时变量
      只在一个训练迭代中存在,在每一个迭代结束后,所有的临时变量都会被销毁,然后在下一个迭代开始之前,又会先构造出新的临时变量供本轮迭代使用
  • 预测、增量训练和恢复训练的API及用途
    [笔记]90分钟快速入门PaddleFluid_第2张图片

  • Python端做预测的一般步骤

    • 保存预测模型

      batch_id = 0
      for pass_id in range(PASS_NUM):
        train_py_reader.start()
        try:
          while True:
            loss_value, = train_exe.rn(fetch_list=[avg_cost.name])
      
            if batch_id % 10 == 0:
              loss_value = numpy.mean(loss_value)
              print("batch={}, loss={}, sample num={}".format(
               batch_id, loss_value, BATCH_SIZE*cpu_num
              ))
            batch_id += 1
        except fluid.core.EOFException:
          train_py_reader.reset()
          model_dir = 'output/models'
          # 在每一个epoch结束后执行一次save_inference_model保存需要预测的模型及参数
          fluid.io.save_inference_model(
            model_dir, [], [predict, auc_var, cur_auc_var], exe
          )
      
    • 加载预测模型进行预测

      inference_scope = fluid.core.Scope()
      with fluid.scope_guard(inference_scope):
      
        # 通过load_inference_model把sava下来的模型和相应的参数加载到程序中
        [inference_program, feed_target_names, fetch_targets] = (
          fluid.io.load_inference_model(save_dirname, ext)
        )
      
        test_reader = paddle.batch(paddle.dataset.uci_housing.test(), batch_size=20)
      
        results = exe.run(
          inference_program,
          feed={feed_target_names[0]: numpy.array(test_feat)},
          fetch_list=fetch_targets
        )
        print("infer results: ", results[0])
      
  • 单机做增量训练与恢复训练的一般步骤

    • 保存预测模型

      batch_id = 0
      for pass_id in range(PASS_NUM):
        train_py_reader.start()
        try:
          while True:
            loss_value, = train_exe.rn(fetch_list=[avg_cost.name])
      
            if batch_id % 10 == 0:
              loss_value = numpy.mean(loss_value)
              print("batch={}, loss={}, sample num={}".format(
                batch_id, loss_value, BATCH_SIZE*cpu_num
              ))
            batch_id += 1
        except fluid.core.EOFException:
          train_py_reader.reset()
          model_dir = 'output/models'
          # 在每一个epoch结束后执行一次save_persistables保存需要预测的模型或者恢复的参数
          fluid.io.save_persistables(
            model_dir, exe, main_program
          )
      
    • 加载模型变量

      exe.run(start_program)
      # 在训练的start_program执行完后执行load_persistables加载保存的参数
      print ("WARNING: model dir: {} exist, load it". format(load_persistabel_dir))
      fluid.io.load_persistables(
        exe, load_persistabel_dir, main_program=start_program
      )
      
      pe = fluid.ParallelExecutor(...)
      
      try:
        while True:
          cost_val, label_val = pe.run(fetch_list=[avg_cost.name, label.name])
      except fluid.core.EOFException:
        reader.reset()
        fluid.io.save_persistables(
          exe, "...", main_program=main_program
        )
      
  • 分布式做增量训练与恢复训练的一般步骤

    • 与单机的不同点

      • 在训练的最后调用fluid.io.save_persistables保存长期变量时,不需要所有的trainer都调用这个方法,一般0号trainer来保存
      • 多机增量训练的参数加载在PServer端,trainer端不用加载参数。在PServer全部启动后,trainer会送PServer端同步参数
      • 超大规模稀疏参数相关内容请参考官网文档
    • 保存预测模型

      batch_id = 0
      for pass_id in range(PASS_NUM):
        train_py_reader.start()
        try:
          while True:
            loss_value, = train_exe.rn(fetch_list=[avg_cost.name])
      
            if batch_id % 10 == 0:
              loss_value = numpy.mean(loss_value)
              print("batch={}, loss={}, sample num={}".format(
                batch_id, loss_value, BATCH_SIZE*cpu_num
              ))
            batch_id += 1
        except fluid.core.EOFException:
          train_py_reader.reset()
          model_dir = 'output/models'
          # 在每一个epoch结束后执行一次save_persistables
          # 保存需要预测的模型或者恢复的参数,
          # 对于多机训练,建议0号trainer保存参数
          if trainer_id == 0:
            fluid.io.save_persistables(
              model_dir, exe, main_program
            )
      
    • 加载模型变量

      t = fluid.DistributeTranspiler(config=config)
      t.transpile(
        trainer_id, pservers=pserver_endpoints, 
        trainers=trainers, sync_mode=True, curent_endpoint=current_point
      )
      
      if training_mode == "PSERVER":
        exe = fluid.Executor(fluid.CPUPlace())
      
        prog = t.get_pserver_program(current_endpoint)
        startup = t.get_startup_program(current_endpoint, pserver_program=prog)
        exe.run(startup)
      
        # 多机需要在PServer端加载参数,且在startup运行之后
        fluid.io.load_persistables(exe, "...", prog)
      
        exe.run(prog)  
      elif training_mode == "TRAINER":
        train_prog = t.get_trainer_program()
        train_loop(args, train_prog, word2vec_reader, py_reader, loss, trainer_id)
      

FAQ

  • 多机的增量训练过程中参数如何同步到每一个PSERVER?
    可以通过HDFS等第三方存储工具来做同步,目前Paddle Fluid提供了相应的工具集
  • 能否用fluid.io.save_persistables/fluid.io.load_persistables来做Python端的预测?
    可以,用户需要即定义好预测的网络,结合save_persistables保存的参数可以灵活的做预测

模型评估与调试

评估指标

  • 损失函数,详见文档
  • 评估器,详见文档

VisualDL可视化与分析

  • VisualDL使用
    安装可以详见官网

    • 安装,建议是在虚拟机环境或anaconda下pip install --upgrade visualdl
    • 运行一个例子,vdl_create_scratch_log将创建测试日志vdl_create_scratch_log
    • 启动board
      visualdl --logdir=scratch_log --port=8080
    • 访问http://127.0.0.1:8080查看

    Python简单用例

    • cat test.py

      import random
      from visualdl import LogWriter
      
      logdir = "./tmp"
      logger = LogWriter(logdir, sync_cycle=10000)
      
      # mark the componets with 'train' label.
      with logger.mode('trian'):
        # create a scalar component called 'scalar0'
        scalar0 = logger.scalar('scalar0')
      
      # add some records during DL model running.
      for step in range(100):
        scalar0.add_record(step, random.random())
      
    • visual --logdir=tmp --port=8080

    • 访问http://127.0.0.1:8080查看

  • VisualDL可视化损失和评估指标

模型评估

  • 模型评估(基于测试集)
  • 模型推理(基于新样本)
    • 方式一:load_persistable,需定义网络

      def load_image(file):
        pass
      
      # 1. Define Network
      img = fluid.layers.data(name='img', shape=[1,28,28], dtype='float32')
      pred = lenet_5(img)
      test_program = fliud.default_main_program().clone(for_test=True)
      place = fluid.CUDAPlace(0)
      exe = fluid.Executor(place)
      exe.run(fluid.default_startup_program())
      
      # 2. Load model
      fluid.io.load_persistables(
        exe, 'mnist_model', main_program=test_program
      )
      
      # 3. Load image
      im = load_image('test.png')
      
      # 4. Run
      results, = exe.run(
        infer_program, feed={'img': im}, fetch_list=[pred]
      )
      num = np.argmax(results)
      prob = results[0][num]
      print ("Inference result prob: {}, number {}".format(prob, num))
      
    • 方式二:load_inference_model,无需定义网络

      def load_image(file):
        pass
      
      # 1. Load model
      place = fluid.CUDAPlace(0)
      exe = fluid.Executor(place)
      model_path = "mnist_save_model"
      infer_program, feeds, fetches = fluid.io.laod_inference_model(
        model_path, exe, model_filename='model', params_filename='params'
      )
      
      # 2. Load image
      im = load_image('test.png')
      
      # 3. Run
      results, = exe.run(
        infer_program, feed={feeds[0]: im}, fetch_list=feteches
      )
      num = np.argmax(results)
      prob = results[0][num]
      print ("Inference result prob: {}, number {}".format(prob, num))
      

服务器端预测部署

预测库介绍

Paddle服务器端预测库面向高性能线上服务部署

  • 提供了易用的预测接口
  • 底层有包括算子融合等多种性能优化

预测库编译

  • Paddle预测库使用C++开发,用户可以链接预测库,实现应用中嵌入Paddle预测库
  • 使用Paddle预测,有两种方式
    • 直接下载编译好的预测库,链接使用。详见这里

    • 自行编译预测库

      • Paddle基于C++开发,利用CMake工具实现编译配置
      • 编译时会自动下载第三方依赖库实现编译
      • 常用编译开关
        名称 建议值 描述
        CMAKER_BUILD_TYPE Release 编译模式,Release为生产环境模式,Debug为debug模式
        ON_INFER ON 是否打开预测专用优化
        WITH_GPU OFF 是否开启GPU支持,默认CPU
        WITH_MKL ON 是否开启MKL
      • 编译命令
       	PADDLE_ROOT=/path/of/capi
        	git clone https://github.com/PaddlePaddle/Paddle.git
        	cd Paddle
        	mkdir build
        	cd build
        	cmake -DFLUID_INFERENCE_INSTALL_DIR=$PADDLE_ROOT \
          	- DCMAKE_BUILD_TYPE=Release \
          	- DWITH_FLUID_ONLY=ON \
          	- DWITH_SWIG_PY=OFF \
          	- DWITH_PYTHON=OFF \
          	- DWITH_MKL=OFF \
          	- DWITH_GPU=OFF \
          	- DON_INFER=ON \
          	...
        	make
        	make inference_lib_dist
      
      • 编译输出

        • 内容包含

          • 编译出的PaddlePaddle预测库和头文件
          • 第三方链接库和头文件
          • 版本信息与编译选项信息
        • version.txt

          GIT COMMIT ID: ...
          WITH_MKL: ON
          WITH_MKLDNN: ON
          WITH_GPU: on
          CDUA version: 8.0
          CUDNN version: v5
          
      • C++预测API/预测过程

        • 创建Predictor
          • 准备预测配置(NativeConfig)
          • 创建Predictor
        • 实际预测
          • 准备输入信息(PaddleTensor)
          • 执行预测(PaddlePredictor::Run)
      • C++预测API

        • 用户通过调用预测API,来实现对训练好的模型进行高性能C++预测
        • 核心API
          • PaddleTensor: 用于输入和输出的数据结构
          • NativeConfig: 预测配置,例如设置是否使用GPU
          • CreatePaddlePredictor: 根据预测配置,生成Predictor
          • PaddlePredictor.Run(inputs, &outputs): 预测执行接口,对输入进行预测,生成输出
      • 案例介绍:手写数字识别

        #include "paddle_inference_api.h"
        
        // 创建一个 config,并修改相关设置
        paddle::NativeConfig config;
        config.model_dir = 'xxx';
        config.use_gpu = false;
        // 创建一个原生的 PaddlePredictor
        auto predictor = paddle::CreatePaddlePredictor;
        // 创建输入 tensor
        float data[784];
        Random(data, 784); // fake input
        paddle::PaddleTensor tensor;
        tensor.shape = std::vector({1, 28*28});
        tensor.data.Reset(data, sizeof(data));
        tensor.dtype = paddle::PaddleDType::INT64;
        // 创建输出 tensor,输入 tensor 的内存可以复用
        std::vector outputs;
        // 执行预测 CHECK(predictor->Run(slots, &outputs));
        // 获取 outputs ...
        

PaddlePaddle模型库介绍

模型库整体介绍

  • 官方模型库

CV方向特色模型

图像分类 AlexNet, VGG, GoogleNet, DPN, Lnception-v4, MobileNet, ResNEt, SE-ResNeXt
目标检测 Fast R-CNN, Faster R-CNN, Mask R-CNN, SSD, Yolo V3
人脸检测 Pyramidbox
语义分割 ICNext, DeepLab V3+, Mask R-CNN
关键点 OpenPose
视频分类 TSN, NeXtVlad, StNet, LSTM, Attention Clusters
OCR识别 CRNN CTC, Seq2Seq + Attention
人脸识别 Deep Metric Learning
图像生成 CycleGAN, ConditionalGAN, DCGAN

NLP方向特色模型

词法分析 Bi-GRU-CRF
语义表示 ELMo, BERT
语义匹配 DAM, CDSSM, DecAtt, InferSent, SSE
情感分析 BOW, CNN, BiLSTM
语言模型 GRU, LSTM
机器翻译 Seq2Seq, Transformer
阅读理解 BiDAF

模型库使用示例

手写数字识别

  • 下载模型库代码
    git clone https://github.com/PaddlePaddle/book.git --depth=1
    
  • 准备训练数据
    train_reader = paddle.batch(
    	paddle.reader.shuffle(paddle.dataset.mnist.train(), buf_size=500),
    	batch_size=BATCH_SIE
    )
    
  • 训练
    cd recognize_digits
    python train.py
    
    train(
    	nn_type=nn_type,
    	use_cuda=use_cuda,
    	save_dirname=save_dirname,
    	model_filename=model_filename,
    	params_filename=params_filename
    )
    
  • 测试
    infer(
    	use_cuda=use_cuda,
    	save_dirname=save_dirname,
    	model_filename=model_filename,
    	params_filename=params_filename
    )
    

Transformer

- 下载模型库代码
  ```bash
  git clone https://github.com/PaddlePaddle/models.git --depth=1
  ```
- 准备训练数据
  - 执行以下命令
    ```bash
    cd fluid/PaddleNLP/neural_machine_tanslation/transformer
    bash gen_data.sh
    ```
  - 获取机器翻译WMT2016英德数据
  - 使用自定义数据集
    - 准备原始训练数据
    - 参考gen_data.sh脚本的过程处理
- 训练翻译模型
  ```bash
  python -u train.py \
    --src_vocab_fpath gen_data/wmt16_ende_data_bpe/vocab_all.bpe.32000 \
    --trg_vocab_fpath gen_data/wmt16_ende_data_bpe/vocab_all.bpe.32000 \
    --special_token '' '' '' \
    --train_file_pattern gen_data/wmt16_ende_data_bpe/train.tok.clean.bpe.32000.en-de \
    --token_delimiter ' ' \
    --use_token_batch True \
    --batch_size 4096 \
    --sort_type pool \
    --pool_size 200000
  ```
- 翻译测试集
  ```bash
  python -u infer.py \
    --src_vocab_fpath gen_data/wmt16_ende_data_bpe/vocab_all.bpe.32000 \
    --trg_vocab_fpath gen_data/wmt16_ende_data_bpe/vocab_all.bpe.32000 \
    --special_token '' '' '' \
    --test_file_pattern gen_data/wmt16_ende_data_bpe/newstest2016.tok.bpe.32000.en-de \
    --token_delimiter ' ' \
    --batch_size 32 \
    model_path trained_models/iter_100000.infer.model \
    n_head 16 \
    d_model 1024 \
    d_inner_hid 4096 \
    prepostprecess_dropout 0.3 \
    beam_size 5 \
    max_out_len 255
  ```

评估翻译结果

  • 计算BLEU值
    perl gen_data/mosesdecoder/scripts/generic/multi-bleu.perl gen_data/wmt16_ende_data/newstest2014.tok.de < predict.tok.txt
    

你可能感兴趣的:(paddlepaddle)