基於TensorFlow Serving的深度學習線上預估

美團技術團隊發表於2018-10-18

一、前言

隨著深度學習在影像、語言、廣告點選率預估等各個領域不斷髮展,很多團隊開始探索深度學習技術在業務層面的實踐與應用。而在廣告CTR預估方面,新模型也是層出不窮: Wide and Deep[1]、DeepCross Network[2]、DeepFM[3]、xDeepFM[4],美團很多篇深度學習部落格也做了詳細的介紹。但是,當離線模型需要上線時,就會遇見各種新的問題: 離線模型效能能否滿足線上要求、模型預估如何鑲入到原有工程系統等等。只有準確的理解深度學習框架,才能更好地將深度學習部署到線上,從而相容原工程系統、滿足線上效能要求。

本文首先介紹下美團平臺使用者增長組業務場景及離線訓練流程,然後主要介紹我們使用TensorFlow Serving部署WDL模型到線上的全過程,以及如何優化線上服務效能,希望能對大家有所啟發。

二、業務場景及離線流程

2.1 業務場景

在廣告精排的場景下,針對每個使用者,最多會有幾百個廣告召回,模型會根據使用者特徵與每一個廣告相關特徵,分別預估該使用者對每條廣告的點選率,從而進行排序。由於廣告交易平臺(AdExchange)對於DSP的超時時間限制,我們的排序模組平均響應時間必須控制在10ms以內,同時美團DSP需要根據預估點選率參與實時競價,因此對模型預估效能要求比較高。

2.2 離線訓練

離線資料方面,我們使用Spark生成TensorFlow[5]原生態的資料格式tfrecord,加快資料讀取。

模型方面,使用經典的Wide and Deep模型,特徵包括使用者維度特徵、場景維度特徵、商品維度特徵。Wide 部分有 80多特徵輸入,Deep部分有60多特徵輸入,經過Embedding輸入層大約有600維度,之後是3層256等寬全連線,模型引數一共有35萬引數,對應匯出模型檔案大小約11M。

離線訓練方面,使用TensorFlow同步 + Backup Workers[6]的分散式框架,解決非同步更新延遲和同步更新效能慢的問題。

在分散式ps引數分配方面,使用GreedyLoadBalancing方式,根據預估引數大小分配引數,取代Round Robin取模分配的方法,可以使各個PS負載均衡。

計算裝置方面,我們發現只使用CPU而不使用GPU,訓練速度會更快,這主要是因為儘管GPU計算上效能可能會提升,但是卻增加了CPU與GPU之間資料傳輸的開銷,當模型計算並不太複雜時,使用CPU效果會更好些。

同時我們使用了Estimator高階API,將資料讀取、分散式訓練、模型驗證、TensorFlow Serving模型匯出進行封裝。

使用Estimator的主要好處在於:

  1. 單機訓練與分散式訓練可以很簡單的切換,而且在使用不同裝置:CPU、GPU、TPU時,無需修改過多的程式碼。

  2. Estimator的框架十分清晰,便於開發者之間的交流。

  3. 初學者還可以直接使用一些已經構建好的Estimator模型:DNN模型、XGBoost模型、線性模型等。

三、TensorFlow Serving及效能優化

3.1 TensorFlow Serving介紹

TensorFlow Serving是一個用於機器學習模型Serving的高效能開源庫,它可以將訓練好的機器學習模型部署到線上,使用gRPC作為介面接受外部呼叫。TensorFlow Serving支援模型熱更新與自動模型版本管理,具有非常靈活的特點。

下圖為TensorFlow Serving整個框架圖。Client端會不斷給Manager傳送請求,Manager會根據版本管理策略管理模型更新,並將最新的模型計算結果返回給Client端。

基於TensorFlow Serving的深度學習線上預估

圖1. TensorFlow Serving架構,圖片來源於TensorFlow Serving官方文件

美團內部由資料平臺提供專門TensorFlow Serving通過YARN分散式地跑在叢集上,其週期性地掃描HDFS路徑來檢查模型版本,並自動進行更新。當然,每一臺本地機器都可以安裝TensorFlow Serving進行試驗。

在我們站外廣告精排的場景下,每來一位使用者時,線上請求端會把該使用者和召回所得100個廣告的所有資訊,轉化成模型輸入格式,然後作為一個Batch傳送給TensorFlow Serving,TensorFlow Serving接受請求後,經過計算得到CTR預估值,再返回給請求端。

部署TensorFlow Serving的第一版時,QPS大約200時,打包請求需要5ms,網路開銷需要固定3ms左右,僅模型預估計算需要10ms,整個過程的TP50線大約18ms,效能完全達不到線上的要求。接下來詳細介紹下我們效能優化的過程。

3.2 效能優化

3.2.1 請求端優化

線上請求端優化主要是對一百個廣告進行並行處理,我們使用OpenMP多執行緒並行處理資料,將請求時間效能從5ms降低到2ms左右。

#pragma omp parallel for 
for (int i = 0; i < request->ad_feat_size(); ++i) {
    tensorflow::Example example;
    data_processing();
}

3.2.2 構建模型OPS優化

在沒有進行優化之前,模型的輸入是未進行處理的原格式資料,例如,渠道特徵取值可能為:'渠道1'、'渠道2' 這樣的string格式,然後在模型裡面做One Hot處理。

最初模型使用了大量的高階tf.feature_column對資料進行處理, 轉為One Hot和embedding格式。 使用tf.feature_column的好處是,輸入時不需要對原資料做任何處理,可以通過feature_column API在模型內部對特徵做很多常用的處理,例如:tf.feature_column.bucketized_column可以做分桶,tf.feature_column.crossed_column可以對類別特徵做特徵交叉。但特徵處理的壓力就放在了模型裡。

為了進一步分析使用feature_column的耗時,我們使用tf.profiler工具,對整個離線訓練流程耗時做了分析。在Estimator框架下使用tf.profiler是非常方便的,只需加一行程式碼即可。

with tf.contrib.tfprof.ProfileContext(job_dir + ‘/tmp/train_dir’) as pctx:
   estimator = tf.estimator.Estimator(model_fn=get_model_fn(job_dir),
                                      config=run_config,
                                      params=hparams)    

下圖為使用tf.profiler,網路在向前傳播的耗時分佈圖,可以看出使用feature_column API的特徵處理耗費了很大時間。

基於TensorFlow Serving的深度學習線上預估

圖2. 優化前profiler記錄, 前向傳播的耗時佔總訓練時間55.78%,主要耗費在feature_column OPS對原始資料的預處理

為了解決特徵在模型內做處理耗時大的問題,我們在處理離線資料時,把所有string格式的原生資料,提前做好One Hot的對映,並且把對映關係落到本地feature_index檔案,進而供線上線下使用。這樣就相當於把原本需要在模型端計算One Hot的過程省略掉,替代為使用詞典做O(1)的查詢。同時在構建模型時候,使用更多效能有保證的低階API替代feature_column這樣的高階API。下圖為效能優化後,前向傳播耗時在整個訓練流程的佔比。可以看出,前向傳播的耗時佔比降低了很多。

基於TensorFlow Serving的深度學習線上預估

圖3. 優化後profiler記錄,前向傳播耗時佔總訓練時間39.53%

3.2.3 XLA,JIT編譯優化

TensorFlow採用有向資料流圖來表達整個計算過程,其中Node代表著操作(OPS),資料通過Tensor的方式來表達,不同Node間有向的邊表示資料流動方向,整個圖就是有向的資料流圖。

XLA(Accelerated Linear Algebra)是一種專門對TensorFlow中線性代數運算進行優化的編譯器,當開啟JIT(Just In Time)編譯模式時,便會使用XLA編譯器。整個編譯流程如下圖所示:

基於TensorFlow Serving的深度學習線上預估

圖4. TensorFlow計算流程

首先TensorFlow整個計算圖會經過優化,圖中冗餘的計算會被剪掉。HLO(High Level Optimizer)會將優化後的計算圖生成HLO的原始操作,XLA編譯器會對HLO的原始操作進行一些優化,最後交給LLVM IR,進而根據不同的後端裝置,生成不同的機器程式碼。

JIT的使用,有助於LLVM IR根據 HLO原始操作生成更高效的機器碼;同時,對於多個可融合的HLO原始操作,會融合成一個更加高效的計算操作。但是JIT的編譯是在程式碼執行時進行編譯,這也意味著執行程式碼時會有一部分額外的編譯開銷。

基於TensorFlow Serving的深度學習線上預估

圖5. 網路結構、Batch Size對JIT效能影響[7]

上圖顯示為不同網路結構,不同Batch Size下使用JIT編譯後與不使用JIT編譯的耗時之比。可以看出,較大的Batch Size效能優化比較明顯,層數與神經元個數變化對JIT編譯優化影響不大。

在實際的應用中,具體效果會因網路結構、模型引數、硬體裝置等原因而異。

3.2.4 最終效能

經過上述一系列的效能優化,模型預估時間從開始的10ms降低到1.1ms,請求時間從5ms降到2ms。整個流程從打包傳送請求到收到結果,耗時大約6ms。

基於TensorFlow Serving的深度學習線上預估

圖6. 模型計算時間相關引數:QPS:1308,50line:1.1ms,999line:3.0ms。下面四個圖分別為:耗時分佈圖顯示大部分耗時控制在1ms內;請求次數顯示每分鐘請求大約8萬次,摺合QPS為1308;平均耗時時間為1.1ms;成功率為100%

3.3 模型切換毛刺問題

通過監控發現,當模型進行更新時,會有大量的請求超時。如下圖所示,每次更新都會導致有大量請求超時,對系統的影響較大。通過TensorFlow Serving日誌和程式碼分析發現,超時問題主要源於兩個方面,一方面,更新、載入模型和處理TensorFlow Serving請求的執行緒共用一個執行緒池,導致切換模型時無法處理請求;另一方面,模型載入後,計算圖採用Lazy Initialization方式,導致第一次請求需要等待計算圖初始化。

基於TensorFlow Serving的深度學習線上預估

圖7. 模型切換導致請求超時

問題一主要是因為載入和解除安裝模型執行緒池配置問題,在原始碼中:

uint32 num_load_threads = 0; uint32 num_unload_threads = 0;

這兩個引數預設為 0,表示不使用獨立執行緒池,和Serving Manager在同一個執行緒中執行。修改成1便可以有效解決此問題。

模型載入的核心操作為RestoreOp,包括從儲存讀取模型檔案、分配記憶體、查詢對應的Variable等操作,其通過呼叫Session的run方法來執行。而預設情況下,一個程式內所有Session的運算均使用同一個執行緒池。所以導致模型載入過程中載入操作和處理Serving請求的運算使用同一執行緒池,導致Serving請求延遲。解決方法是通過配置檔案設定,可構造多個執行緒池,模型載入時指定使用獨立的執行緒池執行載入操作。

對於問題二,模型首次執行耗時較長的問題,採用在模型載入完成後提前進行一次Warm Up運算的方法,可以避免在請求時運算影響請求效能。這裡使用Warm Up的方法是,根據匯出模型時設定的Signature,拿出輸入資料的型別,然後構造出假的輸入資料來初始化模型。

通過上述兩方面的優化,模型切換後請求延遲問題得到很好的解決。如下圖所示,切換模型時毛刺由原來的84ms降低為4ms左右。

基於TensorFlow Serving的深度學習線上預估

圖8. 優化後模型切換後,毛刺降低

四、總結與展望

本文主要介紹了使用者增長組基於Tensorflow Serving在深度學習線上預估的探索,對效能問題的定位、分析、解決;最終實現了高效能、穩定性強、支援各種深度學習模型的線上服務。

在具備完整的離線訓練與線上預估框架基礎之後,我們將會加快策略的快速迭代。在模型方面,我們可以快速嘗試新的模型,嘗試將強化學習與競價結合;在效能方面,結合工程要求,我們會對TensorFlow的圖優化、底層操作運算元、操作融合等方面做進一步的探索;除此之外,TensorFlow Serving的預估功能可以用於模型分析,谷歌也基於此推出What-If-Tools來幫助模型開發者對模型深入分析。最後,我們也會結合模型分析,對資料、特徵再做重新的審視。

參考文獻

[1]  Cheng, H. T., Koc, L., Harmsen, J., Shaked, T., Chandra, T., Aradhye, H., … & Anil, R. (2016, September). Wide & deep learning for recommender systems. In Proceedings of the 1st Workshop on Deep Learning for Recommender Systems (pp. 7-10). ACM.

[2]  Wang, R., Fu, B., Fu, G., & Wang, M. (2017, August). Deep & cross network for ad click predictions. In Proceedings of the ADKDD'17 (p. 12). ACM. 

[3]  Guo, H., Tang, R., Ye, Y., Li, Z., & He, X. (2017). Deepfm: a factorization-machine based neural network for ctr prediction. arXiv preprint arXiv:1703.04247.

[4]  Lian, J., Zhou, X., Zhang, F., Chen, Z., Xie, X., & Sun, G. (2018). xDeepFM: Combining Explicit and Implicit Feature Interactions for Recommender Systems. arXiv preprint arXiv:1803.05170.

[5]  Abadi, M., Barham, P., Chen, J., Chen, Z., Davis, A., Dean, J., … & Kudlur, M. (2016, November). TensorFlow: a system for large-scale machine learning. In OSDI (Vol. 16, pp. 265-283).

[6]  Goyal, P., Dollár, P., Girshick, R., Noordhuis, P., Wesolowski, L., Kyrola, A., … & He, K. (2017). Accurate, large minibatch SGD: training imagenet in 1 hour. arXiv preprint arXiv:1706.02677.

[7]  Neill, R., Drebes, A., Pop, A. (2018). Performance Analysis of Just-in-Time Compilation for Training TensorFlow Multi-Layer Perceptrons.

作者簡介

仲達,2017年畢業於美國羅徹斯特大學資料科學專業,後在加州灣區Stentor Technology Company工作,2018年加入美團,主要負責使用者增長組深度學習、強化學習落地業務場景工作。

鴻傑,2015年加入美團點評。美團平臺與酒旅事業群使用者增長組演算法負責人,曾就職於阿里,主要致力於通過機器學習提升美團點評平臺的活躍使用者數,作為技術負責人,主導了美團DSP廣告投放、站內拉新等專案的演算法工作,有效提升營銷效率,降低營銷成本。

廷穩,2015年加入美團點評。在美團點評離線計算方向先後從事YARN資源排程及GPU計算平臺建設工作。

相關文章