PaddlePaddle发布新版API,简化深度学习编程
PaddlePaddle是百度于2016年9月开源的一款分布式深度学习平台,为百度内部多项产品提供深度学习算法支持。为了使PaddlePaddle更加易用,我们已经做了一系列的工作,包括使用Kubernetes集群管理系统来进行部署与运行。
2017年3月11日,我们很高兴地发布新API的Alpha版0.10.0rc1,以及《深度学习入门教程》。目前教程包括八个示例程序,均可以在Jupyter Notebook上运行,即用户可以通过网页浏览文档并运行程序。
使用新API,PaddlePaddle的程序代码将大幅缩短。下图对比展示了一个卷积神经网络在旧API(图左半边)和新API(图右半边)的效果。
新API能达到如此显著的代码简化效果,有以下三个关键的设计思想。
一个新的概念模型
我们的设计原则是:让用户在神经网络中表达和解决实际的问题,并用更加灵活的方式来描述新的深度学习算法。因此,新模型包含以下几部分概念:
- 模型(model)是一个或多个拓扑结构的组合。
- 拓扑结构(topology)是一系列层的表达式。
- 层(layer)可以是任何类型的计算函数,包括损失(cost)函数。
- 有些层有梯度参数,有些层没有,大多数损失函数没有参数。
- 在一些拓扑结构中,层与层之间共享参数。
- 对于多个拓扑结构间存在参数共享的情况,PaddlePaddle能自动找出并创建这些参数。
下面,通过两个例子来展示我们的一些设计思想。
层与层之间共享参数
假设我们要学习一个文本词向量f,训练样本是“对查询Q来说,搜索结果A比B更准确”,任务目标是:sim(f(A), f(Q)) > sim(f(B), f(Q)),也就是f(A)和f(Q)的相似度,大于f(B)和f(Q)的相似度。为了学习f,我们构建了一个三分支的网络结构:
A -> f -
Q -> f --> cost
B -> f -/
这里的模型实际上是x -> f
,但我们需要重复三次来学习f,以下伪代码展示了如何构建这样一个模型:
def f(in):
e = paddle.layer.embedding(in, parameter_name="embedding")
o = paddle.layer.softmax(e, parameter_name="semantic")
return o
# 创建三个拓扑结构(子模型),由于它们对应每一层的参数名都相同,因此三个拓扑结构共享参数。
fA = f(paddle.layer.data(input_name="A"))
fB = f(paddle.layer.data(input_name="B"))
fQ = f(paddle.layer.data(input_name="Q"))
# 使用交叉熵代价损失函数,并取最小值。
topology = paddle.layer.less_than(
paddle.layer.cross_entropy_cost(fA, fQ),
paddle.layer.corss_entropy_cost(fB, fQ))
# 自动创建参数
parameters = paddle.parameters.create(topology)
拓扑结构之间共享参数
假设GAN模型包含两个拓扑结构d0和d1,d0和d1之间共享了参数。因此在训练过程中,更新一个拓扑结构的参数时可能需要同时更新另一个。如果使用旧API,用户将不得不访问非常底层的API,这部分API接口通常相对晦涩,且文档不全。而使用新API,GAN模型仅需几十行即可,伪代码如下:
def G(in):
# 生成器,本示例中假设只含有一层。
return paddle.layer.fc(in, parameter_name="G")
def D(in, parameters_mutable);
# 对抗器,本示例中假设只含有一层。
# parameters_mutable表示是否需要更新参数。
return paddle.layer.fc(in, parameters_name="D", parameters_mutable)
# 创建第一个拓扑结构d0,包括生成器G和对抗器D,但训练过程中只更新G的参数。
d0 = paddle.layer.should_be_false(
D(G(paddle.layer.data()),
False))
# 创建第二个拓扑结构d1,只包含对抗器D,训练过程中更新D的参数。
# 注意:d1和d0的参数是共享的。
d1 = paddle.layer.should_be_true(D(paddle.layer.data()))
# 自动创建参数
parameters = paddle.parameters.create([d0, d1])
可组合的数据加载模块
在工业届的AI应用中,数据加载部分通常需要大量的源代码。为了减轻用户的这部分工作量,新API的数据接口设计包含几个部分:
-
reader
:从本地、网络、分布式文件系统等读取数据,也可随机生成数据,并返回一个或多个数据项。 -
reader creator
:一个返回reader
的函数。 -
reader decorator
:装饰器,可组合一个或多个reader
。 -
batch reader
:与reader
类似,但可批量返回一个或多个数据项。
下面展示了一个随机生成数据,并返回单个数据项的reader creator
函数:
def reader_creator():
def reader():
while True:
yield numpy.random.uniform(-1, 1, size=784)
return reader
新API还有助于数据加载操作的复用。例如,我们定义两个reader
,分别是impressions()
和clicks()
,前者用于读取搜索引擎的日志流数据,后者用于读取点击流数据;然后,我们可以通过预定义的reader decorator
缓存并组合这些数据,再对合并后的数据进行乱序操作:
r = paddle.reader.shuffle(
paddle.reader.compose(
paddle.reader(impressions(impression_url), buf_size=8192),
paddle.reader(clicks(click_url), buf_size=8192), 4096)
如果我们希望只使用数据集中的前5000个样本来进行小型实验,代码如下:
paddle.reader.firstn(r, 5000)
此外,我们发布了paddle.datasets包,为教程中的八个示例程序都提供了预定义好的数据加载接口。第一次调用时会自动下载公共数据集并进行预处理,之后的调用则会从本地缓存中自动读取。
更高层次的API
PaddlePaddle是一个诞生在工业界的系统,从一开始就强调支持分布式训练。但在编写分布式程序时,旧API暴露了很多用户不需要知道的细节。另外,PaddlePaddle的C++代码中的训练过程是一个for循环结构,不能跑在交互式的Jupyter Notebook上。因此,我们发布了新API,提供了一些更高层次的接口,如train(训练接口)、test(测试接口)和infer(应用接口)。这些新API既能在本地运行,将来也支持在Kubernets集群上运行分布式作业。
以上述的三分支模型和GAN模型为例,下面展示train
和infer
的使用思路。
####三分支模型####
# 训练时,使用read_ranking_model_data读入数据,更新topology中的parameters。
paddle.train(topology, parameters, reader=read_ranking_model_data)
# 应用时,因为fA、fB和fQ共享参数,因此只需要fA的参数即可。
[testA, testB, testQ] = read_ranking_model_data()
print "The sematic-vector of testA: ", paddle.infer(fA, parameters, testA)
####GAN模型####
# 循环训练d0和d1,注意两者的训练数据不同。
for ...:
train(d0, parameters, reader=read_from_rng)
train(d1, parameters, reader=read_from_realistic_images)
# 使用d1来做应用:
print "D thinks a batch of images are realistic", paddle.infer(d1, parameters, read_mnist_images)
我们会持续优化新API,上述设计思想的具体实现会在0.10.0版中完成,同时也欢迎您的评论、反馈和代码贡献!
参考文献
1.PaddlePaddle’s New API Simplifies Deep Learning Programs.
2.PaddlePaddle Design Doc.
3.PaddlePaddle Python Data Reader Design Doc.
关于《深度学习入门教程》
这本书脱胎于PaddlePaddle Team的线上教材《深度学习入门》,包括新手入门、识别数字、图像分类、词向量、情感分析、语义角色标注、机器翻译、个性化推荐等内容。
以下是《深度学习入门》一书的目录:
在线阅读:http://book.paddlepaddle.org/index.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 常用方法(文件夹操作)
- 常用方法(文件名操作)
- 构建File对象
- 两个常用静态变量
- Spring MVC 整合 Servlet 3.0
- 初探 SpringBoot 自动装配
- 报错405:HTTP method GET is not supported by this URL
- 思科模拟器:网络安全实验
- Django入门:基于 Django 的 Web 页面开发
- 从零开始重新认识 Spring Framework
- 思科模拟器:高级交换实验
- ElasticSearch 基本的查询命令+集成 SpringBoot
- 数据库能力测试:SQL 语句改错
- IDEA 导入并运行 Eclipse 的 JavaWeb 项目
- 使用思科模拟器 Cisco Packet Tracer 模拟交换机基本配置