前言
本次比賽賽題是進行人流密度的估計,因為之前看過很多人體姿態估計和目標檢測的論文,隱約感覺到可以用到這次比賽上來,所以趁着現在時間比較多,趕緊報名參加了一下比賽,比賽規定用paddlepaddle來進行開發,所以最近幾天先學習一下paddlepaddle的相關流程,在這里記錄一下,也好讓自己真正的能夠學到東西。
流程前瞻
在我看來,設計一個深度學習網絡(主要是基於CNN的,其他的沒怎么接觸),主要有以下幾方面:
- 數據的讀取(這里主要是圖片數據和它的“標簽”)。
- 數據的預處理(包含數據集增強和你需要的操作)。
- 如何將你的數據送入網絡。
- 網絡結構的設計(layer的使用)。
- 損失函數的計算(這部分是根據你的任務決定的)。
- 優化器的選擇(我想一般是adam吧)這里有一篇博客分享了各種優化算法的不同。
- 模型的存儲以及加載(加載這部分其實挺重要的,之前看過一篇論文說應用ImageNet上預訓練的模型進行遷移學習,往往能在新的任務上取得更好的效果,當然也不是絕對的)。
- 如何進行測試(可以說就是如何進行前向傳播)。
- 可選:可視化。
接下來就以上幾部分進行學習,在次非常感謝Charlotte77和夜雨飄零1。他們的博文給予了我莫大的幫助,向大佬叩首。
一、數據的讀取
對於本次比賽來說,我的數據是圖片(各種監控的圖片,大小不同),標注是json格式的文件,所以接下來要討論一下在paddlepaddle中如何以圖片為輸入。
參見大佬Charlotte77的博文,paddlepaddle主要是通過reader來進行數據的輸入,這里我參考了paddlepaddle github 上的SSD的例子的例子,先看他們的代碼:
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來看一下:
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輸入數據的生成:
-
把你的數據(圖片,標簽)搞出來,然后用yield來產生一個生成器:reader。
-
將此reader生成batch,也就是train_reader = paddle.batch(reader, batch_size=batch_size)這樣子。
-
接下來就是送入網絡了。
二、如何將數據送入網絡
上面第三步就是將數據送入網絡,這是如何辦到的呢,用過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的前提下):
- 定義一個“占位符”,也就是fluid.layers.data。
- 定義一個feeder(fluid.DataFeeder),來指定設備和feed順序。
- 運用執行器(這個后面再說)的run,指定你需要運行的operation,然后feed數據。
看到這里,我總是感覺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
https://www.cnblogs.com/charlotte77/p/7802226.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