【重磅】百度开源分布式深度学习平台,挑战TensorFlow (教程)

时间:2022-05-05
本文章向大家介绍【重磅】百度开源分布式深度学习平台,挑战TensorFlow (教程),主要内容包括安装(Install)、使用概述(Overview)、数据格式准备(Data Preparation)、数据向模型传送(Transfer Data to Model)、配置中的数据加载定义(Data Provider in Configure)、网络结构(Network Architecture)、词向量模型(Word Vector)、卷积模型(Convolution)、时序模型(Time Sequence)、优化算法(Optimization Algorithm)、训练模型(Training Model)、预测(Prediction)、总体效果总结(Summary)、附录(Appendix)、输出日志(Log)、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

【新智元导读】今天百度开源深度学习平台Paddle。业内人士纷纷点赞:Paddle代码简洁、设计干净,没有太多的abstraction,速度比Tensorflow、Theano快,显存占用小,可多机多卡并行,支持CPU和GPU、文档比较全……Hacker News有评论,在不支持TensorFlow的中国,百度的Paddle或有机会获胜。百度一直说自己深耕深度学习,其技术水平如何,本次开源或可提供一些线索。

百度深度学习 PaddlePaddle (Parallel Distributed Deep Learning)是一个云端托管的分布式深度学习平台,对于序列输入、稀疏输入和大规模数据的模型训练有着良好的支持,支持GPU运算,支持数据并行和模型并行,仅需少量代码就能训练深度学习模型,大大降低了用户使用深度学习技术的成本。

根据百度官方网站的介绍,Paddle 有以下优势:

Github 上介绍,PaddlePaddle有以下特点:

  • 灵活

PaddlePaddle支持大量的神经网络架构和优化算法。很容易安装复杂的模型,比如拥有注意力(attention)机制或者复杂记忆连接的神经机器翻译模型。

  • 高效

为了利用异构计算资源的能力,PaddlePaddle中的每一级都会进行优化,其中包括计算、内存、架构和通信。以下 是几个例子:

  1. 通过SSE/AVX 内联函数、BLAS数据库(例如MKL,ATLAS,cuBLAS)或者定制化的CPU/GPU 核优化数字。
  2. 高度优化的递归网络,在没有padding 的情况下,也能处理不同长度的序列。
  3. 对拥有高维稀疏数据的模型进行局部优化和分布式训练。
  • 可扩展

有了PaddlePaddle,使用多个CPU和GPU以及机器来加速训练可以变得很轻松。 PaddlePaddle 能通过优化通信,获得高吞吐量和性能。

  • 与产品的连接

PaddlePaddle的部署也很简单。在百度,PaddlePaddle 已经被用于产品和服务中,拥有大量用户。应用场景包括广告点击率(CTR)预测、大规模图像分类、光学字符识别、搜索排名、计算机病毒检测、推荐等等。PaddlePaddle 在百度有着巨大的影响。

PaddlePaddle地址:Github:https://github.com/baidu/paddle 官方:http://www.paddlepaddle.org/doc_cn/(中文)

评价 :贾扬清在知乎上的技术点评

Facebook 深度学习研究员贾扬清在知乎上评价说:今天刚看到的,简单说一些第一印象(以目前的github repo为准)。整体的设计感觉和Caffe心有灵犀,同时解决了Caffe早期设计当中的一些问题(比如说default stream)。

1. 很高质量的GPU代码

2. 非常好的RNN设计

3. 设计很干净,没有太多的abstraction,这一点比TensorFlow好很多。

4. 高速RDMA的部分貌似没有开源(可能是因为RDMA对于cluster design有一定要求):Paddle/RDMANetwork.h at master · baidu/Paddle · GitHub

5. 设计思路比较像第一代的DL框架,不过考虑到paddle已经有年头了,这样设计还是有历史原因的。

5.1 config是hard-code的protobuf message,这对扩展性可能会有影响。

5.2 可以看到很多有意思的类似历史遗留的设计:采用了STREAM_DEFAULT macro,然后通过TLS的方式定向到非default stream:Paddle/hl_base.h at 4fe7d833cf0dd952bfa8af8d5d7772bbcd552c58 · baidu/Paddle · GitHub (所以Paddle off-the-shelf不支持mac?)

5.3 在梯度计算上采用了传统的粗粒度forward/backward设计(类似Caffe)。可能有人会说“所以paddle没有auto gradient generation”,这是不对的,autograd的存在与否和op的粒度粗细无关。事实上,TensorFlow在意识到细粒度operator超级慢的速度以后,也在逐渐转回粗粒度的operator上。

目前只看到这里。总之是一个非常solid的框架,百度的开发功底还是不错的。

影响力评价 : Hack News的评论

cs702: 又一个深度学习框架,这次来自百度。

鉴于TensorFlow在AI研究者和实践者中的统治力逐渐增强,加上拥有大量使用基础的既有框架,比如Theano,Torch和Caffe,我并不认为这一新的框架在美国或者其他西方的市场会获得大范围的采用。在我看来,TensorFlow的势力在当下很难战胜。

但是,Paddle在中国可能会获得大范围的采用,因为那是百度的母国市场。

Vonnik:使用TensorFlow的大部分都是来自Udacity 课程的学生。所以,“Tensor Flow崛起” 这这种说法并不正确,这些人中95%都是没用什么经验的,更不用说在实际产品中应用了。从技术层面上来说,TensorFlow并没有比其他的框架好很多。它有一个很漂亮的网站,有几个教学视频,但是它性能并不是很好,比如,在大型产品的环境中。深度学习平台实际上变化得非常快:TensorFlow、CNTK、DSSTNE等等,都是过去10个月间出现的。所以说,Paddle 还是有机会的,尤其是在中国,因为那里的人不能使用谷歌云——那是TensorFlow最佳的训练平台。

HackerNews上,曾有一个关于最受欢迎的深度学习工具的投票,时间范围是7月15日到8月15日,当时,TensorFlow获得了第一。

总的来说, Paadle是百度使用了多年的深度学习平台,并且已经做出了一些实际的产品,较为成熟。在性能和各项指标上,比如:代码简洁、设计很干净,没有太多的abstraction、 速度比tensorflow,theano快,显存占用小、可多机多卡并行,支持cpu和gpu、文档比较全等等,Paddle也获得了较高的肯定,是一个不错的深度学习工具,在国内有较大的应用潜力。

附:PaddlePaddle快速入门教程 http://www.paddlepaddle.org/doc_cn/

我们以文本分类问题作为背景,介绍PaddlePaddle使用流程和常用的网络基础单元的配置方法。

安装(Install)

首先请参考安装教程安装PaddlePaddle。

使用概述(Overview)

文本分类问题:对于给定的一条文本, 我们从提前给定的类别集合中选择其所属类 别。比如通过用户对电子商务网站评论,评估产品的质量:

  • 这个显示器很棒! (好评)
  • 用了两个月之后这个显示器屏幕碎了。(差评)

每一个任务流程都可以分为如下5个基础部分。

  1. 数据格式准备
    • 每行保存一条样本,类别Id 和文本信息用Tab间隔, 文本中的单词用空格分隔(如果不切词,则字与字之间用空格分隔),例如:类别Id‘t’ 这 个 显 示 器 很 棒 !
  2. 数据向模型传送
    • PaddlePaddle可以读取Python写的传输数据脚本,所有字符都将转换为连续整数表示的Id传给模型
  3. 网络结构(由易到难展示4种不同的网络配置)
    • 逻辑回归模型
    • 词向量模型
    • 卷积模型
    • 时序模型
    • 优化算法
  4. 训练模型
  5. 预测

数据格式准备(Data Preparation)

在本问题中,我们使用Amazon电子产品评论数据, 将评论分为好评(正样本)和差评(负样本)两类。demo/quick_start里提供了数据下载脚本 和预处理脚本。

cd demo/quick_start
./data/get_data.sh
pip install -r requirements.txt
./preprocess.sh

数据向模型传送(Transfer Data to Model)

Python数据加载脚本(Data Provider Script)

下面dataprovider_bow.py文件给出了完整例子,主要包括两部分:

  • initalizer: 定义文本信息、类别Id的数据类型。
  • process: yield文本信息和类别Id,和initalizer里定义顺序一致。
from paddle.trainer.PyDataProvider2 import *# id of the word not in dictionaryUNK_IDX = 0# initializer is called by the framework during initialization.# It allows the user to describe the data types and setup the# necessary data structure for later use.# `settings` is an object. initializer need to properly fill settings.input_types.# initializer can also store other data structures needed to be used at process().# In this example, dictionary is stored in settings.# `dictionay` and `kwargs` are arguments passed from trainer_config.lr.pydef initializer(settings, dictionary, **kwargs):    # Put the word dictionary into settings
    settings.word_dict = dictionary    # setting.input_types specifies what the data types the data provider
    # generates.
    settings.input_types = [        # The first input is a sparse_binary_vector,
        # which means each dimension of the vector is either 0 or 1. It is the
        # bag-of-words (BOW) representation of the texts.
        sparse_binary_vector(len(dictionary)),        # The second input is an integer. It represents the category id of the
        # sample. 2 means there are two labels in the dataset.
        # (1 for positive and 0 for negative)
        integer_value(2)]# Delaring a data provider. It has an initializer 'data_initialzer'.# It will cache the generated data of the first pass in memory, so that# during later pass, no on-the-fly data generation will be needed.# `setting` is the same object used by initializer()# `file_name` is the name of a file listed train_list or test_list file given# to define_py_data_sources2(). See trainer_config.lr.py.@provider(init_hook=initializer, cache=CacheType.CACHE_PASS_IN_MEM)def process(settings, file_name):    # Open the input data file.
    with open(file_name, 'r') as f:        # Read each line.
        for line in f:            # Each line contains the label and text of the comment, separated by t.
            label, comment = line.strip().split('t')            # Split the words into a list.
            words = comment.split()            # convert the words into a list of ids by looking them up in word_dict.
            word_vector = [settings.word_dict.get(w, UNK_IDX) for w in words]            # Return the features for the current comment. The first is a list
            # of ids representing a 0-1 binary sparse vector of the text,
            # the second is the integer id of the label.
            yield word_vector, int(label)

配置中的数据加载定义(Data Provider in Configure)

在模型配置中利用define_py_data_sources2加载数据:

from paddle.trainer_config_helpers import *file = "data/dict.txt"word_dict = dict()with open(dict_file, 'r') as f:    for i, line in enumerate(f):
        w = line.strip().split()[0]
        word_dict[w] = i# define the data sources for the model.# We need to use different process for training and prediction.# For training, the input data includes both word IDs and labels.# For prediction, the input data only includs word Ids.define_py_data_sources2(train_list='data/train.list',
                        test_list='data/test.list',
                        module="dataprovider_bow",
                        obj="process",
                        args={"dictionary": word_dict})
  • data/train.list,data/test.list: 指定训练、测试数据
  • module=”dataprovider”: 数据处理Python文件名
  • obj=”process”: 指定生成数据的函数
  • args={“dictionary”: word_dict}: 额外的参数,这里指定词典

更详细用例请参考文档Python Use Case, 数据格式和详细文档请参考 PyDataProviderWrapper。

网络结构(Network Architecture)

本节我们将专注于网络结构的介绍。

我们将以基本的逻辑回归网络作为起点,并逐渐展示更加深入的功能。更详细的网络配置 连接请参考Layer文档。 所有配置在demo/quick_start目录,首先列举逻辑回归网络。

逻辑回归模型(Logistic Regression)

流程如下:

  • 获取利用one-hot vector表示的每个单词,维度是词典大小
word = data_layer(name="word",  size=word_dim)
  • 获取该条样本类别Id,维度是类别个数。
label = data_layer(name="label", size=label_dim)
  • 利用逻辑回归模型对该向量进行分类,同时会计算分类准确率
# Define a fully connected layer with logistic activation (also called softmax activation).output = fc_layer(input=word,
                  size=label_dim,
                  act_type=SoftmaxActivation())# Define cross-entropy classification loss and error.classification_cost(input=output, label=label)
  • input: 除过data层,每个层都有一个或多个input,多个input以list方式输入
  • size: 该层神经元个数
  • act_type: 激活函数类型

效果总结:我们将在后面介绍训练和预测的流程的脚本。在此为方便对比不同网络结构, 我们随时总结了各个网络的复杂度和效果。

网络名称

参数数量

错误率

逻辑回归

252 KB

8.652%

词向量模型(Word Vector)

embeding模型需要稍微改变数据提供的脚本,即dataprovider_emb.py,词向量模型、 卷积模型、时序模型均使用该脚

  • 文本输入类型定义为整数类型integer_value
  • 设置文本输入类型seq_type为SequenceType.SEQUENCE
def initializer(settings, dictionary, **kwargs):
    settings.word_dict = dictionary
    settings.input_types = [        # Define the type of the first input as sequence of integer.
        integer_value(len(dictionary), seq_type=SequenceType.SEQUENCE),        # Define the second input for label id
        integer_value(2)]@provider(init_hook=initializer)def process(settings, file_name):    ...
    # omitted, it is same as the data provider for LR model

该模型依然是使用逻辑回归分类网络的框架, 只是将句子利用连续向量表示替换稀疏 向量表示, 即对第3步进行替换。句子表示的计算更新为2步:

  • 利用单词Id查找对应的该单词的连续表示向量(维度为word_dim), 输入N个单词,输出为N个word_dim维度向量
emb = embedding_layer(input=word, size=word_dim)
  • 将该句话包含的所有单词向量求平均得到句子的表示
avg = pooling_layer(input=emb, pooling_type=AvgPooling())

其它部分和逻辑回归网络结构一致。 效果总结:

网络名称

参数数量

错误率

词向量模型

15 MB

8.484%

卷积模型(Convolution)

卷积网络是一种特殊的从词向量表示到句子表示的方法, 也就是将词向量模型额步 骤3-2进行进一步演化, 变为3个新的子步骤。

文本卷积分为三个步骤:

  1. 获取每个单词左右各k个近邻, 拼接成一个新的向量表示;
  2. 对该表示进行非线性变换 (例如Sigmoid变换), 成为维度为hidden_dim的新的向量;
  3. 在每个维度上取出在该句话新的向量集合上该维度的最大值作为最后的句子表示向量。 这3个子步骤可配置为:
text_conv = sequence_conv_pool(input=emb,
                               context_start=k,
                               context_len=2 * k + 1)

效果总结:

网络名称

参数数量

错误率

卷积模型

16 MB

5.628%

时序模型(Time Sequence)

时序模型即为RNN模型, 包括简单的RNN模型、GRU模型、LSTM模型等。

  • GRU模型配置:
gru = simple_gru(input=emb, size=gru_size)
  • LSTM模型配置:
lstm = simple_lstm(input=emb, size=lstm_size)

针对本问题,我们采用单层LSTM模型,并使用了Dropout,效果总结:

网络名称

参数数量

错误率

时序模型

16 MB

4.812%

优化算法(Optimization Algorithm)

优化算法包括 Momentum, RMSProp,AdaDelta,AdaGrad,ADAM,Adamax等,这里采用Adam优化方法,加了L2正则和梯度截断。

settings(batch_size=128,
         learning_rate=2e-3,
         learning_method=AdamOptimizer(),
         regularization=L2Regularization(8e-4),
         gradient_clipping_threshold=25)

训练模型(Training Model)

在完成了数据和网络结构搭建之后, 我们进入到训练部分。

训练脚本:我们将训练的命令行保存在了 train.sh文件中。训练时所需设置的主要参数如下:

paddle train --config=trainer_config.py --log_period=20 --save_dir=./output --num_passes=15 --use_gpu=false

这里没有介绍多机分布式训练,可以参考分布式训练的demo学习如何进行多机训练。

预测(Prediction)

可以使用训练好的模型评估带有label的验证集,也可以预测没有label的测试集。

测试脚本如下,将会测试配置文件中test.list指定的数据。

paddle train --use_gpu=false --job=test --init_model_path=./output/pass-0000x

可以参考Python API预测 教程,或其他demo的Python预测过程。也可以通过如下方式预测。

预测脚本(predict.sh):

model="output/pass-00003"paddle train 
    --config=trainer_config.lstm.py 
    --use_gpu=false 
    --job=test 
    --init_model_path=$model 
    --config_args=is_predict=1 
    --predict_output_dir=. mv rank-00000 result.txt

与训练网络配置不同的是:无需label相关的层,指定outputs输出概率层(softmax输出), 指定batch_size=1,数据传输无需label数据,预测数据指定test_list的位置。

is_predict = get_config_arg('is_predict', bool, False)trn = 'data/train.list' if not is_predict else Nonetst = 'data/test.list' if not is_predict else 'data/pred.list'obj = 'process' if not is_predict else 'process_pre'batch_size = 128 if not is_predict else 1if is_predict:
    maxid = maxid_layer(output)
    outputs([maxid,output])else:
    label = data_layer(name="label", size=2)
    cls = classification_cost(input=output, label=label)
    outputs(cls)

总体效果总结(Summary)

这些流程中的数据下载、网络配置、训练脚本在/demo/quick_start目录,我们在此总 结上述网络结构在Amazon-Elec测试集(25k)上的效果:

网络名称

参数数量

错误率

配置文件

逻辑回归模型

252KB

8.652%

trainer_config.lr.py

词向量模型

15MB

8.484%

trainer_config.emb.py

卷积模型

16MB

5.628%

trainer_config.cnn.py

时序模型

16MB

4.812%

trainer_config.lstm.py

附录(Appendix)

命令行参数(Command Line Argument)

  • –config:网络配置
  • –save_dir:模型存储路径
  • –log_period:每隔多少batch打印一次日志
  • –num_passes:训练轮次,一个pass表示过一遍所有训练样本
  • –config_args:命令指定的参数会传入网络配置中。
  • –init_model_path:指定初始化模型路径,可用在测试或训练时指定初始化模型。

默认一个pass保存一次模型,也可以通过saving_period_by_batches设置每隔多少batch保存一次模型。 可以通过show_parameter_stats_period设置打印参数信息等。 其他参数请参考令行参数文档。

输出日志(Log)

TrainerInternal.cpp:160]  Batch=20 samples=2560 AvgCost=0.628761 CurrentCost=0.628761 Eval: classification_error_evaluator=0.304297  CurrentEval: classification_error_evaluator=0.304297

模型训练会看到这样的日志,详细的参数解释如下面表格:

名称

解释

Batch=20

表示过了20个batch

samples=2560

表示过了2560个样本

AvgCost

每个pass的第0个batch到当前batch所有样本的平均cost

CurrentCost

当前log_period个batch所有样本的平均cost

Eval: classification_error_evaluator

每个pass的第0个batch到当前batch所有样本的平均分类错误率

CurrentEval: classification_error_evaluator

当前log_period个batch所有样本的平均分类错误率