热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:深度学习系列PaddlePaddle之手写数字识别

本文由编程笔记#小编为大家整理,主要介绍了深度学习系列PaddlePaddle之手写数字识别相关的知识,希望对你有一定的参考价值。上周
本文由编程笔记#小编为大家整理,主要介绍了深度学习系列PaddlePaddle之手写数字识别相关的知识,希望对你有一定的参考价值。


  上周在搜索关于深度学习分布式运行方式的资料时,无意间搜到了paddlepaddle,发现这个框架的分布式训练方案做的还挺不错的,想跟大家分享一下。不过呢,这块内容太复杂了,所以就简单的介绍一下paddlepaddle的第一个“hello word”程序----mnist手写数字识别。下一次再介绍用PaddlePaddle做分布式训练的方案。其实之前也写过一篇用CNN识别手写数字集的文章(链接戳这里~),是用keras实现的,这次用了paddlepaddle后,正好可以简单对比一下两个框架的优劣。

 

什么是PaddlePaddle?

  PaddlePaddle是百度推出的一个深度学习框架,可能大多数人平常用的比较多的一般是tensorflow,caffe,mxnet等,但其实PaddlePaddle也是一个非常不错的框架(据说以前叫Paddle,现在改名叫PaddlePaddle,不知道为啥总觉得有股莫名的萌点)

 

PaddlePaddle能做什么?

  传统的基本都能做,尤其对NLP的支持很好,譬如情感分析,word embedding,语言模型等,反正你想得到的,常见的都可以用它来试一试~

 

PaddlePaddle的安装

  不得不吐槽一下PaddlePaddle的安装,官网上说“PaddlePaddle目前唯一官方支持的运行的方式是Docker容器”,而docker其实在国内还并不是特别的流行,之前遇到的所有的框架,都有很多种安装方式,非常方便,所以这个唯一支持docker让人觉得非常诡异 = =!不过偶然试了一下,居然可以用pip install,不过为啥官网上没有写呢?所以,对于新手来说,最简单的安装方式就是:




  • CPU版本安装



        pip install paddlepaddle 



  • GPU版本安装



        pip install paddlepaddle-gpu 


 

 用PaddlePaddle实现手写数字识别

  训练步骤

  传统的方式这次就不展开讲了,为了对比我们还是用CNN来进行训练。PaddlePaddle训练一次模型完整的过程可以如下几个步骤:



导入数据---->定义网络结构---->训练模型---->保存模型---->测试结果  

  下面,我直接用代码来展示训练的过程(以后代码都会放在github里):

 

#coding:utf-8import osfrom PIL import Imageimport numpy as npimport paddle.v2 as paddle# 设置是否用gpu,0为否,1为是with_gpu = os.getenv('WITH_GPU', '0') != '1'# 定义网络结构def convolutional_neural_network_org(img):    # 第一层卷积层
conv_pool_1 = paddle.networks.simple_img_conv_pool(
       input
=img,
       filter_size
=5,
       num_filters
=20,
       num_channel
=1,
       pool_size
=2,
       pool_stride
=2,
       act
=paddle.activation.Relu())    # 第二层卷积层
conv_pool_2 = paddle.networks.simple_img_conv_pool(
       input
=conv_pool_1,
       filter_size
=5,
       num_filters
=50,
       num_channel
=20,
       pool_size
=2,
       pool_stride
=2,
       act
=paddle.activation.Relu())    # 全连接层
predict = paddle.layer.fc(
       input
=conv_pool_2, size=10, act=paddle.activation.Softmax())    return predictdef main():    # 初始化定义跑模型的设备
paddle.init(use_gpu=with_gpu, trainer_count=1)    # 读取数据
images = paddle.layer.data(
       name
='pixel', type=paddle.data_type.dense_vector(784))
   label
= paddle.layer.data(
       name
='label', type=paddle.data_type.integer_value(10))    # 调用之前定义的网络结构
predict = convolutional_neural_network(images)    # 定义损失函数
cost = paddle.layer.classification_cost(input=predict, label=label)    # 指定训练相关的参数
parameters = paddle.parameters.create(cost)    # 定义训练方法
optimizer = paddle.optimizer.Momentum(
       learning_rate
=0.1 / 128.0,
       momentum
=0.9,
       regularization
=paddle.optimizer.L2Regularization(rate=0.0005 * 128))    # 训练模型
trainer = paddle.trainer.SGD(
       cost
=cost, parameters=parameters, update_equation=optimizer)
   lists
= []    # 定义event_handler,输出训练过程中的结果
def event_handler(event):        if isinstance(event, paddle.event.EndIteration):            if event.batch_id % 100 == 0:                print "Pass %d, Batch %d, Cost %f, %s" % (
                   event.pass_id, event.batch_id, event.cost, event.metrics)        
if isinstance(event, paddle.event.EndPass):            # 保存参数
with open('params_pass_%d.tar' % event.pass_id, 'w') as f:
               parameters.to_tar(f)
           result
= trainer.test(reader=paddle.batch(
               paddle.dataset.mnist.test(), batch_size
=128))            print "Test with Pass %d, Cost %f, %s
" % (
               event.pass_id, result.cost, result.metrics)
           lists.append((event.pass_id, result.cost,
                         result.metrics[
'classification_error_evaluator']))
   trainer.train(
       reader
=paddle.batch(
           paddle.reader.shuffle(paddle.dataset.mnist.train(), buf_size
=8192),
           batch_size
=128),
       event_handler
=event_handler,
       num_passes
=10)    # 找到训练误差最小的一次结果
best = sorted(lists, key=lambda list: float(list[1]))[0]    print 'Best pass is %s, testing Avgcost is %s' % (best[0], best[1])    print 'The classification accuracy is %.2f%%' % (100 - float(best[2]) * 100)    # 加载数据  
def load_image(file):
       im
= Image.open(file).convert('L')
       im
= im.resize((28, 28), Image.ANTIALIAS)
       im
= np.array(im).astype(np.float32).flatten()
       im
= im / 255.0 return im    # 测试结果
test_data = []
   cur_dir
= os.path.dirname(os.path.realpath(__file__))
   test_data.append((load_image(cur_dir
+ '/image/infer_3.png'), ))
   probs
= paddle.infer(
       output_layer
=predict, parameters=parameters, input=test_data)
   lab
= np.argsort(-probs) # probs and lab are the results of one batch data
print "Label of image/infer_3.png is: %d" % lab[0][0]if __name__ == '__main__':
   main()

【深度学习系列】PaddlePaddle之手写数字识别

  上面的代码看起来很长,但结构还是很清楚的。下面我们用实际数据测试一下,看一下效果到底怎么样~

 



  BaseLine版本

 

  首先我用了官网给出的例子,直接用最基本的CNN网络结构训练了一下,代码如下: 

【深度学习系列】PaddlePaddle之手写数字识别

1 def convolutional_neural_network_org(img): 2 # 第一层卷积层 3 conv_pool_1 = paddle.networks.simple_img_conv_pool( 4 input=img, 5 filter_size=5, 6 num_filters=20, 7 num_channel=1, 8 pool_size=2, 9 pool_stride=2,10 act=paddle.activation.Relu())11 # 第二层卷积层12 conv_pool_2 = paddle.networks.simple_img_conv_pool(13 input=conv_pool_1,14 filter_size=5,15 num_filters=50,16 num_channel=20,17 pool_size=2,18 pool_stride=2,19 act=paddle.activation.Relu())20 # 全连接层21 predict = paddle.layer.fc(22 input=conv_pool_2, size=10, act=paddle.activation.Softmax())23 return predict

【深度学习系列】PaddlePaddle之手写数字识别

  输出结果如下:

【深度学习系列】PaddlePaddle之手写数字识别

I1023 13:45:46.519075 34144 Util.cpp:166] commandline: --use_gpu=True --trainer_count=1[INFO 2017-10-23 13:45:52,667 layers.py:2539] output for __conv_pool_0___conv: c = 20, h = 24, w = 24, size = 11520[INFO 2017-10-23 13:45:52,667 layers.py:2667] output for __conv_pool_0___pool: c = 20, h = 12, w = 12, size = 2880[INFO 2017-10-23 13:45:52,668 layers.py:2539] output for __conv_pool_1___conv: c = 50, h = 8, w = 8, size = 3200[INFO 2017-10-23 13:45:52,669 layers.py:2667] output for __conv_pool_1___pool: c = 50, h = 4, w = 4, size = 800I1023 13:45:52.675750 34144 GradientMachine.cpp:85] Initing parameters..
I1023
13:45:52.686153 34144 GradientMachine.cpp:92] Init parameters done.
Pass 0, Batch 0, Cost
3.048408, {'classification_error_evaluator': 0.890625}
Pass 0, Batch
100, Cost 0.188828, {'classification_error_evaluator': 0.0546875}
Pass 0, Batch
200, Cost 0.075183, {'classification_error_evaluator': 0.015625}
Pass 0, Batch
300, Cost 0.070798, {'classification_error_evaluator': 0.015625}
Pass 0, Batch
400, Cost 0.079673, {'classification_error_evaluator': 0.046875}
Test with Pass 0, Cost
0.074587, {'classification_error_evaluator': 0.023800000548362732}
```
```
```
Pass
4, Batch 0, Cost 0.032454, {'classification_error_evaluator': 0.015625}
Pass
4, Batch 100, Cost 0.021028, {'classification_error_evaluator': 0.0078125}
Pass
4, Batch 200, Cost 0.020458, {'classification_error_evaluator': 0.0}
Pass
4, Batch 300, Cost 0.046728, {'classification_error_evaluator': 0.015625}
Pass
4, Batch 400, Cost 0.030264, {'classification_error_evaluator': 0.015625}
Test with Pass
4, Cost 0.035841, {'classification_error_evaluator': 0.01209999993443489}
Best
pass is 4, testing Avgcost is 0.0358410408473The classification accuracy is 98.79%Label of image/infer_3.png is: 3real    0m31.565s
user    0m20.996s
sys    0m15.891s

【深度学习系列】PaddlePaddle之手写数字识别

  可以看到,第一行输出选择的设备是否是gpu,这里我选择的是gpu,所以等于1,如果是cpu,就是0。接下来四行输出的是网络结构,然后开始输出训练结果,训练结束,我们把这几次迭代中误差最小的结果输出来,98.79%,效果还是很不错的,毕竟只迭代了5次。最后看一下输出时间,非常快,约31秒。然而这个结果我并不是特别满意,因为之前用keras做的时候调整的网络模型训练往后准确率能够达到99.72%,不过速度非常慢,迭代69次大概需要30分钟左右,所以我觉得这个网络结构还是可以改进一下的,所以我对这个网络结构改进了一下,请看改进版

 



  改进版 

【深度学习系列】PaddlePaddle之手写数字识别

def convolutional_neural_network(img):    # 第一层卷积层
conv_pool_1 = paddle.networks.simple_img_conv_pool(
       input
=img,
       filter_size
=5,
       num_filters
=20,
       num_channel
=1,
       pool_size
=2,
       pool_stride
=2,
       act
=paddle.activation.Relu())    # 加一层dropout层
drop_1 = paddle.layer.dropout(input=conv_pool_1, dropout_rate=0.2)    # 第二层卷积层
conv_pool_2 = paddle.networks.simple_img_conv_pool(
       input
=drop_1,
       filter_size
=5,
       num_filters
=50,
       num_channel
=20,
       pool_size
=2,
       pool_stride
=2,
       act
=paddle.activation.Relu())    # 加一层dropout层
drop_2 = paddle.layer.dropout(input=conv_pool_2, dropout_rate=0.5)    # 全连接层
fc1 = paddle.layer.fc(input=drop_2, size=10, act=paddle.activation.Linear())
   bn
= paddle.layer.batch_norm(input=fc1,act=paddle.activation.Relu(),
        layer_attr
=paddle.attr.Extra(drop_rate=0.2))
   predict
= paddle.layer.fc(input=bn, size=10, act=paddle.activation.Softmax())    return predict

【深度学习系列】PaddlePaddle之手写数字识别

 

  在改进版里我们加了一些dropout层来避免过拟合。分别在第一层卷积层和第二层卷积层后加了dropout,阈值设为0.5。改变网络结构也非常简单,直接在定义的网络结构函数里对模型进行修改即可,这一点其实和keras的网络结构定义方式还是挺像的,易用性很高。下面来看看效果:

【深度学习系列】PaddlePaddle之手写数字识别

I1023 14:01:51.653827 34244 Util.cpp:166] commandline: --use_gpu=True --trainer_count=1[INFO 2017-10-23 14:01:57,830 layers.py:2539] output for __conv_pool_0___conv: c = 20, h = 24, w = 24, size = 11520[INFO 2017-10-23 14:01:57,831 layers.py:2667] output for __conv_pool_0___pool: c = 20, h = 12, w = 12, size = 2880[INFO 2017-10-23 14:01:57,832 layers.py:2539] output for __conv_pool_1___conv: c = 50, h = 8, w = 8, size = 3200[INFO 2017-10-23 14:01:57,833 layers.py:2667] output for __conv_pool_1___pool: c = 50, h = 4, w = 4, size = 800I1023 14:01:57.842871 34244 GradientMachine.cpp:85] Initing parameters..
I1023
14:01:57.854014 34244 GradientMachine.cpp:92] Init parameters done.
Pass 0, Batch 0, Cost
2.536199, {'classification_error_evaluator': 0.875}
Pass 0, Batch
100, Cost 1.668236, {'classification_error_evaluator': 0.515625}
Pass 0, Batch
200, Cost 1.024846, {'classification_error_evaluator': 0.375}
Pass 0, Batch
300, Cost 1.086315, {'classification_error_evaluator': 0.46875}
Pass 0, Batch
400, Cost 0.767804, {'classification_error_evaluator': 0.25}
Pass 0, Batch
500, Cost 0.545784, {'classification_error_evaluator': 0.1875}
Pass 0, Batch
600, Cost 0.731662, {'classification_error_evaluator': 0.328125}
```
```
```
Pass
49, Batch 0, Cost 0.415184, {'classification_error_evaluator': 0.09375}
Pass
49, Batch 100, Cost 0.067616, {'classification_error_evaluator': 0.0}
Pass
49, Batch 200, Cost 0.161415, {'classification_error_evaluator': 0.046875}
Pass
49, Batch 300, Cost 0.202667, {'classification_error_evaluator': 0.046875}
Pass
49, Batch 400, Cost 0.336043, {'classification_error_evaluator': 0.140625}
Pass
49, Batch 500, Cost 0.290948, {'classification_error_evaluator': 0.125}
Pass
49, Batch 600, Cost 0.223433, {'classification_error_evaluator': 0.109375}
Pass
49, Batch 700, Cost 0.217345, {'classification_error_evaluator': 0.0625}
Pass
49, Batch 800, Cost 0.163140, {'classification_error_evaluator': 0.046875}
Pass
49, Batch 900, Cost 0.203645, {'classification_error_evaluator': 0.078125}
Test with Pass
49, Cost 0.033639, {'classification_error_evaluator': 0.008100000210106373}
Best
pass is 48, testing Avgcost is 0.0313018567383The classification accuracy is 99.28%Label of image/infer_3.png is: 3real    5m3.151s
user    4m0.052s
sys    1m8.084s

 

  从上面的数据来看,这个效果还是很不错滴,对比之前用keras训练的效果来看,结果如下:

                    

 

 

  可以看到这个速度差异是很大的了,在准确率差不多的情况下,训练时间几乎比原来缩短了六倍,网络结构也相对简单,说明需要调整的参数也少了很多。

 

总结

  paddlepaddle用起来还是很方便的,不论是定义网络结构还是训练速度,都值得一提,然而我个人的体验中,认为最值得说的是这几点:

 

1.导入数据方便。这次训练的手写数字识别数据量比较小,但是如果想要添加数据,也非常方便,直接添加到相应目录下。

2.event_handler机制,可以自定义训练结果输出内容。之前用的keras,以及mxnet等都是已经封装好的函数,输出信息都是一样的,这里paddlepaddle把这个函数并没有完全封装,而是让我们用户自定义输出的内容,可以方便我们减少冗余的信息,增加一些模型训练的细节的输出,也可以用相应的函数画出模型收敛的图片,可视化收敛曲线。

3.速度快。上面的例子已经证明了paddlepaddle的速度,并且在提升速度的同时,模型准确度也与最优结果相差不多,这对于我们训练海量数据的模型是一个极大的优势啊!

 

然而,paddlepaddle也有几点让我用的有点难受,譬如文档太少了啊,报错了上网上搜没啥结果啊等等,不过我觉得这个应该不是大问题,以后用的人多了以后肯定相关资料也会更多。所以一直很疑惑,为啥paddlepaddle不火呢?安装诡异是一个吐槽点,但其实还是很优秀的一个开源软件,尤其是最值得说的分布式训练方式,多机多卡的设计是非常优秀的,本篇没有讲,下次讲讲如何用paddlepaddle做单机单卡,单机多卡,多机单卡和多机多卡的训练方式来训练模型,大家多多用起来呀~~可以多交流呀~

 

ps:由于paddlepaddle的文档实在太少了,官网的文章理论介绍的比较多,网上的博文大多数都是几个经典例子来回跑,所以我打算写个系列,跟实战相关的,不再只有深度学习的“hello world”程序,这次用“hello world”做个引子,下篇开始写点干货哈哈~


推荐阅读
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • ElasticSerach初探第一篇认识ES+环境搭建+简单MySQL数据同步+SpringBoot整合ES
    一、认识ElasticSearch是一个基于Lucene的开源搜索引擎,通过简单的RESTfulAPI来隐藏Lucene的复杂性。全文搜索,分析系统&# ... [详细]
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • 众筹商城与传统商城的区别及php众筹网站的程序源码
    本文介绍了众筹商城与传统商城的区别,包括所售产品和玩法不同以及运营方式不同。同时还提到了php众筹网站的程序源码和方维众筹的安装和环境问题。 ... [详细]
  • 本文讨论了在PHP中将空格转换为问号的问题,并提供了解决方案。文章指出,空格不是标准的空格,而是特殊的0xC2 0xA0字符。作者尝试使用mb_convert_encoding函数将utf8字符串转换为gbk编码,但未成功。文章建议检查编辑器是否对空格进行了特殊处理,并提供了使用base64_encode函数打印结果的方法。最后,给出了完整的代码示例。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • SpringMVC接收请求参数的方式总结
    本文总结了在SpringMVC开发中处理控制器参数的各种方式,包括处理使用@RequestParam注解的参数、MultipartFile类型参数和Simple类型参数的RequestParamMethodArgumentResolver,处理@RequestBody注解的参数的RequestResponseBodyMethodProcessor,以及PathVariableMapMethodArgumentResol等子类。 ... [详细]
  • HTML5网页模板怎么加百度统计?
    本文介绍了如何在HTML5网页模板中加入百度统计,并对模板文件、css样式表、js插件库等内容进行了说明。同时还解答了关于HTML5网页模板的使用方法、表单提交、域名和空间的问题,并介绍了如何使用Visual Studio 2010创建HTML5模板。此外,还提到了使用Jquery编写美好的HTML5前端框架模板的方法,以及制作企业HTML5网站模板和支持HTML5的CMS。 ... [详细]
  • 本文介绍了互联网思维中的三个段子,涵盖了餐饮行业、淘品牌和创业企业的案例。通过这些案例,探讨了互联网思维的九大分类和十九条法则。其中包括雕爷牛腩餐厅的成功经验,三只松鼠淘品牌的包装策略以及一家创业企业的销售额增长情况。这些案例展示了互联网思维在不同领域的应用和成功之道。 ... [详细]
  • Sleuth+zipkin链路追踪SpringCloud微服务的解决方案
    在庞大的微服务群中,随着业务扩展,微服务个数增多,系统调用链路复杂化。Sleuth+zipkin是解决SpringCloud微服务定位和追踪的方案。通过TraceId将不同服务调用的日志串联起来,实现请求链路跟踪。通过Feign调用和Request传递TraceId,将整个调用链路的服务日志归组合并,提供定位和追踪的功能。 ... [详细]
  • 本文介绍了iOS开发中检测和解决内存泄漏的方法,包括静态分析、使用instruments检查内存泄漏以及代码测试等。同时还介绍了最能挣钱的行业,包括互联网行业、娱乐行业、教育行业、智能行业和老年服务行业,并提供了选行业的技巧。 ... [详细]
  • Python脚本编写创建输出数据库并添加模型和场数据的方法
    本文介绍了使用Python脚本编写创建输出数据库并添加模型数据和场数据的方法。首先导入相应模块,然后创建输出数据库并添加材料属性、截面、部件实例、分析步和帧、节点和单元等对象。接着向输出数据库中添加场数据和历程数据,本例中只添加了节点位移。最后保存数据库文件并关闭文件。文章还提供了部分代码和Abaqus操作步骤。另外,作者还建立了关于Abaqus的学习交流群,欢迎加入并提问。 ... [详细]
  • 如何使用PLEX播放组播、抓取信号源以及设置路由器
    本文介绍了如何使用PLEX播放组播、抓取信号源以及设置路由器。通过使用xTeve软件和M3U源,用户可以在PLEX上实现直播功能,并且可以自动匹配EPG信息和定时录制节目。同时,本文还提供了从华为itv盒子提取组播地址的方法以及如何在ASUS固件路由器上设置IPTV。在使用PLEX之前,建议先使用VLC测试是否可以正常播放UDPXY转发的iptv流。最后,本文还介绍了docker版xTeve的设置方法。 ... [详细]
author-avatar
钟爱gyt_201
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有