當哈利波特用深度學習打魁地奇,斯萊特林已經涼了!

當哈利波特用深度學習打魁地奇,斯萊特林已經涼了!

你是不是也和優達菌一樣,如果沒有圖像識別,眼神還真的跟不上魁地奇(可能是優達菌老了orz)。接下來優達菌將手把手教你實現這一黑科技!

使用卷積神經網路(CNN)的圖像分類其實非常容易,特別是隨著功能強大的前端封裝(如帶有TensorFlow後端的Keras)的出現。 但是如果你想識別一個圖像中的多個對象呢?

TensorFlow物體識別API把最複雜的部分幫你解決了,你只需要準備數據集和進行一些配置就能訓練自己的模型了。TensorFlow也提供在MS COCO,Kitti或開放圖片集訓練過的預訓練模型,如果你只是想做簡單的物體檢測可以使用這些模型。唯一的缺點是它們是預定義好的,只能預測被數據集定義好的類別。

TensorFlow物體識別API實踐

但如果你想識別不在類別列表裡的東西呢?這就是這篇文章的目的——我會以哈利波特里的魁地奇運動為例手把手教你創建自己的物體識別程序。

準備開始

克隆我的這個GitHub倉庫:github.com/thatbrguy/Ob 作為你的基目錄。這篇文章中提到的所有的文件都可以在這個倉庫找到。

你也可以克隆TensorFlow的模型倉庫:github.com/tensorflow/m 。如此你只需叫做slim和object_detection的兩個文件夾,其它都可以刪除。記住不要重命名任何文件夾。

安裝依賴包

假設你已經安裝好TensorFlow了,你可能還需要安裝一些依賴包。在基目錄里運行下列命令:

pip install -r requirements.txt

此API使用Protobufs來配置和訓練模型參數,我們需要在使用前編譯Protobuf庫。首先,使用如下命令安裝Protobuf編譯器:

sudo apt-get install protobuf-compiler

現在你可以使用下列命令編譯Protobuf庫:

protoc object_detection/protos/*.proto --python_out=.

你需要在你的Python路徑變數里插入基目錄和slim目錄的路徑。每次打開新終端都需要輸入如下命令,你也可以選擇把路徑加入你的~/.bashrc文件,就不用每次都要輸入啦。

export PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slim

準備輸入

起初我的動機非常單純,就是用TensorFlow搞一個魁地奇追求手,我想寫一個程序去定位每一個畫面里的金色飛賊。但後來我又決定玩大一點,直接識別魁地奇世界盃里的所有會動的裝置豈不更酷?

魁地奇運動使用三種球:一隻在球員中互相傳遞,用來得分的紅球·鬼飛球(Quaffle);兩隻會自己行動,干擾球員的鐵球·遊走球(Bludgers),一隻被抓到就可以獲得高分結束比賽的金球·金色飛賊(Golden Snitch)。(譯者:歪個樓,這裡有個英文梗:Quaffle+Bludgers×2+Golden Snitch=Quidditch 如果不改變球的譯名,按照J·K·羅琳的構詞法,魁地奇應該叫鬼走走賊。飛球+游球×2+金色飛=鬼走走賊)

我們從準備label_map.pbtxt 文件開始。所有的目標標籤名和各自的ID都包含在內。注意:標籤ID應該從1開始。下面是我的文件內容:

item {id: 1 name: 『snitch』}item { id: 2 name: 『quaffle』}item {id: 3name: 『bludger』}

OK,現在該收集數據了。可能你會覺得這個過程有趣或無聊,但這始終不是一個輕鬆的活。

我用OpenCV寫了個腳本,獲取了一個哈利波特視頻里的所有鏡頭,然後用另一個腳本隨機從中抽樣了300張圖。如果你也想用一樣的,這個腳本在我的GitHub 倉庫 里。

是的,不要998,只要300張圖!我的數據集不大,主要是因為我受不了標註太多圖片。如果你想,你可以選擇像亞馬遜的Mechanical Turk這類服務來標註你的圖片。

標註

每個圖像定位任務都要求正確標註(ground truth)。這裡使用的注釋是XML文件,其中4個坐標表示圍繞對象的邊界框的位置及其標籤。 我們使用Pascal VOC格式。 示例注釋將如下所示:

<annotation> <filename>182.jpg</filename> <size> <width>1280</width> <height>586</height> <depth>3</depth> </size> <segmented>0</segmented> <object> <name>bludger</name> <bndbox> <xmin>581</xmin> <ymin>106</ymin> <xmax>618</xmax> <ymax>142</ymax> </bndbox> </object> <object> <name>quaffle</name> <bndbox> <xmin>127</xmin> <ymin>406</ymin> <xmax>239</xmax> <ymax>526</ymax> </bndbox> </object></annotation>

你可能會想:我真的需要那麼痛苦地一個一個手動標註這些文件嗎?當然不咯!聰明寶寶都用在對象上繪製框並對其進行注釋的GUI工具。 LabelImg是Linux / Windows用戶的絕佳工具。 Mac用戶可以選擇RectLabel。

開始收集數據前的幾個小Tips:

? 注釋後不要重命名圖片文件。代碼嘗試使用XML文件中指定的文件名(LabelImg自動使用圖像文件名填寫)來查找圖像。此外,請確保您的圖片XML文件具有相同的名稱。

? 在開始注釋之前,請確保將圖像調整為所需的大小。如果稍後再這樣做,則注釋將無意義,你會不得不縮放XML內的注釋值。

? LabelImg可能會向XML文件輸出一些額外的元素(如<pose>,<truncated>,<path>)。不需要刪除這些,因為它們不會干擾代碼。

要是你搞砸了,utils.py或許有些功能可以幫助你。如果你只是想小試牛刀,可以直接下載我注視好的數據集。二者你都能在我的GitHub倉庫找到。

最後,創建一個名為trainval的文本文件。它應該包含所有圖像/ XML文件的名稱。例如,如果你的數據集中包含img1.jpg,img2.jpg和img1.xml,img2.xml,那麼你的trainval.txt文件應如下所示:

img1img2

將數據集分為兩個文件夾,即圖像注釋。 在注釋文件夾中放 label_map.pbtxttrainval.txt 。 在標註文件夾內創建一個名為 xmls 的文件夾,並將所有XML放入該文件夾中。 目錄層次結構應該如下所示:

-base_directory|-images|-annotations||-xmls||-label_map.pbtxt||-trainval.txt

API接受TFRecords格式輸入。 不用擔心,你可以使用我的代碼倉庫中提供的create_tf_record.py文件將你的數據集轉換為TFRecords。 在你的基目錄中執行以下命令:

python create_tf_record.py --data_dir=`pwd` --output_dir=`pwd`

程序運行結束後,你會得到兩個文件:train.recordval.record 。 標準數據集被拆分為用作訓練的70%和用於驗證的30%。 如果需要,你可以更改文件main()函數中的拆分部分。

訓練模型

終於到訓練步驟啦!我們需要選擇一個定位模型來訓練。那麼問題來了:那麼多模型可選,每個在性能上都不一樣。你得為你的任務選出它獨一無二的「真命模型」。選擇太多,於是焦慮?想知道如何權衡,可以參考這篇論文

簡而言之,SSD雖然速度很快,但可能無法以較高的準確度檢測出較小的物體,而Faster RCNN相對較慢且較大,但具有更高的精度。

TensorFlow物體識別API給我們提供了一系列預訓練模型。強烈推薦使用預訓練模型來初始化訓練 ,可以大大減少訓練時間。

MS COCO數據集的預訓練模型們:

擇一下載,解壓縮內容到你的基目錄。因為我更重視精確度,也需要合理的運行時間,我選擇Faster RCNN模型的ResNet-50版本。解壓縮之後,模型的checkpoints,frozen inference graph和pipeline.config文件get。

最後一步!定義pipeline.config文件里的training job,把此文件放到基目錄里。重要的是文件的最後幾行:你只需將model.ckpt,annotations/label_map.pbtxt,train.record,annotations/label_map.pbtxt,val.record設置到各自的文件位置即可。

gradient_clipping_by_norm: 10.0 fine_tune_checkpoint: "model.ckpt" from_detection_checkpoint: true num_steps: 200000}train_input_reader { label_map_path: "annotations/label_map.pbtxt" tf_record_input_reader { input_path: "train.record" }}eval_config { num_examples: 8000 max_evals: 10 use_moving_averages: false}eval_input_reader { label_map_path: "annotations/label_map.pbtxt" shuffle: false num_epochs: 1 num_readers: 1 tf_record_input_reader { input_path: "val.record" }}

如果你有相關經驗,你可以為模型設置最佳超參數。 TensorFlow團隊在這裡 給出了一些相當簡短的指南。

python object_detection/train.py --logtostderr --pipeline_config_path=pipeline.config --train_dir=train

我的筆記本電腦GPU無法處理模型的大小(Nvidia 950M,2GB),所以我不得不在CPU上運行它。 我的設備每步需要大約7-13秒。 經過大約10000個極其痛苦的步驟之後,該模型達到了相當好的準確性。 在達到2萬步之後,我停止了訓練,僅僅因為它已經花了兩天時間。

通過修改model.ckpt中的「fine_tune_checkpoint」屬性為model.ckpt-xxxx,其中xxxx代表已保存的checkpoints的全局步驟編號,你可以從checkpoint恢復訓練。

導出模型進行推理

只訓練不拿來用就沒意思啦。API又來幫忙啦。但有一個問題:推理模塊需要一個frozen graph模型作為輸入。 不要擔心:使用以下命令,可以將訓練好的模型導出為frozen graph模型。

python object_detection/export_inference_graph.py --input_type=image_tensor --pipeline_config_path=pipeline.config --trained_checkpoint_prefix=train/model.ckpt-xxxxx --output_directory=output

太棒辣!你將會得到一個叫做frozen_inference_graph.pb 的文件和一堆checkpoint文件。

你可以在我的GitHub倉庫找到一個名為inference.py的文件,用它來測試或運行你的物體識別模塊。你可以輸輸入以下命令來執行它:

python object_detection/inference.py --input_dir={PATH} --output_dir={PATH} --label_map={PATH} --frozen_graph={PATH} --num_output_classes={NUM}

將{PATH} 替換為相應文件/目錄的文件名或路徑。將 {NUM} 替換為你為你的模型定義的識別對象數量(在我的情況下為3)。

最終成果

最終的呈現結果就如開頭的視頻那樣,視頻著重演示了找球能力。簡直比單純的找球手還棒棒!只能說是非常炫酷了。

斯萊特林現在有一個強大的競爭對手了。

弱弱來個安利

機器學習工程師 | Udacity?

cn.udacity.com圖標深度學習 | Udacity?

cn.udacity.com圖標


推薦閱讀:

TAG:深度學習DeepLearning | 哈利·波特 | TensorFlow |