沒人告訴你的大規模部署AI高效流程!

如今有許多關於 AI 的教程。好比如何進行目標檢測、圖像分類、NLP 以及構建聊天機器人等,不勝枚舉。瀏覽器

但當我查找如何正確擴展 AI 的內容時,卻發現少得可憐。更使人驚訝的是,現有的極少數資源在反覆強調相同的幾點:緩存

  • 用像 TensorFlow 這樣的可擴展框架構建模型。
  • 將其打包到客戶端(TF.js、TF Lite、TF-slim 等)或部署爲基於容器的微服務。

我對第二點更感興趣,由於我已經開發好了一個模型,但令我驚訝的是,沒有任何關於如何實現第二點的細節,而關於每一個解決方案缺點的信息則更少。研究了幾天並在 Crane.ai 上擴展 AI 以後,我整理了一些關於如何部署這些方法、這些方法的缺點以及如何在低級別優化 TensorFlow 模型的內容。安全

將模型打包到客戶端——這種方法太糟了!服務器

最經常使用的方法之一是用像 TensorFlow.js、TF Lite 或 TensorFlow Slim 這樣的工具將 AI 打包到你所選擇的客戶端中。我不會詳細介紹這些框架如何運行,但我會重點說明它們的缺點。網絡

  • 計算能力。部署這些模型的問題在於它們須要大量的內存(我指的是移動應用程序或瀏覽器的限制,即 > 1-2GB RAM)。許多手機都沒有這樣的計算能力,而桌面瀏覽器又會延遲 UI 線程,同時也下降了用戶的計算機速度,要打開瀏覽器,還要打開風扇等等。
  • 推斷時間。當你在計算能力未知的設備上運行模型時,推斷時間通常也是未知的;這些設備不是 GPU 驅動的高 RAM、高 CPU 機器,它們只是在普通計算機上運行的手機、瀏覽器和桌面應用程序。較大模型的推斷時間能夠輕鬆超過一分鐘時間,而從用戶體驗的角度來看,這並不可行。

  • 大文件。不幸的是大多數模型都存儲在至關大的文件中(我指的是數10、數百 MB)。所以,加載這些文件速度很慢,須要的內存量比較大,也大幅增長了應用程序包的大小。
  • 不安全。除非你用的是開源模型,不然你要相對保密你的 AI 模型和預訓練檢查點。然而,當你將模型打包進應用程序時,不只你的推斷代碼容易被反編譯,並且在應用程序包裏的預訓練檢查點也很容易被竊取。
  • 難以更新。若是想要更新你的模型,在客戶端中你有兩個選擇。要麼經過集中管理器(即 Play Store、App Store 等)發佈更新,這會致使頻繁的大型更新(對用戶而言這是很煩人的,並且用戶能夠根據設置打斷這個過程,或者壓根就不開啓更新)。或者應用程序自己能夠獲取新模型的檢查點和元數據。後者聽起來要好得多,可是這意味着你可能要在用戶鏈接不穩定的狀況下下載 100MB 以上的文件,這可能須要一段時間,因此你的應用程序至少要在後臺開到下載過程完成,並且會產生很高的互聯網輸出成本(這取決於你的雲計算)。
  • 缺少可訓練性。針對新用戶的數據訓練的模型提供了必定程度的個性化,同時提升了準確率,並創建了核心的高信號數據集。不幸的是大部分設備缺少訓練模型的計算能力,即使它們的計算能力夠了,也沒法將訓練效果傳遞到服務器或其餘運行該應用程序的設備。

這些缺點使得在客戶端上部署和維護大型神經網絡幾乎不可能,因此咱們從擴展模型的備選項中排除這一項。架構

部署爲雲端點框架

大規模部署AI高效流程

雲是能夠大規模部署模型的強大工具。你能夠根據須要定製環境、容器化應用程序、當即水平擴展應用程序,同時提供足以和大公司媲美的 SLA 和運行時間。微服務

對大部分 TensorFlow 模型來講,部署流程是相同的:工具

  • 將圖像固化爲 Protobuf 二進制文件
  • 調整推斷代碼,使它能夠處理固化的圖
  • 容器化應用程序
  • 在最上面加上 API 層

第一部分相對簡單。「固化」圖要用全部命名節點、權重、架構和檢查點元數據建立一個 protobuf 二進制文件。這一步能夠用多種工具實現,最經常使用的是 TF 本身的工具,它能夠固化任何給定輸出節點名字的圖。性能

大規模部署AI高效流程

調整推斷代碼也不難。在大多數狀況下,feed_dict 是不變的,主要區別在於添加了加載模型的代碼,也許還有輸出節點的規範。

容器化也很簡單——只要在 Dockerfile 中設置環境便可。而當咱們開始添加 API 層時,事情就會變得混亂。一般用這兩種方法:

部署能夠運行推斷腳本的擴展容器。這些容器根據輸入運行腳本,腳本啓動一個會話並執行推斷,再經過管道返回輸出結果。這是頗有問題的:對大多數雲供應商而言添加一個能夠操縱容器和管道進出的 API 層並不容易(例如,AWS 有 API 網關,但它並不像你指望的那麼方便),並且這種方法是你能夠採用的效率最低的方法。這裏的問題是你在啓動容器、分配硬件、啓動會話以及推斷時損失的寶貴時間。若是你讓 stdin 開着並保持管道輸出,那麼你的腳本就會加速可是會失去可擴展性(如今你已經鏈接到容器的 STDIN,而它沒法接受多個請求)。

部署運行 API 層的擴展容器。儘管在架構上類似,但因爲如下幾個緣由,這種方法效率更高。將 API 層內置在容器中,能夠緩解以前提出的大多數問題。雖然這須要更多資源,但它已經用了最少資源並且沒有垂直擴展;它容許每一個容器保持運行狀態,並且因爲這種狀況下 API 是分散的,所以能夠將特定的 stdin/stout 鏈接到主要的請求路由器上。這意味着省去了啓動時間,能夠在服務多個請求的同時維持速度並保證水平擴展。能夠用負載平衡器集中容器,並用 Kubernetes 保證近乎 100% 的運行時間並管理集羣。這種方式簡單且有效。

大規模部署AI高效流程

部署集羣!

經過容器集羣分散 API 的主要缺點在於計算成本會相對較快地累積起來。不幸的是這在 AI 中是不可避免的,但有一些方法能夠緩解這一問題。

  • 重複使用會話。集羣會根據負載成比例地增加和收縮,所以你的目標是最小化執行推斷的時間,使容器能夠釋放出來處理另外的請求。實現這一想法的方法是初始化 tf.Session 和 tf.Graph 後就將它們存儲起來並將它們做爲全局變量傳遞,以達到重複使用 tf.Session 和 tf.Graph 的目的。對 TF 來講,這一舉措能夠減小啓動會話和構建圖的時間,從而大大提升推斷任務的速度。即使是單個容器,這個方法也是有效的,並且這一技術被普遍用於資源再分配最小化和效率最大化。
  • 緩存輸入,若是可能的話還要緩存輸出。在 AI 中,動態規劃範式在 AI 中是最重要的。經過緩存輸入,你能夠節省預處理輸入或從遠程得到輸入的時間;經過緩存輸出,你能夠節省運行推斷的時間。這在 Python 中很容易實現,但你要視本身的狀況而定。一般,你的模型會隨着時間的推移變得更好,但這會很大程度上影響你的輸出緩存機制。我本身的系統用的是我所謂的「80-20」規則。當模型準確率低於 80% 時,我不會緩存任何輸出;一旦準確率到了 80%,就開始緩存並設置爲在準確率到必定值(而不是某個時間點)的時候中止緩存。這樣,隨着模型變得愈來愈準確,輸出也會發生變化,可是在「80-20」緩存中,性能和速度之間存在的權衡更少。

大規模部署AI高效流程

使用任務隊列。通常須要運行或大或小的推斷任務(在咱們的例子中是較大和較小、複雜和簡單的圖像)。對 UX 來講,使用堆隊列(heap queue)可能更好,它會優先處理小一些的任務,因此要運行簡單步驟的用戶只要等這一步結束就好了,而沒必要等另外一個用戶的更大推斷任務先完成。(也許你會想我在這裏爲何不用水平擴展,你能夠這麼作可是會增長計算成本)。

在帶有任務隊列的專用 GPU 上訓練模型。訓練是一項長期、困難的任務,它須要大量可用的資源,並且模型在訓練過程當中沒法使用。若是你要將每一個交互返回到模型中進行訓練,請考慮在單獨的服務器或 GPU 上運行。一旦訓練結束,你就能夠將模型(在 AWS 中,你能夠將模型 repo 集中在 S3 中)部署到容器中了。

結論

大規模部署AI高效流程

深思熟慮後,咱們提出了一個大規模部署 AI 的高效工做流程:

  • 固化圖並將推斷封裝在 API 下
  • 重複使用會話和圖,緩存輸入和輸出
  • 用 Docker 容器化應用程序(包括 API 層)
  • 將大規模應用程序與 Kubernetes 一塊兒部署在你選擇的雲上
  • 將訓練從推斷中分離出來
  • 創建任務隊列,將較小的任務確立爲優先級

使用這些技術,你就能夠在成本最小、速度和效率最大的狀況下大規模部署 AI。

感興趣的能夠本身來個人Java架構羣,能夠獲取免費的學習資料,羣號:855801563 對Java技術,架構技術感興趣的同窗,歡迎加羣,一塊兒學習,相互討論。

相關文章
相關標籤/搜索