【新智元导读】今天百度开源深度学习平台Paddle。业内人士纷纷点赞:Paddle代码简洁、设计干净,没有太多的abstraction,速度比Tensorflow、Theano快,显存占用小,可多机多卡并行,支持CPU和GPU、文档比较全……Hacker News有评论,在不支持TensorFlow的中国,百度的Paddle或有机会获胜。百度一直说自己深耕深度学习,其技术水平如何,本次开源或可提供一些线索。
百度深度学习 PaddlePaddle (Parallel Distributed Deep Learning)是一个云端托管的分布式深度学习平台,对于序列输入、稀疏输入和大规模数据的模型训练有着良好的支持,支持GPU运算,支持数据并行和模型并行,仅需少量代码就能训练深度学习模型,大大降低了用户使用深度学习技术的成本。
根据百度官方网站的介绍,Paddle 有以下优势:
Github 上介绍,PaddlePaddle有以下特点:
PaddlePaddle支持大量的神经网络架构和优化算法。很容易安装复杂的模型,比如拥有注意力(attention)机制或者复杂记忆连接的神经机器翻译模型。
为了利用异构计算资源的能力,PaddlePaddle中的每一级都会进行优化,其中包括计算、内存、架构和通信。以下 是几个例子:
有了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使用流程和常用的网络基础单元的配置方法。
首先请参考安装教程安装PaddlePaddle。
文本分类问题:对于给定的一条文本, 我们从提前给定的类别集合中选择其所属类 别。比如通过用户对电子商务网站评论,评估产品的质量:
每一个任务流程都可以分为如下5个基础部分。
类别Id‘\t’ 这 个 显 示 器 很 棒 !
在本问题中,我们使用Amazon电子产品评论数据, 将评论分为好评(正样本)和差评(负样本)两类。demo/quick_start
里提供了数据下载脚本 和预处理脚本。
cd demo/quick_start
./data/get_data.sh
pip install -r requirements.txt
./preprocess.sh
下面dataprovider_bow.py文件给出了完整例子,主要包括两部分:
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)
在模型配置中利用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})
更详细用例请参考文档Python Use Case, 数据格式和详细文档请参考 PyDataProviderWrapper。
本节我们将专注于网络结构的介绍。
我们将以基本的逻辑回归网络作为起点,并逐渐展示更加深入的功能。更详细的网络配置 连接请参考Layer文档。 所有配置在demo/quick_start
目录,首先列举逻辑回归网络。
流程如下:
word = data_layer(name="word", size=word_dim)
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)
效果总结:我们将在后面介绍训练和预测的流程的脚本。在此为方便对比不同网络结构, 我们随时总结了各个网络的复杂度和效果。
网络名称 | 参数数量 | 错误率 |
---|---|---|
逻辑回归 | 252 KB | 8.652% |
embeding模型需要稍微改变数据提供的脚本,即dataprovider_emb.py
,词向量模型、 卷积模型、时序模型均使用该脚
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步:
emb = embedding_layer(input=word, size=word_dim)
avg = pooling_layer(input=emb, pooling_type=AvgPooling())
其它部分和逻辑回归网络结构一致。 效果总结:
网络名称 | 参数数量 | 错误率 |
---|---|---|
词向量模型 | 15 MB | 8.484% |
卷积网络是一种特殊的从词向量表示到句子表示的方法, 也就是将词向量模型额步 骤3-2进行进一步演化, 变为3个新的子步骤。
文本卷积分为三个步骤:
text_conv = sequence_conv_pool(input=emb,
context_start=k,
context_len=2 * k + 1)
效果总结:
网络名称 | 参数数量 | 错误率 |
---|---|---|
卷积模型 | 16 MB | 5.628% |
时序模型即为RNN模型, 包括简单的RNN模型、GRU模型、LSTM模型等。
gru = simple_gru(input=emb, size=gru_size)
lstm = simple_lstm(input=emb, size=lstm_size)
针对本问题,我们采用单层LSTM模型,并使用了Dropout,效果总结:
网络名称 | 参数数量 | 错误率 |
---|---|---|
时序模型 | 16 MB | 4.812% |
优化算法包括 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)
在完成了数据和网络结构搭建之后, 我们进入到训练部分。
训练脚本:我们将训练的命令行保存在了 train.sh
文件中。训练时所需设置的主要参数如下:
paddle train \--config=trainer_config.py \--log_period=20 \--save_dir=./output \--num_passes=15 \--use_gpu=false
这里没有介绍多机分布式训练,可以参考分布式训练的demo学习如何进行多机训练。
可以使用训练好的模型评估带有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)
这些流程中的数据下载、网络配置、训练脚本在/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 |
默认一个pass保存一次模型,也可以通过saving_period_by_batches设置每隔多少batch保存一次模型。 可以通过show_parameter_stats_period设置打印参数信息等。 其他参数请参考令行参数文档。
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所有样本的平均分类错误率 |