您的位置:首頁>正文

「深度學習系列」PaddlePaddle之手寫數位識別

作者:Charlotte77 數學系的資料採擷民工

博客專欄:http://www.cnblogs.com/charlotte77/

個人公眾號:Charlotte資料採擷(ID:CharlotteDataMining)

在搜索關於深度學習分散式運行方式的資料時, 無意間搜到了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_org(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()

上面的代碼看起來很長, 但結構還是很清楚的。 下面我們用實際資料測試一下, 看一下效果到底怎麼樣~

BaseLine版本

首先我用了官網給出的例子, 直接用最基本的CNN網路結構訓練了一下, 代碼如下:

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 predict

輸出結果如下:

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.565suser 0m20.996ssys 0m15.891s

可以看到, 第一行輸出選擇的設備是否是gpu, 這裡我選擇的是gpu, 所以等於1, 如果是cpu, 就是0。 接下來四行輸出的是網路結構, 然後開始輸出訓練結果, 訓練結束, 我們把這幾次反覆運算中誤差最小的結果輸出來, 98.79%, 效果還是很不錯的, 畢竟只反覆運算了5次。 最後看一下輸出時間, 非常快, 約31秒。 然而這個結果我並不是特別滿意, 因為之前用keras做的時候調整的網路模型訓練往後準確率能夠達到99.72%, 不過速度非常慢, 反覆運算69次大概需要30分鐘左右, 所以我覺得這個網路結構還是可以改進一下的,

所以我對這個網路結構改進了一下, 請看改進版

改進版 

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

在改進版裡我們加了一些dropout層來避免過擬合。 分別在第一層卷積層和第二層卷積層後加了dropout, 閾值設為0.5。 改變網路結構也非常簡單, 直接在定義的網路結構函數裡對模型進行修改即可, 這一點其實和keras的網路結構定義方式還是挺像的, 易用性很高。 下面來看看效果:

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.151suser 4m0.052ssys 1m8.084s

從上面的資料來看, 這個效果還是很不錯滴, 對比之前用keras訓練的效果來看, 結果如下:

可以看到這個速度差異是很大的了, 在準確率差不多的情況下, 訓練時間幾乎比原來縮短了六倍, 網路結構也相對簡單, 說明需要調整的參數也少了很多。

總結

paddlepaddle用起來還是很方便的, 不論是定義網路結構還是訓練速度, 都值得一提, 然而我個人的體驗中, 認為最值得說的是這幾點:

1.導入資料方便。 這次訓練的手寫數位識別資料量比較小, 但是如果想要添加資料, 也非常方便, 直接添加到相應目錄下。

2.event_handler機制, 可以自訂訓練結果輸出內容。 之前用的keras, 以及mxnet等都是已經封裝好的函數, 輸出資訊都是一樣的, 這裡paddlepaddle把這個函數並沒有完全封裝, 而是讓我們使用者自訂輸出的內容, 可以方便我們減少冗餘的資訊, 增加一些模型訓練的細節的輸出, 也可以用相應的函數畫出模型收斂的圖片, 視覺化收斂曲線。

3.速度快。 上面的例子已經證明了paddlepaddle的速度, 並且在提升速度的同時, 模型準確度也與最優結果相差不多,這對於我們訓練海量資料的模型是一個極大的優勢啊!

然而,paddlepaddle也有幾點讓我用的有點難受,譬如文檔太少了啊,報錯了上網上搜沒啥結果啊等等,不過我覺得這個應該不是大問題,以後用的人多了以後肯定相關資料也會更多。所以一直很疑惑,為啥paddlepaddle不火呢?安裝詭異是一個吐槽點,但其實還是很優秀的一個開源軟體,尤其是最值得說的分散式訓練方式,多機多卡的設計是非常優秀的,本篇沒有講,下次講講如何用paddlepaddle做單機單卡,單機多卡,多機單卡和多機多卡的訓練方式來訓練模型,大家多多用起來呀~~可以多交流呀~

ps:由於paddlepaddle的文檔實在太少了,官網的文章理論介紹的比較多,網上的博文大多數都是幾個經典例子來回跑,所以我打算寫個系列,跟實戰相關的,不再只有深度學習的“hello world”程式,這次用“hello world”做個引子,下篇開始寫點乾貨哈哈~

老師介紹:胡曉曼老師(Charlotte),高級演算法工程師 ,博客專家;

擅長用通俗易懂的方式講解深度學習和機器學習演算法,熟悉Tensorflow,PaddlePaddle等深度學習框架,負責過多個機器學習落地專案,如垃圾評論自動過濾,用戶分級精准行銷,分散式深度學習平臺搭建等,都取了的不錯的效果。

出處:https://www.hellobi.com/u/CharlotteDataMining/articles

三個月教你從零入門人工智慧!| 深度學習精華實踐課程

https://edu.hellobi.com/course/268

模型準確度也與最優結果相差不多,這對於我們訓練海量資料的模型是一個極大的優勢啊!

然而,paddlepaddle也有幾點讓我用的有點難受,譬如文檔太少了啊,報錯了上網上搜沒啥結果啊等等,不過我覺得這個應該不是大問題,以後用的人多了以後肯定相關資料也會更多。所以一直很疑惑,為啥paddlepaddle不火呢?安裝詭異是一個吐槽點,但其實還是很優秀的一個開源軟體,尤其是最值得說的分散式訓練方式,多機多卡的設計是非常優秀的,本篇沒有講,下次講講如何用paddlepaddle做單機單卡,單機多卡,多機單卡和多機多卡的訓練方式來訓練模型,大家多多用起來呀~~可以多交流呀~

ps:由於paddlepaddle的文檔實在太少了,官網的文章理論介紹的比較多,網上的博文大多數都是幾個經典例子來回跑,所以我打算寫個系列,跟實戰相關的,不再只有深度學習的“hello world”程式,這次用“hello world”做個引子,下篇開始寫點乾貨哈哈~

老師介紹:胡曉曼老師(Charlotte),高級演算法工程師 ,博客專家;

擅長用通俗易懂的方式講解深度學習和機器學習演算法,熟悉Tensorflow,PaddlePaddle等深度學習框架,負責過多個機器學習落地專案,如垃圾評論自動過濾,用戶分級精准行銷,分散式深度學習平臺搭建等,都取了的不錯的效果。

出處:https://www.hellobi.com/u/CharlotteDataMining/articles

三個月教你從零入門人工智慧!| 深度學習精華實踐課程

https://edu.hellobi.com/course/268

同類文章
Next Article
喜欢就按个赞吧!!!
点击关闭提示