本次比賽賽題是進行人流密度的估計,由於以前看過不少人體姿態估計和目標檢測的論文,隱約感受到能夠用到此次比賽上來,因此趁着如今時間比較多,趕忙報名參加了一下比賽,比賽規定用paddlepaddle來進行開發,因此最近幾天先學習一下paddlepaddle的相關流程,在這裏記錄一下,也好讓本身真正的可以學到東西。html
在我看來,設計一個深度學習網絡(主要是基於CNN的,其餘的沒怎麼接觸),主要有如下幾方面:git
接下來就以上幾部分進行學習,在次很是感謝Charlotte77和夜雨飄零1。他們的博文給予了我莫大的幫助,向大佬叩首。github
對於本次比賽來講,個人數據是圖片(各類監控的圖片,大小不一樣),標註是json格式的文件,因此接下來要討論一下在paddlepaddle中如何以圖片爲輸入。算法
參見大佬Charlotte77的博文,paddlepaddle主要是經過reader來進行數據的輸入,這裏我參考了paddlepaddle github 上的SSD的例子的例子,先看他們的代碼:json
train_reader = paddle.batch( reader.train(data_args, train_file_list), batch_size=batch_size) test_reader = paddle.batch( reader.test(data_args, val_file_list), batch_size=batch_size)
其中reader是import來的,咱們以reader.train來看一下:api
def train(settings, file_list, shuffle=True): file_list = os.path.join(settings.data_dir, file_list) if 'coco' in settings.dataset: train_settings = copy.copy(settings) if '2014' in file_list: sub_dir = "train2014" elif '2017' in file_list: sub_dir = "train2017" train_settings.data_dir = os.path.join(settings.data_dir, sub_dir) return coco(train_settings, file_list, 'train', shuffle) else: return pascalvoc(settings, file_list, 'train', shuffle)
這裏看得出來,是利用了以前定義的coco函數或者pascalvoc函數,就是從不一樣的數據集讀取數據,以coco爲例,看一下,到底返回了什麼,這裏代碼有點長,咱們主要看返回的是什麼:網絡
... if 'cocoMAP' in settings.ap_version: yield im, boxes, lbls, iscrowd, \ [im_id, im_width, im_height] else: yield im, boxes, lbls, iscrowd return reader
balabala一大堆,終於發現,返回的是一個生成器reader,可見,主要就在於生成這個生成器,下面來總結一下padlepaddle輸入數據的生成:session
把你的數據(圖片,標籤)搞出來,而後用yield來產生一個生成器:reader。 框架
接下來就是送入網絡了。ide
上面第三步就是將數據送入網絡,這是如何辦到的呢,用過tensorflow的童鞋們可能知道,咱們能夠用一個palceholder(佔位符)來連接咱們的原始數據和咱們的網絡,在這裏,也是一樣的方法:
image = fluid.layers.data(name='image', shape=image_shape, dtype='float32') gt_box = fluid.layers.data(name='gt_box', shape=[4], dtype='float32', lod_level=1) gt_label = fluid.layers.data(name='gt_label', shape=[1], dtype='int32', lod_level=1)
用的是fluid.layers.data,等一下,lod_level是啥,這裏paddlepaddle有個序列輸入格式,這裏lod_level爲1說明這條數據是序列格式,可是這裏感受應該不用加,這部分等我作過再說把。具體查看大佬Charlotte77的博文吧。
有了這個「佔位符」以後,只需將咱們以前的那個batch_size的train_reader feed進去就行了,具體以下:
feeder = fluid.DataFeeder(place=place, feed_list=[image, gt_box, gt_label]) if args.parallel: loss_v, = train_exe.run(fetch_list=[loss.name],feed=feeder.feed(data))#這裏的data就是以前train_reader的數據,fetch_list就是要執行的operation的名稱,feed的順序就是上面feed_list指定的 else: loss_v, = exe.run(fluid.default_main_program(), feed=feeder.feed(data), fetch_list=[loss]) #train_exe和exe是以前定義的,相似與tensorflow的session(我的感受,實際上仍是不同的)以下: #exe = fluid.Executor(place) #train_exe = fluid.ParallelExecutor(use_cuda=args.use_gpu, loss_name=loss.name) #其中place爲指定設備(CPU GPU)
好了,總結一下,如何將數據送入網絡(在有了reader的前提下):
看到這裏,我老是感受paddlepaddle的fluid和tensorflow很像,先定義圖模型,而後運行,可是看到官方說fluid是和TensorFlow Eager Execution很像,據我瞭解(沒有用過,因此有多是錯誤的,望批評指正)TensorFlow Eager Execution是針對以前tensoflow不能實時出結果(必須sess.run)來設計的,可是如今看好像不是很像,之後看懂了再來解釋。留坑。
對於如何將數據保存成RecordIO文件並讀取,這篇會詳細解釋。
這部分咱們直接看代碼吧,在SSD的例子中:
locs, confs, box, box_var = mobile_net(num_classes, image, image_shape)
調用了mobile_net模型,這個有興趣本身看吧,主要是fluid.layers中各類層的應用,這個估計各個深度學習框架都差很少,這部分實現的仍是挺全的。
loss = fluid.layers.ssd_loss(locs, confs, gt_box, gt_label, box,box_var) loss = fluid.layers.reduce_sum(loss) ... optimizer = fluid.optimizer.RMSProp( learning_rate=fluid.layers.piecewise_decay(boundaries, values), regularization=fluid.regularizer.L2Decay(0.00005), ) optimizer.minimize(loss) place = fluid.CUDAPlace(0) if args.use_gpu else fluid.CPUPlace() exe = fluid.Executor(place) exe.run(fluid.default_startup_program())
咱們來看這一部分,定義了loss,而後指定了優化器,而後最小化loss,指定設備,而後啓動咱們的程序。我感受這裏是個大坑!有沒有發現有些文檔裏面不是這麼個流程,而是這樣子的(來源paddlepaddle 03.image_classification):
place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace() trainer = fluid.Trainer( train_func=train_program, optimizer_func=optimizer_program, place=place) trainer.train( reader=train_reader, num_epochs=EPOCH_NUM, event_handler=event_handler, feed_order=['pixel', 'label'])
指定了一個trainer而後調用train。
還有一種:
parameters = paddle.parameters.create(cost) trainer = paddle.trainer.SGD(cost=cost, parameters=parameters,update_equation=momentum_optimizer)
先根據cost(loss)產生要優化的參數,而後指定這些參數進行優化。
這到底用哪種呢?幸虧有大佬夜雨飄零1的經驗,是由於新版本Fluid的緣由,如今大部分都是用executor來進行編寫的。因此之後也不用煩惱了,這裏吐槽一下官方文檔,感受維護人員要少吃一個雞腿,不一樣版本變化太大,然而官方只給最新的示例,可是對於以前的代碼並無進行版本的說明,致使咱們學習起來有點混亂,但願可以從新寫一下book。
這部分官方文檔資料挺全的,固然對於你們比較關心的如何加載ImageNet 預訓練模型,也是有的,這裏有例子,可是說實話這裏有點問題,大佬在這裏也作了討論,原本想參考官方文檔進行resnet的加載,可是一方面官方腳本執行時鏈接不上,再看模型加載會出現各類問題,因此暫時放棄了這種想法,等一下官方的優化。
這部分的主要代碼:
exe = fluid.Executor(fluid.CPUPlace()) param_path = "./my_paddle_model" prog = fluid.default_main_program() fluid.io.save_params(executor=exe, dirname=param_path, main_program=None) ... exe = fluid.Executor(fluid.CPUPlace()) param_path = "./my_paddle_model" prog = fluid.default_main_program() fluid.io.load_params(executor=exe, dirname=param_path, main_program=prog)
可見是經過fluid.io來實現的。
這部分應該是paddlepaddle的優點了,一方面咱們訓練的過程當中但願可以進行測試,一方面當咱們的模型訓練完之後咱們也但願可以利用前向傳播進行預測。paddlepaddle都有這兩方面實現:第一種官方給了很好的示例,這裏就不贅述了。對於第二種,paddlepaddle也進行了很好的封裝:
inferencer = fluid.Inferencer( # infer_func=softmax_regression, # uncomment for softmax regression # infer_func=multilayer_perceptron, # uncomment for MLP infer_func=convolutional_neural_network, # uncomment for LeNet5 param_path=params_dirname, place=place) results = inferencer.infer({'img': img})
convolutional_neural_network就是你的模型裏面生成predict的那個函數,params_dirname是保存參數的路徑,可見,用paddlepaddle來進行前向傳播十分簡單,定義好數據以後,加載參數,而後調用infer就能夠預測了。
paddlepaddle還有很好的部署能力,可是侷限於我如今用的功能,這部分並無研究,這篇博客主要是串一下如何用paddlepadle搭建深度學習模型,其中有不少細節沒有注意,並且有不少地方也不必定準確,但願各位多批評指正。
參考:
https://blog.csdn.net/u010089444/article/details/76725843
http://www.javashuo.com/article/p-wwjagzao-gd.html
http://www.cnblogs.com/charlotte77/p/7906363.html
https://github.com/PaddlePaddle/models/tree/develop/fluid/object_detection
https://github.com/PaddlePaddle/book/blob/high-level-api-branch/03.image_classification/train.py