Sunfish:有贊智能平臺實踐

一、前言

‍‍機器學習/深度學習在有贊應用的越來越多,例如在營銷、推薦、風控等場景下都起着越來越重要的作用。對於深度學習在實際業務場景的落地來說,除了數據和算法,工程和系統上的支持同樣必不可少,這樣的支持包括模型的快速構建與評估,穩定的線上模型服務環境等等。爲此,我們開發了有贊智能平臺 Sunfish ,本文詳細介紹 Sunfish 的設計和實現。

二、背景

在有贊,機器學習/深度學習在各個業務場景下發揮着越來越重要的作用。這裏以推薦系統爲例,介紹一下深度學習的落地實踐。在之前的博客文章有贊推薦系統關鍵技術中介紹過有贊微商城個性化推薦系統。簡單來說,當用戶打開一個有商品推薦位的頁面時,推薦系統會根據用戶特徵按一定策略從商品池中選擇出一些候選的推薦商品,這個過程稱爲召回。然後,針對這些候選商品,需要分別對它們進行打分,選擇其中得分最高的商品,推薦給用戶,這個過程稱爲線上精排。這裏對某個商品進行打分的操作,就是在使用深度學習模型進行推理。

爲了實現個性化推薦系統中的線上精排服務,我們需要進行三個階段的工作。1.數據探測與準備;2.模型訓練與評估;3.模型服務部署。我們會在有贊大數據平臺上面進行數據探測與準備。在模型訓練與評估階段,算法同學需要選擇合適的特徵數據,進行適當的特徵變換,建立模型,調整超參,評估模型效果。這是一個不斷迭代的過程,直到訓練出效果滿足期望的模型。之後,要把模型發佈爲線上服務,接受線上模型推理請求。作爲一個線上服務,需要保證穩定較低的響應時間,平滑的版本升級,各服務版本的線上監控與評估等要求。爲了提升算法業務的開發效率,並提供穩定的算法服務環境,我們開發了 Sunfish ,從模型訓練到模型服務部署的一站式智能平臺。下文將詳細介紹 Sunfish 的設計與實現。

三、 Sunfish 功能架構

Sunfish 功能架構如下圖所示,主要分爲可視化模型訓練平臺和小盒子模型在線服務平臺兩個部分。

可視化訓練平臺主要提供了三個方面的支持:

  1. 可視化的快速建模。用戶通過拖拽式的構建實驗,只要配置相關訓練參數,運行實驗,即可開始進行模型訓練。在訓練過程中,用戶可以方便的查看訓練日誌,也可以通過可視化方式觀察訓練進度與模型效果收斂情況。用戶可以隨時停止實驗,爲節點設置超時時間。可只運行某個實驗節點,或是從某個實驗節點開始運行。由於訓練任務可能會運行較長時間,平臺保證了任務運行的容錯恢復,當某個集羣節點宕機時,任務會自動遷移並運行。
  2. 算法開發與分享。對於資深的算法工程師,並不滿足於使用已有組件進行拖拽式建模。平臺提供 Notebook 建模方式,開發自定義算法。同時,還可以將自己開發的算法發佈爲平臺組件。成爲其他算法人員的建模工具。
  3. 模型管理與發佈。平臺會自動保存實驗運行生成的模型,用戶也可以上傳自己已有的模型。用戶可以將平臺上的模型一鍵發佈到小盒子模型服務平臺,並一鍵測試驗證服務狀態和模型推理結果。發佈之後,上層業務方應用可直接通過 Dubbo 或 HTTP 方式調用模型推理服務。

小盒子模型在線服務平臺提供了低延遲高可用的模型推理環境,其功能可分爲以下三個方面:

  1. 模型持續集成與 A/BTest 。模型是需要持續更新的。一方面,算法工程師會不斷優化模型結構,調整超參選擇;另一方面,新的特徵數據不斷產生,需要通過增量訓練,注入到新的模型中。小盒子支持兩類模型服務更新。對於算法相關的模型變化,一般是算法工程師主動重新訓練得到的模型,可以通過新增模型服務版本,發佈爲新的模型服務。對於使用增量數據更新的模型,可以通過設置調度週期,定時增量訓練,並自動更新已有模型服務。小盒子內部有小版本的概念,標註這一類模型更新。對於模型服務更新,小盒子還支持模型版本的灰度上線以及版本間的 A/BTest 。
  2. 服務低延遲與高可用。小盒子作爲線上服務平臺,需保證請求的低延遲,服務的高可用,以及服務監控與告警。對於一些推理請求,例如推薦精排的請求,可能一次請求包含了上百個需要打分的商品項。小盒子會根據請求中的參數配置,將請求中的所有商品項拆分爲幾個子集,發送到多個模型服務節點,並行的進行推理打分,優化整個請求的響應時間。管理員可以根據業務重要性對集羣進行業務組劃分,保證高優先級業務的模型服務穩定性。在小盒子中,模型會根據節點負載自動遷移,進行負載均衡。小盒子會收集每個請求的各種指標,並可靈活配置監控告警。
  3. 業務自定義插件。有時候模型推理只是算法服務中的一環。例如在 OCR 識別的場景,上層應用方期望的服務接口是請求一張圖片,返回識別出來的文字。這個接口是無法通過簡單的一次模型推理來實現的,這其中會有很多對圖像的處理步驟,還可能需要多次調用不同的模型推理服務。爲了滿足這樣的場景,小盒子支持動態加載用戶自己開發的自定義插件,在插件中進行圖像預處理等操作。小盒子還提供了本地開發模式,方便用戶開發調試自定義插件。下面分別對可視化訓練平臺和小盒子模型服務平臺介紹它們的架構設計及核心功能的實現原理。

四、可視化模型訓練平臺

4.1 可視化建模

我們以鳶尾花( Iris )分類任務的訓練爲例,介紹如何在 Sunfish 中進行建模訓練。在 TensorFlow 的官方文檔預創建的 Estimators 中,介紹瞭如何使用 TensorFlow 的 Estimator API 解決 Iris 分類問題。概括的說,編寫基於預創建的 Estimator 的 TensorFlow 項目,需要開發這麼幾個部分:創建一個或多個返回 tf.data.Dataset 對象的輸入函數;定義模型的特徵列;實例化一個 Estimator ,指定特徵列和各種超參數;在 Estimator 對象上調用一個或多個方法,傳遞合適的輸入函數以作爲數據源。

在 Sunfish 中,我們可以把這類 TensorFlow 項目的以上幾個部分抽象並提取了出來,發佈爲平臺中的組件。因此,我們可以在平臺中以拖拽式的方式完成 Iris 分類問題的實驗構建和模型訓練。如下圖所示,我們從組件列表中選擇需要的各個組件拖到實驗畫布,爲各個節點建立數據和控制依賴,配置相關參數。之後我們運行實驗,即可訓練得到 Iris 模型。

4.2 實驗與組件

用戶可以爲不同的業務創建不同的項目,然後在項目中創建實驗。如上圖所示,Iris 實驗是一個以組件節點構成的 DAG 。組件節點可由頁面左側的組件窗口直接拖動組件到頁面中間的畫板生成。點擊組件節點可以爲其配置參數。算法同學可以發佈自己的組件,發佈時指定該算法的代碼實現在 GitLab 上的地址,以及聲明組件的輸入輸出數量和類型即可。在上圖的 Iris 實驗中,組件節點有兩種類型,藍色的節點爲 Python 類型,這類組件可以直接單獨運行。如圖中 Training_TFRecord 節點,在配置中指定 Hive 表或 SQL 語句,運行該節點會生成 TFRecord 文件。黃色節點爲 Module 類型,需要組合到 python 類型的組件中,作爲其依賴在運行時調用。如圖中 Adam_optimizer 節點,表示 TensorFlow 中的一個 Optimizer ,不能單獨運行一個 Optimizer ,而是在訓練時作爲一個模塊被使用。

4.3 系統架構

訓練平臺的系統架構設計如下圖所示:

訓練平臺的集羣節點有兩類角色, Master 和 Worker ,都是無狀態節點,可水平擴容。Master 接受前端請求,負責實驗、組件、模型等內容的管理,以及實驗狀態管控。Worker 負責從 GitLab 獲取組件代碼實現,運行組件任務,並提供日誌查詢服務。Master 依賴 Zookeeper 進行容錯恢復, Worker 依賴 Zookeeper 進行服務註冊。AlgorithmBox 即小盒子模型服務平臺, Master 與之交互,進行模型的發佈管理。

4.4 實驗運行

下面以上文提到的 Iris 實驗的運行過程爲例,介紹其中各模塊的交互過程。運行 Iris 實驗, Master 首先會將實驗傳到 Compiler , Compiler 負責對實驗進行編譯,生成 Plan , Plan 是一個由 Task 構成的 DAG 。Compiler 會協同 PlanManager 根據實驗的 Plan 是否已生成,某個實驗節點配置及其上游節點配置是否已更新等信息,判斷哪些節點需要重新編譯生成 Task 。編譯過程中,會根據實驗節點的組件類型生成對應的 Task ,爲節點間的數據傳遞生成臨時載體(如 TFRecord 文件的 HDFS 目錄),根據節點的配置項和輸入輸出項生成配置文件。對於 Module 類型的節點,會將這些節點進行組合,生成一個可運行的 Task 。編譯完成之後,會生成 Plan , Plan 中包含一個由 Task 組成的 DAG 。

PlanScheduler 負責實驗的調度和運行。一個 Plan 的運行主要通過 Runnable 和 Running 兩個 Task 隊列進行控制。初始化時將 Plan 中的 Root Tasks 放到 Runnable 隊列中,總是根據 Task 最大並行度取 N 個 Task 運行,並把運行中的 Task 放到 Running 隊列。當有 Task 運行完成時,從 Running 隊列取出,並從其 Child Tasks 中取可運行(其所有 Parent Tasks 已運行成功)的 Tasks,放到Runnable 隊列。爲了 Master 的宕機容錯, PlanScheduler 會將 Runnable 和 Running 等 Plan 運行狀態放到 Zookeeper ,並定期更新。當其他 Master 節點發現這個 Plan 的運行狀態久未更新,會接管這個 Plan 的運行。在運行過程中, PlanScheduler 還會進行實驗停止、 Task 運行超時或失敗進而終止實驗等處理。TaskScheduler 負責將 Task 發送到 Worker 執行,並在 Worker 宕機時,負責 Task 遷移。

Worker 節點接受 Task 運行請求,從 MySQL 獲取 Task 元信息。根據 Task 類型進行不同的處理。對於 Python Task,會從 HDFS 獲取運行所需的配置文件,從 GitLab 拉取 Python 代碼,然後提交給 TaskExecutor 執行。除此之外, Worker 還接受 Task 停止, Log 查詢等服務請求。

運行過程中右鍵點擊某個節點,可以查看實時運行日誌。點擊實驗畫板上方的 TensorBoard ,可以選擇某個生成 EventLog 的節點,然後打開 TensorBoard 頁面,可以查看訓練進度,收斂情況等信息。

4.5 模型管理與發佈

實驗運行完成之後,生成的模型會自動保存下來。可以在模型管理頁面看到每次運行生成的模型。用戶也可以指定模型在 HDFS 上的路徑上傳已有模型。在模型管理頁面,可以選擇某個模型發佈。模型發佈是指將模型發佈到小盒子模型服務平臺,發佈成功之後業務方可以直接通過小盒子的接口進行模型推理服務的調用。小盒子中的模型服務調用會在下節詳細介紹。在服務管理頁面,用戶可以查看所有已發佈的模型服務狀態,並進行管理。如可以選擇服務上下線,可以調整模型服務實例個數,提高吞吐量,也可以直接向模型服務發送請求數據,測試服務功能。

4.6 Notebook

我們部署了 JupyterLab 並將頁面嵌入 Sunfish,提供 Notebook 建模能力。我們安裝了支持 Python2 和 Python3 版本的 Kernel,用戶可在 Notebook 中使用 PySpark 直接訪問離線數據。我們使用 JupyterLab 的 WorkSpace 特性對用戶空間進行隔離。

五、小盒子模型服務平臺

上文已經介紹過小盒子在模型、服務和業務三個方面的功能。簡單來說,使用小盒子進行模型推理可以分爲兩個步驟,1.模型創建與發佈,用戶指定模型服務名稱、模型服務版本、模型服務類型、模型所在的HDFS路徑、集羣、業務組、期望的服務實例數量等信息(如果有自定義插件,也需要指定Jar包所在的HDFS路徑),在小盒子中發佈模型。2.業務方在請求中指定執行計劃(詳見下文介紹),向小盒子發送算法服務請求。

5.1 系統架構

下面來看一下小盒子的設計與實現,小盒子的系統架構如下圖:

小盒子的集羣節點分爲三類角色, Manager 、Master 和 Worker ,都是無狀態節點,可水平擴容。Manager 主要負責模型管理和發佈,集羣和業務組管理,服務路由整理等工作;Master 接受業務方的模型推理請求,根據路由信息,將請求轉發到合適的 Worker ;Worker 負責本節點的模型加載、服務健康檢查,並提供模型推理服務。對於 TensorFlow 模型, Worker 節點部署了 TensorFlow-Serving 的 Docker 環境,由 TensorFlow-Serving 進行實際的模型推理工作。

5.2 模型發佈與路由管理

小盒子管理着由很多模型服務節點( Worker )組成的集羣,一個模型會根據發佈時指定的實例數,加載到 N 個 Worker 節點。當 Master 收到業務請求之後,需要快速的將數據提交到正確的 Worker 進行推理計算。因此,我們需要維護一組模型到 Worker 的路由信息。首先, Manager 會根據模型發佈信息,維護一份靜態路由( StaticRoute )用於聲明模型期望加載到的 Worker 節點集合。另外,模型實際加載情況則由 Worker 各自通過心跳註冊到 Zookeeper 。最終實際的路由信息( DynamicRoute )由靜態路由和心跳信息聚合得到。

新模型發佈時,Manager 收到模型發佈請求之後,小盒子會進行以下處理。1.根據請求中的參數,DeploymentManager 從指定集羣指定業務組的 Workers 中選擇 N 個負載最低(已加載模型數量最少)的節點,作爲新發布模型的宿主節點。StaticRouteManager 將此新模型信息及選中 Worker 信息( StaticRoute )更新到 Zookeeper。2.被選中的 Workers 會監聽到 Zookeeper 中 StaticRoute 的變更,ModelLoader 會在本節點加載新發布的模型。ServiceMonitor 監聽到新模型加載成功之後,會更新本節點的可用模型服務信息。此信息會隨着 HeartBeatManager 上報到 Zookeeper。3. Manager 中的 DynamicRouteManager 監聽到 Zookeeper 中可用模型服務信息的變化,會結合 StaticRoute 整理得到 DynamicRoute,並更新到 Zookeeper。4. Master 中的 RouteTableManager 監聽到 Zookeeper 中 DynamicRoute 變化,更新本地的 RouteTable。

5.3 請求處理過程

上文提到過有的算法服務(例如 OCR 場景)可能需要經過多個推理步驟。在小盒子中我們定義了執行計劃( Plan )和執行步驟( Stage )的概念,用於描述一個算法服務的請求處理過程。Plan 由一組 Stage 組成,請求需要依次執行各個 Stage 的描述。一個Stage描述了一個模型推理請求,Stage 中指定了模型服務的名稱、版本和類型。有的場景下,Stage 中還可以指定多個 SubStage,SubStage 之間可以併發執行,降低整個請求的延遲。我們可以在 Apollo 中爲各種業務和場景配置不同的 Plan,對於某個業務下的某個場景,我們可以配置多個 Plan,並指定每個 Plan 的流量比例。這樣,業務方的算法服務請求參數中只要聲明業務名稱和場景名稱即可。當然,業務方也可以直接在算法服務請求的參數中指定 Plan 的描述。

小盒子 Master 負責處理算法服務請求。收到一個請求之後,SessionManager 會生成一個 Session 管理請求處理狀態及接口超時返回。PlanManager 根據請求中的業務和場景參數從 Apollo 獲取 Plan 描述,生成 Plan 實例,並提交到 PlanExecutor。PlanExecutor 依次處理 Plan 中的 Stage。針對每一個 Stage,WorkSender 根據 RouteTable 中的模型服務路由信息,將請求數據發送到對應的 Worker 進行模型推理計算。如果其中有用到用戶自定義插件,PluginLoader 負責加載 Jar 包,用於處理各個 Stage 在模型推理的前後進行業務相關的數據處理。Master 中默認的 Plugin 會將請求中的數據進行拆分,併發的向 Worker 請求模型推理服務,再將結果合併,降低服務延遲。

六、展望‍

目前 Sunfish 還處於剛剛成型的階段,只是滿足了算法訓練和模型服務的基本需求,我們還有更多的期待和挑戰。我們期望 Sunfish 向着產品化和中臺化的方向發展。產品化,Sunfish 會進一步優化功能和使用體驗,覆蓋更多的目標用戶。中臺化,Sunfish 不僅僅作爲提升算法開發效率的工具,更要深入業務場景,沉澱算法能力,優化算法服務構建流程。我們需要在以下這些方面投入更多的精力。

  1. 功能優化。我們會爲 Sunfish 增加更多實用功能。如將訓練任務或離線推理任務發佈爲週期性調度任務;小盒子支持根據請求的內容動態選擇執行計劃等待。
  2. 易用性優化。目前,只有對算法模型比較熟悉的算法工程師才能用好 Sunfish,其中的組件還需要相對複雜的參數配置。我們會開發更多實用簡單的算法組件,引入自動調參之類的技術。並針對一些常見場景,建立實驗模板,使更多同學可以方便的應用算法能力。
  3. 賦能業務。建立特徵池和模型庫,管理通用特徵和模型的生命週期和血緣,爲上層業務提供通用的特徵和模型服務。

本文轉載自公衆號有贊coder(ID:youzan_coder)。

原文鏈接

https://mp.weixin.qq.com/s?__biz=MzAxOTY5MDMxNA==&mid=2455760891&idx=1&sn=8c836b598c62763fc3409296287e9e15&chksm=8c6869debb1fe0c8453bc0129f0ca4474a282fa13b96b985d0b3185101bffd078a89255e5234&scene=27#wechat_redirect

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