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

一、前言

隨着深度學習在圖像、語言、廣告點擊率預估等各個領域不斷髮展,很多團隊開始探索深度學習技術在業務層面的實踐與應用。而在廣告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架構,圖片來源於TensorFlow Serving官方文檔
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的特徵處理耗費了很大時間。


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

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


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

3.2.3 XLA,JIT編譯優化

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

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


   TensorFlow計算流程
TensorFlow計算流程

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

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


   網絡結構、Batch Size對JIT性能影響[7]
網絡結構、Batch Size對JIT性能影響[7]

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

在實際的應用中,具體效果會因網絡結構、模型參數、硬件設備等原因而異。

3.2.4 最終性能

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


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

3.3 模型切換毛刺問題

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


   模型切換導致請求超時
模型切換導致請求超時

問題一主要是因為加載和卸載模型線程池配置問題,在源代碼中:

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在深度學習線上預估的探索,對性能問題的定位、分析、解決;最終實現了高性能、穩定性強、支持各種深度學習模型的在線服務。

在具備完整的離線訓練與在線預估框架基礎之後,我們將會加快策略的快速迭代。在模型方面,我們可以快速嘗試新的模型,嘗試將強化學習與競價結合;在性能方面,結合工程要求,我們會對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計算平台建設工作。

招聘

美團DSP是美團在線數字營銷的核心業務方向,加入我們,你可以親身參與打造和優化一個可觸達億級用户的營銷平台,並引導他們的生活娛樂決策。同時,你也會直面如何精準,高效,低成本營銷的挑戰,也有機會接觸到計算廣告領域前沿的AI算法體系和大數據解決方案。你會和美團營銷技術團隊一起推動創建流量運營生態,支持酒旅、外賣、到店、打車、金融等業務繼續快速的發展。我們誠邀有激情、有想法、有經驗、有能力的你,和我們一起並肩奮鬥!參與美團點評站外廣告投放體系的實現,基於大規模用户行為數據,優化在線廣告算法,提升DAU,ROI, 提高在線廣告的相關度、投放效果。歡迎郵件wuhongjie#meituan.com諮詢。

發現文章有錯誤、對內容有疑問,都可以關注美團技術團隊微信公眾號(meituantech),在後台給我們留言。我們每週會挑選出一位熱心小夥伴,送上一份精美的小禮品。快來掃碼關注我們吧!

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