推理性能提升100%,TensorFlow Feature Column性能可以這樣玩
點(diǎn)擊“開發(fā)者技術(shù)前線”,選擇“星標(biāo)”
讓一部分開發(fā)者先看到未
來自:愛奇藝深度學(xué)習(xí)平臺(tái)團(tuán)隊(duì)
01
寫在前面
在CTR(Click Through Rate)點(diǎn)擊率預(yù)估的推薦算法場(chǎng)景,TensorFlow Feature Column被廣泛應(yīng)用到實(shí)踐中。這一方面帶來了模型特征處理的便利,另一方面也帶來了一些線上推理服務(wù)的性能問題。為了優(yōu)化推薦業(yè)務(wù)性能,提升線上服務(wù)效率,愛奇藝深度學(xué)習(xí)平臺(tái)團(tuán)隊(duì)在實(shí)踐中總結(jié)了一些性能優(yōu)化方法。
經(jīng)過這些優(yōu)化,推薦業(yè)務(wù)的線上推理服務(wù)性能效率可以提升一倍以上,p99延遲降低達(dá)到50%以上。
02
背景介紹
Feature Column是TensorFlow提供的用于處理結(jié)構(gòu)化數(shù)據(jù)的工具,是將樣本特征映射到用于訓(xùn)練模型特征的橋梁。它提供了多種特征處理方法,讓算法人員可以很容易將各種原始特征轉(zhuǎn)換為模型的輸入,來進(jìn)行模型實(shí)驗(yàn)。

如上圖所示,所有Feature Column都源自FeatureColumn類,并繼承了三個(gè)子類CategoricalColumn、DenseColumn和SequenceDenseColumn,分別對(duì)應(yīng)稀疏特征、稠密特征、序列稠密特征。算法人員可以按照樣本特征的類型找到對(duì)應(yīng)的接口直接適配。
而且 Feature Column 和 TF Estimator 接口有很好的集成,通過定義好對(duì)應(yīng)的特征輸入就可以直接在預(yù)定義的 Estimator 模型中使用。TF Estimator 在推薦算法的使用非常普遍,特別是它封裝了分布式訓(xùn)練的功能。
下圖是一個(gè)使用Feature Column處理特征,進(jìn)入到 Estimator DNN Classifier的示例:

雖然Feature Column使用起來很方便,模型代碼編寫比較快,但是在愛奇藝推薦類業(yè)務(wù)的線上服務(wù)落地過程中,一些性能問題逐漸凸顯,下面將逐個(gè)介紹我們?cè)趯?shí)際中碰到的一些問題,以及如何優(yōu)化。
03
整型特征哈希優(yōu)化
推薦類模型通常都會(huì)將ID類特征哈希到一定數(shù)量的bucket 分桶,然后轉(zhuǎn)換成 Embedding再作為神經(jīng)網(wǎng)絡(luò)的輸入,比如視頻類ID特征,用戶ID特征,商品ID特征等。示例如下:

在` categorical_column_with_hash_bucket `的文檔[2]里面說到:對(duì)于String類型的輸入,會(huì)執(zhí)行`output_id = Hash(input_feature_string) % bucket_size`做哈希操作,而對(duì)于整數(shù)類型的輸入會(huì)先轉(zhuǎn)成String類型然后再進(jìn)行同樣的哈希操作。通過查看源代碼[3]可以看到這樣的邏輯:

在推薦業(yè)務(wù)中,通常這類ID都是已經(jīng)過某種方式的哈希,形成64bit的整型特征放到樣本里面,因此必然要執(zhí)行整型轉(zhuǎn)化成String的操作。但是在TensorFlow 的 Timeline中可以看到函數(shù)`as_string`所對(duì)應(yīng)的TF內(nèi)部的`AsString` OP其實(shí)是一個(gè)比較耗時(shí)的操作,經(jīng)過分析對(duì)比發(fā)現(xiàn)`AsString` OP的耗時(shí)通常是后面的哈希操作的3倍以上,如下圖所示:

進(jìn)一步分析`AsString` OP內(nèi)部的代碼,可以發(fā)現(xiàn)這個(gè)OP內(nèi)部還涉及到了內(nèi)存分配和拷貝操作,因此比純哈希計(jì)算慢就可以理解了。
很自然,團(tuán)隊(duì)考慮去掉相關(guān)操作來做優(yōu)化,因此專門編寫了一個(gè)給整型做哈希的函數(shù)來做優(yōu)化,示例代碼如下:

經(jīng)過這樣做區(qū)分類型的哈希方式,完全優(yōu)化了原先耗時(shí)長(zhǎng)的類型轉(zhuǎn)換操作。這里需要注意的是新加的哈希函數(shù)對(duì)應(yīng)的新OP同樣需要加到 TF Serving 中。
04
定長(zhǎng)特征轉(zhuǎn)換優(yōu)化
定長(zhǎng)特征是指使用接口`tf.io.FixedLenFeature`來解析的特征,比如用戶的性別,年齡等,這類特征的長(zhǎng)度通常都是定長(zhǎng)的,并且固定為 1 維或多維。這類特征經(jīng)過接口`tf.io.parse_example` 解析成 Dense Tensor,然后經(jīng)過Feature Column處理,再進(jìn)入到模型的輸入層。常見的代碼示例如下:

以上面的代碼為例子,舉例解析一下 TensorFlow 內(nèi)部的Tensor 轉(zhuǎn)換邏輯。如下圖所示,兩個(gè)樣本user_name分別為bob和wanda,經(jīng)過樣本解析成shape為2的Dense Tensor,然后經(jīng)過`categorical_column_with_vocabulary_list`轉(zhuǎn)換,查找詞表分別轉(zhuǎn)成0和2,再經(jīng)過`indicator_column`轉(zhuǎn)換成One hot編碼的Dense輸入。

從上面的樣本處理來看沒有什么問題,然后再來看一下Feature Column代碼內(nèi)部的轉(zhuǎn)換處理邏輯:

如上圖所示,在代碼中Vocabulary Categorical Column 會(huì)先去除掉一些非法值,然后把輸入的Dense Tensor 轉(zhuǎn)換成 Sparse Tensor,在Indicator Column中會(huì)再次把Tensor從Sparse轉(zhuǎn)成Dense,最后轉(zhuǎn)成需要的One Hot Tensor。
先來思考一下上面兩個(gè)轉(zhuǎn)換操作的目的,一方面是為了去除樣本數(shù)據(jù)中一些異常的值,另外一方面是這樣的處理其實(shí)是同時(shí)兼顧了輸入是 Sparse Tensor 的情況,如果輸入是Spare Tensor就直接做Vocabulary詞表查找,然后再轉(zhuǎn)成Dense Tensor。這樣轉(zhuǎn)換雖然達(dá)到代碼復(fù)用的作用,但是在性能上卻有損失。如果能直接將原始的Input Tensor轉(zhuǎn)換成One Hot Tensor,就可以省去兩個(gè)轉(zhuǎn)換過程,而且Sparse Tensor 和Dense Tensor之間的轉(zhuǎn)換其實(shí)是非常耗時(shí)的操作。
再回到定長(zhǎng)特征的原始性質(zhì),對(duì)于這類定長(zhǎng)特征來講,在樣本處理的時(shí)候如果沒有值,會(huì)被填充成默認(rèn)值,而且在生成樣本的時(shí)候都會(huì)被保證不會(huì)出現(xiàn)有空值或者 -1的情況,因此異常值的處理其實(shí)是可以被省略的。最后優(yōu)化后的內(nèi)部轉(zhuǎn)換邏輯如下圖,省去了兩次Sparse Tensor 和Dense Tensor之間的轉(zhuǎn)換。

除了上面的Vocabulary Categorical Column,還有別的類似Feature Column也有同樣的問題,因此針對(duì)這類特征,平臺(tái)專門開發(fā)了一套優(yōu)化的Feature Column接口提供給業(yè)務(wù)使用,優(yōu)化性能效果還不錯(cuò)。
05
用戶特征去重優(yōu)化
推薦類算法模型都有個(gè)很典型的特點(diǎn),那就是模型中會(huì)包含用戶側(cè)特征和要推薦的Item側(cè)特征,比如視頻的特征、商品的特征等。模型在線上服務(wù)部署的時(shí)候,會(huì)給一個(gè)用戶推薦多個(gè)視頻或商品,模型會(huì)返回這多個(gè)視頻或商品的打分,然后按照打分的大小推薦給用戶。由于是給單個(gè)用戶做推薦,這個(gè)時(shí)候該用戶的特征會(huì)根據(jù)推薦Item的數(shù)量重復(fù)多次,再發(fā)送給模型。如下是一個(gè)典型的推薦算法排序模型線上推理的示意圖:

圖示的模型輸入有3個(gè)User特征,3個(gè)Item特征,假定在對(duì)某個(gè)用戶做推薦,該用戶的3個(gè)特征分別對(duì)應(yīng)為u1,u2和u3。這時(shí)要對(duì)兩個(gè)不同的Item做推薦評(píng)分請(qǐng)求,也就是一個(gè)請(qǐng)求里面有兩個(gè)Item,這兩個(gè)Item分別為I1和I2,這兩個(gè)Item分別有三個(gè)特征,I1對(duì)應(yīng)I11,I12,I13,以此類推,這樣構(gòu)成一個(gè)batch size為2的推理請(qǐng)求。從圖中可以看到,因?yàn)槭墙o同一個(gè)用戶推薦兩個(gè)不同的Item,Item側(cè)的特征是不同的,但是用戶的特征被重復(fù)了兩次。
上面的例子只以2個(gè)Item為例,但是實(shí)際線上的服務(wù)一個(gè)推理請(qǐng)求會(huì)帶100個(gè)Item甚至更多,因此用戶的特征也會(huì)被重復(fù)100次甚至更多,重復(fù)的用戶特征不僅增加了傳輸?shù)膸挘以黾恿颂卣魈幚頃r(shí)的計(jì)算量,因此業(yè)務(wù)非常希望能解決這個(gè)問題。
這個(gè)問題的本源要從TensorFlow 的模型訓(xùn)練代碼說起。TensorFlow 訓(xùn)練時(shí)的每一條樣本是某個(gè)用戶對(duì)某個(gè)Item的行為,然后經(jīng)過shuffle和batch后進(jìn)入到訓(xùn)練模型,這時(shí)候一個(gè)batch里面的數(shù)據(jù)肯定包含了多個(gè)用戶行為的樣本,這個(gè)和線上推理服務(wù)的輸入數(shù)據(jù)格式是完全不同的。
如何解決這個(gè)問題?最簡(jiǎn)單的想法,如果在線上服務(wù)就只發(fā)送一條用戶特征會(huì)怎么樣?快速的嘗試就可以知道特征數(shù)據(jù)進(jìn)入到模型輸入層的時(shí)候會(huì) concat失敗。這是因?yàn)镮tem特征的batch size是多個(gè),而用戶特征的batch size只有1,示例如下:

為了解決concat失敗的問題,單純先從模型的角度來看,可以考慮在進(jìn)入到輸入層之前把用戶特征還原到和Item特征同樣的batch size,如下圖所示。

顯然這個(gè)從數(shù)學(xué)模型上是可行的,接下來就是怎么在TensorFlow 的代碼里面實(shí)現(xiàn)這個(gè)想法。這里需要注意的是復(fù)制的操作只能在線上服務(wù)的模型里面,不能在訓(xùn)練的模型里面。
目前TF Estimator 接口在推薦類算法的應(yīng)用比較常見,而Estimator 接口提供了很好的模型區(qū)分方法,通過判斷ModeKeys為`tf.estimator.ModeKeys.PREDICT`時(shí)是線上的服務(wù)模型,ModeKeys為`tf.estimator.ModeKeys.TRAIN`時(shí)是訓(xùn)練模型,下面是示例代碼:

在實(shí)際的模型上,需要將User和Item的feature column區(qū)分開來分別傳入,這個(gè)對(duì)原來的模型代碼改動(dòng)比較大,batch size的獲取可以通過判斷Item特征的長(zhǎng)度來獲取,這里不再贅述。
在實(shí)際的上線過程中,團(tuán)隊(duì)經(jīng)歷了兩個(gè)階段,第一個(gè)階段是只對(duì)算法模型代碼做修改,在處理用戶特征時(shí)只取第一維,但是實(shí)際發(fā)送的推理請(qǐng)求還是會(huì)把用戶特征重復(fù)多次;第二個(gè)階段才把發(fā)送的推薦請(qǐng)求優(yōu)化成只發(fā)送一份用戶特征,這個(gè)時(shí)候模型代碼不需要再做修改,已經(jīng)自動(dòng)適配。

如上圖所示,第一階段的時(shí)候用戶特征的輸入還是重復(fù)多次,在模型中,對(duì)用戶特征只取第一維再進(jìn)行特征處理,示例代碼如下:

上面的模型代碼可以同時(shí)適配推理請(qǐng)求發(fā)送重復(fù)的用戶特征,或者只發(fā)送一條用戶特征。因此在第二階段的時(shí)候,不需要再修改模型代碼,只需要優(yōu)化發(fā)送推理請(qǐng)求的引擎?zhèn)却a。
經(jīng)過這樣的優(yōu)化,線上推理服務(wù)不需要重復(fù)發(fā)送用戶特征,不僅節(jié)約了帶寬,而且減少了序列化的消耗。對(duì)一個(gè) batch 中的用戶特征只做一份數(shù)據(jù)的Feature Column轉(zhuǎn)換,然后做復(fù)制操作,復(fù)制消耗的時(shí)間遠(yuǎn)遠(yuǎn)小于轉(zhuǎn)換的時(shí)間。
這里其實(shí)還可以做進(jìn)一步的優(yōu)化,將復(fù)制操作延后到第一層神經(jīng)網(wǎng)絡(luò)的矩陣乘后面,這樣可以減少第一個(gè)矩陣乘的部分計(jì)算消耗。如果用戶特征的維度占比比較高,優(yōu)化的效果會(huì)比較明顯。
06
總結(jié)
本文介紹了愛奇藝深度學(xué)習(xí)平臺(tái)在實(shí)踐過程中總結(jié)的一些TensorFlow Feature Column優(yōu)化。經(jīng)過這些優(yōu)化,線上的推理服務(wù)性能效率提升一倍以上,p99延遲降低達(dá)到50%以上。而且相比較于做op fuse,模型圖修改等優(yōu)化,這些優(yōu)化在業(yè)務(wù)實(shí)際中也比較容易去落地。
最后,我們還是要肯定TensorFlow Feature Column給推薦類算法帶來的特征處理便利性,它將整個(gè)特征的處理抽象出來,算法只要稍微適配一下樣本特征就可以很快的做迭代和實(shí)驗(yàn)。
參考文獻(xiàn)
1. https://www.tensorflow.org/tutorials/structured_data/feature_columns
2. https://www.tensorflow.org/api_docs/python/tf/feature_column
3. https://github.com/tensorflow/tensorflow
— 完 —
點(diǎn)這里??關(guān)注我,記得標(biāo)星呀~
前線推出學(xué)習(xí)交流一定要備注:研究/工作方向+地點(diǎn)+學(xué)校/公司+昵稱(如JAVA+上海
掃碼加小編微信,進(jìn)群和大佬們零距離
后臺(tái)回復(fù)“電子書” “資料” 領(lǐng)取一份干貨,數(shù)百面試手冊(cè)等你 開發(fā)者技術(shù)前線 ,匯集技術(shù)前線快訊和關(guān)注行業(yè)趨勢(shì),大廠干貨, 是開發(fā)者經(jīng)歷和成長(zhǎng)的優(yōu)秀指南。

