阿里雲PAI-DeepRec CTR 模型性能優化天池大賽——獲獎隊伍技術分享

語言: CN / TW / HK

阿里雲聯合英特爾舉辦的“創新大師杯”全球AI極客挑戰賽——PAI-DeepRec CTR模型性能優化挑戰賽已結束 ,此次大賽旨在DeepRec中沉澱CTR模型新的優化思路和優化方向。為了和大家共享經驗成果,邀請獲獎隊伍分享解題思路,共同推動實際工業實際場景中點擊率預估模型的訓練效率的提升。

大家好,我們是MetaSpore團隊,三位成員孫凱、蘇程成、朱亞東均來自北京數元靈科技有限公司,其中蘇程成就讀於西安交大,曾為數元靈科技實習生。

今年7月中旬,阿里雲聯合 Intel 啟動了“創新大師杯”全球AI極客挑戰賽——PAI-DeepRec CTR模型性能優化,全球一共有超過3800支隊伍報名參加比賽。經過近 5 個月的努力,我們在保障 6 個經典的 CTR 模型AUC 基本不損失的前提下,將訓練效率提升了 3 倍以上,減少了接近 70% 的訓練時間。團隊在全球初賽和全球複賽都獲得了排名第一的成績,本文將就比賽中的整體思路和具體方案進行闡述。

解題思路

首先必須承認,這是一道比較難的題目。因為 DeepRec 已經集成了來自 Alibaba、Intel、Google等眾多優秀工程師的智慧,在這個基礎上再進行性能優化,不得不説是一個非常具有挑戰性的問題。經過長時間的迭代,團隊優化思路如下圖所示,主要可以概括為一下 3 個方面:

image.png

  1. CTR稀疏模型訓練優化:6個模型均為經典的 CTR 稀疏模型,在特徵處理、算子等方面可能具有一定的優化空間。
  2. DeepRec訓練加速參數調優:DeepRec 本身已經具有有來自 Alibaba 和 Intel 團隊的很多優秀的技術沉澱,對模型訓練有很多參數都可以進行調優。
  3. DeepRec框架性能優化:這個方面我們覺得可能在編譯選項、優化器等方面有一定的空間,以便更好的發揮硬件潛能。

稀疏模型訓練優化

1. 選擇更快的 GRUCell

對於DIEN模型,我們注意到其使用了GRU,而GRU是串行執行,必然會耗費大量時間,因此我們先把矛頭對準了GRU。

階段一:DIEN使用的是tf.nn.rnn_cell.GRUCell接口,在查閲 tensorflow 官方文檔時我們注意到tf.contrib.rnn.GRUBlockCellV2能夠有更好的性能。

圖片

因此我們將 tensorflow 中的tf.nn.rnn_cell.GRUCell改為了 tf.contrib.rnn.GRUBlockCellV2。tf.nn.rnn_cell.GRUCell是使用 python寫的 GRU,因此其反向傳播需要計算圖層層傳遞。而tf.contrib.rnn.GRUBlockCellV2用 C++ 編寫的,並且實現了 forward 和 backward,因此速度會相對快一點。

階段二:在 GRU 的優化獲得初步收益之後,我們在想能否有替代 GRU的網絡結構。之後我們調研了替換 GRU 的方法,發現 SRU 可以在不損失 AUC 的情況下加快模型的訓練,相比原始版本速度提升約80s。SRU 論文鏈接:

http://arxiv.org/pdf/1709.02755.pdf

圖片

為什麼 SRU 會比較快呢?我們來看GRU與SRU的實現公式:

圖片

相比於GRU,SRU 對時序依賴更弱一些,SRU有 3 個步驟依賴於前面的狀態,並且依賴 C(t-1) 的操作使用的是 Hadamard 積,計算量更小;論文最後還通過消融實驗發現,與C(t-1)相關的 2 個操作可以省略,因此代碼實現中並沒有粉色部分。

階段三(未採用):既然 GRU 能改成 SRU,那 SRU 能否繼續優化呢,我們帶着這個疑問開始嘗試優化SRU,最終我們得到了一個保持 AUC 不變的簡化版 SRU,其速度又能夠提升 50s 左右。由於並沒有嚴格的理論分析,最終我們並未把這個版本提交上去,不過在代碼記錄了這個版本。

2. 優化稀疏特徵表示

在查看DeepFM 模型的 Timeline 圖(下圖所示),我們發現其中有大量的 OneHot 算子異常耗時。

圖片

我們注意到官方文檔中描述embedding_column 速度會更快,而且更適合高維稀疏的類別特徵,於是我們將Indicator_column替換為了embedding_column。

圖片

對比結果如下:

圖片

訓練加速參數調優

開啟流水線在閲讀 DeepRec 文檔時,我們注意到了 AutoMicroBatch,它的本質是一個模型訓練的流水線,多個MicroBatch 對梯度進行累加後更新至 variable,DeepRec 文檔中給出的實測效果下圖所示。

圖片

我們首先對這五個模型開啟 micro_batch 進行了實驗,發現Wide & Deep 模型不能使。我們首先對這五個模型開啟micro_batch 進行了實驗,發現Wide & Deep 模型不能使用 micro_batch,其使用的tf.feature_column.linear_model 接口與 micro_batch 衝突,導致運行crash,如下左圖示。因此我們將 Wide & Deep 模型使用的 tf.feature_column.linear_model 進行了重寫,如下右圖所示。

圖片

經過了以上的準備,我們開啟了micro_batch 的性能優化。

  1. 我們最初對所有模型都設置了相同的 micro_batch_num,經過我們實驗,當micro_batch_num = 2時,所有模型都可達到 AUC 要求,相對原始版本速度可以提升900s左右。
  2. 當 micro_batch_num 再大一點,DIEN 模型的 AUC 會低於賽題標準,其他幾個模型AUC基本沒有變化。因此,我們對DIEN 模型進行了特殊處理,也就是給它單獨設置一個 micro_batch_num ,最終經過我們實驗,我們給DIEN模型 micro_batch_num 設置為 2,其他幾個模型採用默認值 8。

對比結果如下:

圖片

底層框架性能調優

1. 優化編譯選項

在DeepRec比賽教程中給出的編譯選項如下

bazel build  -c opt --config=opt  --config=mkl_threadpool --define build_with_mkl_dnn_v1_only=true

該編譯選項使用了針對intel處理器進行優化的 mkl_threadpool。tensorflow有很多可配置的編譯選項,不同的編譯選項會編譯出不同性能的框架,經過我們嘗試,在本次比賽中,經過優化編譯選項,相較原始版本速度提升130s左右。

編譯選項如下:

bazel build -c opt --config=opt //tensorflow/tools/pip_package:build_pip_package

對比結果如下:

圖片

2. 其他底層優化選項

下面是我們對於其他底層優化的想法與探索:

  1. 使用微軟開源的 mimalloc 作為內存分配器可以進一步優化性能,實測可以節省 4% 的時間,但由於時間關係我們並未打包提交。
  2. MKL 庫有比較多算子可供使用,可以針對不同的算子選擇性地調用 MKL,這一方向也由於時間的關係沒有來得及完成。

總結

在 DeepCTR 比賽中,我們從稀疏模型、訓練加速調優、底層框架調優等 3 個方面出發,主要做了以上 5 點的優化,其中 GRU 算子和稀疏特徵的優化靈感來自於團隊之前在 MetaSpore 的開發中的技術沉澱。決賽階段遇到了各路好手,很多問題的切入點獨到而新穎,非常有啟發性,值得我們學習和借鑑。

最後,將以上所有優化點進行疊加,我們得到如下總運行時間對比圖,可以清晰的看到,經過我們的優化,模型訓練效率得到 3 倍以上提升,訓練時間減少了 70%。

圖片

注:以上測試都是在我們本地機器(8核16G)上進行的測試,因此與線上成績有一定差異。

Github 鏈接:

http://github.com/meta-soul/DeepRec/tree/tianchi

DeepRec開源地址:

http://github.com/alibaba/DeepRec