讀懂本文讓你和深度學習模型“官宣”

人工智慧頻道發表於2018-10-26

近年來,學術界和工業界的研究人員在深度學習領域進行了許多令人興奮和開創性的研究。他們開發了許多強大得令人難以置信的新模型。然而,這些研究成果的大部分(少數科技巨頭除外)仍然只是停留在實驗階段,而不是成為生產應用程式的一部分。儘管新的發現/發明源源不斷,但在生產過程中,使用這些模型仍然非常困難(甚至做相關方面的論文也是如此),部署機器學習模型仍然是一個重大挑戰。在本文中,我將概述各種"生產型"機器學習模型的方法,並權衡它們各自的優缺點,但不會涉及太多細節。並將用具體的程式碼和示例來探討這些方法。

讀懂本文讓你和深度學習模型“官宣”


挑戰

我們假設我們已經訓練了模型,或者已經從網際網路上獲得了訓練過的權重。為了在應用程式中使用你的模型,你必須:

  1. 載入模型的權重

  2. 對資料進行預處理

  3. 進行測試

  4. 處理預測響應資料

聽起來很簡單嗎?實際上,這個過程非常複雜。

與許多事情一樣,對於將機器學習模型部署到生產環境的最佳方式,沒有一個明確的答案。你應該問自己的問題是:

  1. 我的要求是什麼?(例如,你希望每秒有多少請求,需要的延遲是什麼…等等)

  2. 我將如何評估模型在生產中的效能?(我將如何收集和儲存來自互動的額外資料)

  3. 我打算多久訓練一次我的模型?

  4. 資料預處理需要什麼?

  5. 生產輸入資料的格式是否與模型訓練資料有很大的差異?它是分批生產還是成批生產?

  6. 模型需要離線執行嗎?

這些都是在部署模型之前必須想到的基本問題。

直接將模型載入到應用程式中

這個選項本質上認為模型是整個應用程式的一部分,因此才在應用程式中載入它。這種方法在某些情況下比其他方法更容易上手。

例如,如果核心應用程式本身是用Python編寫的,那麼這個過程(即,將模型載入到應用程式中)會很順利。總的來說,它通常需要向設定/配置檔案新增依賴項,並修改你的預測值函式,以便通過使用者的互動呼叫。總的來說,模型作為應用程式的一部分載入,所有依賴項都必須包含在應用程式中。

如果應用程式不是用Python編寫的,這個過程就會變得更加困難。例如,我現在還沒有將PyTorch或Caffe載入到Java程式中的好方法。即使是擁有Java庫的Tensorflow也需要編寫大量附加程式碼才能完全整合到應用程式中。而且,這並沒有明確地解決可伸縮性問題。

然而,正如前面所述,當你希望快速部署用Python編寫的應用程式時,這種方法仍然是有益的。因為對於沒有網際網路連線的裝置來說,它仍然是更好的選擇之一。

呼叫API

第二個選擇涉及建立API並從應用程式呼叫API。這可以通過許多不同的方式來實現。我在這裡詳細介紹了最常見的方法。

Kubernetes

Docker在許多方面似乎是部署機器學習模型最好的選擇。模型及其所有依賴項可以整齊地打包在一個容器中。此外,伺服器可以通過在需要時新增更多Docker容器來自動擴充套件。Kubernetes是管理Docker容器的最佳方法之一。

最近,Kubernetes推出了旨在將機器學習引入Kubernetes框架的Kubeflow。Kubeflow試圖使訓練、測試和部署模型以及收集評估指標變得容易(https://techcrunch.com/2018/05/04/google-kubeflow-machine-learning-for-kubernetes-begins-to-take-shape/)。由於它可能相當複雜,我們現在只需要瞭解,它是一個全面的包,旨在使大規模開發和部署機器學習微服務變得容易。

使用Flask/Django定製REST-API

另一個選擇是從頭建立你自己的REST-API(這個選項也可以與Docker結合使用),這取決於你對API的熟悉程度(https://blog.hyperiondev.com/index.php/2018/02/01/deploy-machine-learning-model-flask-api/)。這可以用Flask比較容易做到。我將不再詳細介紹如何用它進行此工作,因為有許多現有的教程正好涵蓋了這個主題。根據請求的數量,你通常可以通過調整Flask以做出改變。

然而,由於ML框架、載入時間和特定模型預處理需求的不同,我們的工作往往會變得一團糟。目前,我正在開發一個與模型無關的例項化類,以便與Flask/Django一起使用,目的是容易地使用模型,併為以後的模型提供一個標準化的模板(https://github.com/isaacmg/model_agnostic_prediction)。因此,不必為不同的模型記住和實現不同的函式,你可以呼叫model.preprocess()和model.predict(),而不管後端是什麼。

AWS Lambda/ Serverless

AWS Lambda是另一種可能的選擇。你可以閱讀AWS關於如何設定此功能的文章(https://aws.amazon.com/blogs/machine-learning/how-to-deploy-deep-learning-models-with-aws-lambda-and-tensorflow/)。在"機器學習"中有一篇在Caffe2中使用AWS lambda的好文章(https://machinelearnings.co/serving-pytorch-models-on-aws-lambda-with-caffe2-onnx-7b096806cfac)。

其他方法

Apache Beam -我對這種方法不太瞭解,但這種方法似乎涉及到使用Beam進行模型預處理,然後使用Tensorflow(目前只支援框架)進行實際預測。有關更多資訊,請點選連結:https://qcon.ai/system/files/presentation-slides/simplifying_ml_workflows_with_apache_beam.pdf 。

Spark/ Flink

Spark和Flink等幾個主要的資料處理框架正在開發幫助部署機器學習模型的包(https://github.com/FlinkML/flink-tensorflow)(https://github.com/databricks/spark-deep-learning)。此外,雅虎最近釋出了自己的庫,允許分散式訓練並提供相關服務(https://github.com/yahoo/TensorFlowOnSpark)。

讀懂本文讓你和深度學習模型“官宣”


有段時間,由於大量的工作,我陷入了困境,在此期間Oracle出現了一個名為GraphPipe的新模型部署框架。那時候我本來打算選用Kubeflow,因為它有很多不錯的特性,但是我發現GraphPipe更容易使用,至少從它的網站上看,它比JSON型別的api(比如Kubeflow)快得多(https://blogs.oracle.com/developers/introducing-graphpipe)。

所以我決定在這裡介紹一個使用GraphPipe和與我自己的模型無關(MA)的庫(現在它包含了對GraphPipe的支援)部署經過訓練的PyTorch模型的示例。在這個例子中,我選擇了ChexNet和arroweng的實現,後者在GitHub上公開使用(https://github.com/arnoweng/CheXNet)。

1.重構程式碼以支援"單一示例"處理(或生產所需的任何模式)。

在幾乎所有情況下,第一步都是相同的。它通常也是最耗時和最辛苦的。這通常需要非常仔細地閱讀實現程式碼。我的model_agnostic類的目的是讓這個過程稍微簡單一些(不幸的是,它仍然很糟糕)。

1(a)載入模型權重

在我們嘗試重構之前,我們需要確保我們可以載入模型權重(令人驚訝的是,這導致了比你想象中更多的問題)。為此,我們從MA中子類化PytorchModel。現在我們不需要擔心預處理和process_result步驟。相反,我們將只關注於載入模型權重。

讀懂本文讓你和深度學習模型“官宣”


讀懂本文讓你和深度學習模型“官宣”


MA通常有三種方法來實現,分別是__init__(通常只是對super的呼叫)、create_model和預處理。根據你的模型和設定,你可能還希望實現process_result。

裝載PyTorch模型有多種方式,MA支援兩種方式。第一種方法是,你將模型儲存到torch.save(the_model, some_path)。在實踐中,這種情況非常少見,相反,大多數時候你只儲存state_dict,而不是將整個模型儲存到torch.save(the_model.state_dict(),path))中。在這裡arroweng提供了儲存的state_dict,所以我們必須呼叫create_model。而ChexNet實際上只是一種DenseNet121。因此,我們在create_model中需要做的就是返回一個DenseNet121(https://github.com/isaacmg/s2i_pytorch_chex/blob/master/densenet/dense_ne.py)。PytorchModel中的rest MA處理方法就好似將state_dict轉換為一個可用的CPU格式(反之亦然)。最後,我們將訓練模式設定為false,因為我們並不打算繼續下去。

讀懂本文讓你和深度學習模型“官宣”


1(b)實現預處理和process_result

為了測試這個設定是否有效,我們需要在一些資料上執行建立的模型。這些相同的函式也將在稍後部署模型時使用。所以現在是時候讓他們儘可能"瘦身"了。這通常是比較耗時的步驟之一,因為程式碼經常分批評估模型。此外,當我們需要使用單獨的示例來執行模型時,模型通常有一個資料載入器。此過程將根據你的特定模型或用例而有所不同。有趣的是,對於這個特殊的模型,arroweng甚至在測試期間使用TenCrop來增加資料。因此,在我的預處理方法中,我決定有一個增強引數,使用者可以標記為true或not。

讀懂本文讓你和深度學習模型“官宣”


讀懂本文讓你和深度學習模型“官宣”


讀懂本文讓你和深度學習模型“官宣”


程式碼為預處理部分的演算法。GitHub:https://gist.github.com/isaacmg/767f069e8d25a22dbf2d88abda288ad3/

讀懂本文讓你和深度學習模型“官宣”


2.將PyTorch轉換為Caffe2

這個部分相對簡單,並且在PyTorch網站上有詳細的文件說明。基本上,它包括Caffe2追蹤模型的PyTorch執行情況。

讀懂本文讓你和深度學習模型“官宣”


這是我直接從PyTorch網站的官方教程中獲得的程式碼(https://pytorch.org/tutorials/advanced/super_resolution_with_caffe2.html)。

為了測試模型是否被成功地轉換為Caffe2,我使用了以下程式碼。

讀懂本文讓你和深度學習模型“官宣”


你可以使用NumPY測試框架來確保PyTorch執行的結果大致等於Caffe2執行的結果(無論需要多少小數位數)。

如果一切正常,下面的程式碼執行時應該沒有錯誤。

3.服務與GraphPipe

3(a)執行GraphPipe docker容器

現在,將儲存的Caffe ONNX檔案(在本例中是chexnet-py.onnx)儲存到雲中或本地目錄中。然後執行以下命令。

讀懂本文讓你和深度學習模型“官宣”


這個命令的作用是將GraphPipe拖放到nx CPU docker、模型和其他資訊中,以執行容器。"Value inputs"是輸入值的維度。

讀懂本文讓你和深度學習模型“官宣”


因此,在本例中(如果計劃不使用裁剪增強),輸入將是批處理大小1、三個通道(即RGB)和224x224影像大小。如果一切順利,你應該在終端中使用"INFO[0005] Listening on 0.0.0.0:9000"之類的內容。

3(b)定義GraphPipe服務類

讀懂本文讓你和深度學習模型“官宣”


讀懂本文讓你和深度學習模型“官宣”


讀懂本文讓你和深度學習模型“官宣”


我在這裡出了些小問題,你可以點選連結檢視完整檢視(https://gist.github.com/isaacmg/554be1338025bc4529d2bb7cffda9d49)。

因此,預處理函式將保持不變,如果還沒有定義process_result函式,則需要定義process_result函式。MA將處理GraphPipe docker容器的所有幕後呼叫。現在要使用它,你所需要的是以下內容。

讀懂本文讓你和深度學習模型“官宣”


要建立ChexNet GraphPipe物件,只需傳入容器的url。然後簡單地執行標準模型無關命令。

現在你可以在任何Django或Flask API中來完成部署。

結語

現在,你可能會想,為什麼這比簡單地在Flask/Django REST API中執行PyTorch更好呢?答案是(1)這種方法通常更快,因為GraphPipe優化了模型預測階段,(2)該方法具有高度的可伸縮性,(3)任何應用程式都可以通過任何語言呼叫此API(假設你可以執行等效的預處理)。在我看來,最大的好處是(2)。

我的成品是GitHub:https://github.com/isaacmg/ml_serving_flask。

更多資源

graphpipe官方網址:https://oracle.github.io/graphpipe/#/

graphpipe釋出的文章:https://blogs.oracle.com/developers/introducing-graphpipe

散亂模型載入程式碼:https://github.com/isaacmg/s2i_pytorch_chex


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31545819/viewspace-2217708/,如需轉載,請註明出處,否則將追究法律責任。

相關文章