天天看點

手把手:我的深度學習模型訓練好了,然後要做啥?

本文講的是如何快速而不求完美地部署一個訓練好的機器學習模型并應用到實際中。如果你已經成功地使用諸如Tensorflow或Caffe這樣的架構訓練好了一個機器學習模型,現在你正在試圖讓這個模型能夠快速的示範,那麼讀這篇文章就對了。

閱讀時長: 10-15分鐘

使用前檢查清單

檢查tensorflow的安裝

從 stdin 運作線上分類

在本地運作分類

把分類器放到寫死(hardcoded)的代理

把分類器放到有服務發現(service discovery)的代理

用一個僞DNS調用分類器

機器學習的實際應用

當我們第一次進入Hive的機器學習空間時,針對我們的實際應用場景,我們已經擁有了數百萬張準确标記的圖像,這些圖像使我們能夠在一周之内,從頭開始訓練最先進的深度卷積神經網絡圖像分類模型(即随機權重)。然而,在更典型的應用場景中,圖像的數量級通常隻有數百幅,這種情況下,我建議微調現有的模型。比如,https://www.tensorflow.org/tutorials/image_retraining有一個關于如何微調Imagenet模型(在1.2M圖像上訓練1000個類别)以對花進行分類的樣本資料集(3647個圖像, 5個類别)。

上面的Tensorflow教程簡要而言,是在安裝bazel和tensorflow之後,需要運作以下代碼,用大約30分鐘的來模組化,5分鐘來訓練:

或者,如果你安裝了Docker,則可以使用以下預建構的Docker鏡像:

這将進入容器内部的互動式shell中并運作上述指令; 如果你願意的話,也可以按照容器内的其餘部分進行操作。

現在,tensorflow已經将模型資訊儲存到/tmp/output_graph.pb和/tmp/output_labels.txt中,這些作為指令行參數傳遞給label_image.py腳本。Google的image_recognition教程也連結到另一個腳本,但是這裡我們仍将使用label_image.py。

将本地運作轉換為線上運作(Tensorflow)

如果我們隻想接受來自标準輸入的檔案名,每行一個,我們就可以很容易地進行“線上”運作:

然而,從性能的角度來看這樣糟糕透了—— 每一個輸入都要重新加載神經網絡,權重,整個Tensorflow架構和python本身!

當然可以改進。先修改label_image.py 腳本。對我而言,這個腳本的位置在:

in bazel-bin/tensorflow/examples/image_retraining/label_image.runfiles/org_tensorflow/tensorflow/examples/image_retraining/label_image.py.

修改如下:

修改後馬上快了很多,但這還不是最好。

原因在于用with tf.Session()建構對話。Tensorflow本質上是在每次調用run_graph時将所有的計算加載到記憶體中。一旦開始嘗試在GPU上進行運算,這一點就會變得很明顯——可以看到GPU記憶體使用随着Tensorflow加載和解除安裝GPU的模型參數而上下波動。據我所知,這種結構并不存在于Caffe或Pytorch架構中。

解決方法是把with指令去掉,傳遞一個sess變量到run_graph:

如果你運作完這一段,你會發現每張圖隻需要大約0.1秒,對于線上應用來說已經夠快了。

将本地運作轉換為線上運作(其他ML架構)

Caffe使用net.forward代碼,很容易被放入一個可調用的架構中:see http://nbviewer.jupyter.org/github/BVLC/caffe/blob/master/examples/00-classification.ipynb

Mxnet也是非常獨特的:它實際上已經準備好了面向大衆的伺服器代碼。

部署

我們的計劃是,将這些代碼包裝到一個Flask應用程式中。如果你沒有聽說Flask,簡單解釋一下,Flask是一個非常輕量級的Python Web架構,它允許你以最少的工作啟動一個http api伺服器。

作為一個快速參考,這裡是一個Flask應用程式,它接收包含多部分表單資料的POST請求:

這裡是如何将相應的FLASK應用程式連接配接到上面的run_graph:

模型部署至此看起來還是相當不錯的。除了一點——需要FlASK和Tensorflow完全同步——Flask按照接收的順序一次處理一個請求,并且Tensorflow在進行圖像分類時完全占用線程。

速度瓶頸可能還是在實際的計算工作中,是以更新Flask包裝代碼沒有太多的意義。現在,也許這個代碼足以處理你的負載。

有兩種顯而易見的方法可以擴大請求的通量:通過增加勞工數量來橫向放大,這在下一節将會介紹,或者通過使用GPU和批處理邏輯來縱向擴充。實作後者需要一個能夠一次處理多個待處理請求的web伺服器,并決定是否繼續等待更大的批處理或将其發送到Tensorflow圖形線程進行分類,對于這個Flask應用程式是非常不适合的。有兩種可能性:使用Twisted + Klein來保留Python代碼,或者如果你更喜歡一流的事件循環支援,并且能夠連接配接到非Python ML架構(如Torch),則可以使用Node.js + ZeroMQ。

擴充:負載平衡和服務發現

那麼,假設現在你隻有一台伺服器來部署模型,由于它太慢了,或者我們的負載變得太高了,此時你想要啟動更多伺服器——如何在每個伺服器上配置設定請求?

正常的方法是添加一個代理層,也許是haproxy或nginx,它能夠平衡後端伺服器之間的負載,同時向用戶端呈現一個統一的接口。為了在本節稍後使用,以下是運作基本Node.js負載均衡器http代理的一些示例代碼:

為了自動檢測後端伺服器的數量和位置,人們通常使用“服務發現”工具,該工具可能與負載平衡器捆綁在一起,或者是分開的。一些知名例子的是Consul和Zookeeper。設定和學習使用它們不在本文的讨論範圍之内,是以我使用了一個非常基本的,通過node.js服務發現包seport實作的代理。

Proxy代碼:

Worker代碼:

然而,當應用于機器學習時,這個設定遇到了帶寬問題。

每秒幾十到幾百張圖像,這個系統就會成為網絡帶寬的瓶頸。在目前的設定中,所有的資料都必須通過我們的單個seaport 主節點,這也是呈現給用戶端的端點。

為了解決這個問題,我們需要我們的用戶端不要通路http://127.0.0.1:12480這個端點,而是要在後端伺服器之間通過自動輪換來通路。如果你懂網絡,一定會想:這不就是DNS幹的活嘛!

但是,設定自定義的DNS伺服器已經超出了本文的範圍。相反,通過更改用戶端以遵循兩步“手動DNS”協定,我們可以重新使用我們的基礎版的seaport 代理來實作用戶端直接連接配接到其伺服器的“點對點”協定:

Client代碼:

 結論和進一步閱讀

至此你的系統應該可以進入實際應用了,但它總是要發展的。本指南中未涉及幾個重要的主題:

新硬體上的自動部署和設定。

值得注意的工具包括Openstack / VMware(如果您使用的是自己的硬體),Chef / Puppet(用于安裝Docker并處理網絡路由)以及Docker(用于安裝Tensorflow,Python等)。

如果你在雲端,Kubernetes或Marathon / Mesos也很棒

模型版本管理

 一開始手動管理不難。

Tensorflow Serving是一個很好的工具,可以非常徹底地處理這個問題,以及批處理和整體部署。 缺點是設定和編寫用戶端代碼有點難,另外不支援Caffe / PyTorch。

如何将機器學習代碼從Matlab中遷移出來。

 在生産階段不要用Matlab

GPU驅動,Cuda,CUDNN

使用nvidia-docker,試試其它的線上Dockfiles。

後處理層。

一旦你在生産中得到了一些不同的ML模型,你可能會開始想要混合和比對不同的用例——隻有在模型B不确定的情況下才運作模型A,在Caffe中運作模型C并将結果傳遞給模型D在Tensorflow 等等。

原文釋出時間為:2018-02-7

本文作者:文摘菌