蘑菇街自研服務框架如何提升在線推理效率?

在線服務(Online Serving),又稱爲在線推理(Inference),是機器學習工程中的重要的一環,通過在線服務,訓練出來的模型得以釋放價值。蘑菇街計算視覺擁有大幾十個服務,每天調用次數高達千萬級別。爲了高效穩定的支撐在線服務,蘑菇街技術團隊研發在線服務框架,它支持Tensorflow、Pytorch 和 Openvino 等深度學習框架,支持運行在 CPU 和 GPU 資源上,並且單顯卡支持運行多個模型,大大提升 GPU 資源利用率。

Online Serving 簡介

從本質而言,在線服務就是提供(http, rpc)等接口,用戶輸入 X, X 經過 pre-process 處理成符合模型輸入的參數,經由模型推理後得到 Y,Y 經過 post-process 處理成符合用戶認知的數據格式,最後將結果返回。

第 2 步和訓練中的 evalute/test 相應步驟幾乎一樣,只是在線推理下的 batch size 往往爲 1,遠遠小於訓練過程中的 batch size,故在線推理下的顯卡和顯存的利用率相對訓練更低。

1. X = pre_process(X)
2. Y = model.predict(X)
3. Y = post_process(Y)

以 mnist 爲例,其模型輸入爲 28 * 28 的圖片,模型輸出爲 1 * 10 的向量。從用戶體驗角度來看,客戶端更期望的輸入是一張圖片的 URL,返回結果是預測的數字,相比 10 維向量,數字更符合人類的認知。所以從易用性出發,在線服務不僅需要預測模塊(2),還需要預處理(1)和後處理模塊(3),方能爲用戶提供友好的服務。很遺憾的是,當前大部分開源的在線服務框架或者雲服務,僅僅提供了模型預測功能。

開源的 Online Serving 現狀

縱觀業界,開源的在線服務項目可謂形態靈活、百花齊放,但也呈現出微服務化、容器化部署的特點,本節選擇幾個具備代表性的開源項目進行分析。

Tensorflow

TensorFlow Serving is a flexible, high-performance serving system for machine learning models, designed for production environments.

正如官網所述,基於 C++ 編寫的 Tensorflow Serving 註定是一個高性能的在線服務系統框架,它支持 Tensorflow,Pytorch 等深度學習框架訓練導出的模型(通常基於 ONNX 完成模型轉換),而且部署非常方便,支持多個版本的模型,詳細的例子請見官網。特別在結合 K8S + Docker,完美的解決了資源管理和調度、服務彈性部署、服務發現等痛點,從而對外提供穩定可靠的服務。

Tensorflow Serving 的缺點也很明顯,正如本文第一節所講,它缺乏預處理和後處理相關的邏輯,所以影響用戶體驗,這點由官網樣例也能看出來,用戶需要關注調用 API 時傳入的數據及其維度和格式,解析 protobuf 等等。另外一個痛點是 Tensorflow 對其它深度學習框架生成的模型不夠友好,比如卷積和池化時的 padding 參數,不同的深度學習框架的存在差異,例如博文 Running Pytorch models in Tensorflow

Pytorch

對於 Pytorch 的 Online Serving,社區並沒有提供相關的框架,僅在官網文檔 model-serving-in-pyorch 給出了一個概述,爲不同場景下的 Online Serving 介紹了一些可行性的方案。

KFServing

KFServing 是 Kubeflow 的子項目,旨在 Kubernetes 的基礎上提供 Serverless 的在線推理服務,它支持多種機器學習框架,具備彈性伸縮、服務治理、A/B Test、金絲雀發佈等重要功能,允許用戶擴展 pre-processing、post-processing 邏輯,從而對外提供完整易用的服務,它主要有兩部分組成:

  • KFServing Controller:基於 K8S 定義了在線推理服務相關資源 K8S CRD(Custom Resource Definition) 和生命週期管理 API,這部分主要爲 golang 代碼。
  • Model Server: 基於 tornado 提供了一套 Restful Web 框架,支持 Tensorflow、Pytorch 等深度學習框架,這部分爲 Python 代碼。

如上圖所示,KFServing 的弱點也很明顯,深度依賴 Knative、Istio 等模塊,這些模塊比較複雜,且穩定性有待商榷,其次 200+Stars 的 KFServing 的成熟度亦欠佳。但長遠來看,待 Knative、Istio 穩定和普及後,KFServing 或將釋放更大能力。

蘑菇街 Online Serving 實踐

蘑菇街計算視覺有大幾十個在線服務(模型),其中不少運行在 GPU 容器上,每個服務的業務代碼通常在數百行以內,依賴 Pytorch、Tensorflow 和 Openvino 等框架完成推理。因此,建設一個易用、支持多深度學習框架、支持異構計算的統一在線服務,將大大提升效率和降低維護成本。

基本需求

易用性主要體現在兩方面,一方面是對客戶端要提供簡單易用的 API,即 HTTP Restful API,由於公司所有圖片均存儲在對象存儲上,故用戶只需要在 body 中提供圖片的 URL 即可,圖片的下載和解析在服務端完成;另外一方面是在線服務模塊對算法同學要簡單易用,以便快速迭代上線。

此外,在線服務模塊不僅需要支持 Tensorflow、Pytorch 和 Openvino 等深度學習框架,還需要同時支持 GPU 和 CPU 計算資源。Batch size 爲 1 下的推理無法充分利用 GPU 資源,我們通過單卡運行多個服務以提升 GPU 資源利用率。

最後,支持彈性部署、完善的業務層面的監控和服務發現等功能也非常重要。

技術選型和實現

本節介紹蘑菇街計算視覺在線推理的實踐,包括技術選型、架構和一些經驗等。

語言和 Web 框架

Java 是公司的"標準"語言,幾乎所有的基礎技術體系均是基於 Java 建設。但是在機器學習領域,Python 有着無可比擬的豐富的生態,包括主流的深度學習框架和大量的圖像數學庫。採用 Java 實現在線推理通常碰到對應庫和函數缺失的情況,甚至導致準確性下降的問題。其次 Java 並非算法同學常用的語言,導致經常需要工程同學協助實現對應的邏輯,帶來大量的溝通成本,影響上線效率。雖然 Java 性能較 Python 高,但權衡準確性和效率等因素,Python 是更好的選擇。

現在分析 Python 下的併發方式,主要從進程、線程和協程擇優用之。由於 CUDA 對多進程支持不夠成熟,容易出現耗盡顯存等異常情況,故排除多進程;受 GIL(Global Interpreter Lock) 鎖的限制,線程的性能要低於協程,故最終採用高效和成熟的 Gevent 協程庫提供併發,選擇簡單易用的 Flask 作爲 WSGI Web 框架,對外提供 Restful API。

爲了進一步提升易用性,可將下載圖片、統計等通用功能放置在 WSGI 的 Middleware 中。每當新增一個在線服務,只需增加一個繼承基礎類的新類,並在新類中實現 load 和 handler 兩個方法即可。其中 load 方法僅在服務啓動時執行一次,用於完成下載和加載模型、創建 session 等初始化工作;handler 方法實現具體的業務邏輯,通常包含預處理、預測和後處理三個步驟,每當一個請求到來,啓動一個協程執行對應在線服務的 handler 方法,並將結果返回給客戶端。通過給每個 handler 方法加上各自的互斥鎖,使得同一個服務只能串行執行,以避免相同服務並行帶來的可能的 GPU 異常,但是不同服務之間可並行執行。

容器化部署

我們採用容器化部署在線服務,針對 CPU 和 GPU 異構資源製作了對應的兩個鏡像。以 GPU 場景爲例,所有服務所依賴的深度學習框架庫、圖像庫、數學庫和 GPU 驅動相關已在鏡像中安裝,雖然這個鏡像高達十幾個 GB,但是便於使用,特別是算法同學新增一個模型時,極少會碰到庫缺失的情況。在線推理的邏輯較訓練相比簡單許多,對各種庫的版本要求比較寬鬆,通常只需一個版本的鏡像即可滿足所有 GPU 在線服務的依賴要求。

K8S 不支持顯卡虛擬化,且業內暫無開源的解決方案,只能爲每個 GPU 容器分配一張真實的顯卡,每個容器運行一個在線服務進程,它支持加載多個服務(模型),其中服務的數量由模型大小和顯存大小決定。通常而言,在線推理的顯卡,如 P4 通常爲 8GB,計算視覺的模型大小普遍在小几百 MB,一張 8G 的 P4 GPU 可加載十幾個服務,且不同服務之間可以並行執行,故 GPU 資源可以得到較爲充分的利用,使用率可達到 40%,單卡能支持幾十 QPS。

藉助 K8S 的 StatefulSet,進一步簡化部署和彈性擴縮,同時提升可靠性。

架構

每當在線推理的容器啓動時,它根據環境變量決定加載哪些服務(模型),依次從模型管理中心下載和加載模型,完成初始化工作後再註冊路由和 API,最後成功啓動的服務註冊到註冊中心。客戶端根據服務名稱從註冊中心獲取 IP 信息,然後訪問對應的服務。Middleware 的統計模塊會採集 QPS 等數據,並上報到監控中心。

模型管理主要用於存儲和管理模型,常用的存儲媒介有對象存儲、HDFS 等,蘑菇街採用 HDFS 作爲模型的存儲中心。監控模塊因不同公司而異,通常的做法是通過 Kafka 或者監控服務的 API 上傳統計數據。註冊中心主要用於服務發現,Zookeeper 或 Redis 是常用的方案。我們之所以需要註冊中心,是因爲有大幾十個服務(模型),同時單個 pod 加載多個服務並且這些服務經常增刪,所以 K8S 原生的服務發現功能無法滿足需求。但是如果您的服務數量少或者極少變更,那麼採用 K8S 的服務發現即可,甚至可部署在虛擬機上,不需要註冊中心。

爲了提升性能,可以在多個層次增加 Cache。對於業務存在週期性調用的服務,可以將結果緩存在 Redis;計算視覺通常依賴多個在線服務完成一件事情,比如搭配購業務,它需要依次調用目標檢測、服裝粗分類和服裝細分類的服務,將圖片緩存在內存可在一定概率上避免重複下載,降低 RTT。對於視頻類服務,由於視頻較大,可以採用本地磁盤緩存視頻和拆幀後的圖片。

注意事項

在單卡支持運行多個服務(模型)時,主要遇到兩個問題:

  • 同一個 GPU 上運行同時不同框架的服務時,例如 Tensorflow 和 Pytorch,容易造成異常,故同一個 Pod 只能加載相同框架的服務。
  • 同一個進程下 Tensorflow 的 config.gpu_options.per_process_gpu_memory_fraction 僅在第一次初始化 session 時生效,爲了避免該值設置過小引起異常,建議TF Pod 下的所有服務的參數均設置爲 0.95。

作者簡介

範德良,花名攬勝,蘑菇街技術專家,在 IaaS、PaaS 和機器學習工程領域具有豐富經驗。

楊立,花名浮塵,蘑菇街智能投放平臺小組負責人,深度參與了蘑菇街算法在線服務體系0到1的建設,主導了搜索推薦鏈路算法相關核心系統平臺的建設與持續演進。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章