導讀:隨着展現廣告業務數據量的日益增加,360展現廣告召回系統也隨之也進行不斷升級改進。本次介紹主要從召回系統演進的角度詳細闡述工程實踐中的算法應用、技術難點以及解決方案。主要分紅三塊:第一個是360展現廣告的大體介紹,第二個是總體架構介紹,第三個就是今天的主題召回模塊的演進脈絡。python
▌1. 展現廣告介紹c++
1.1 展現廣告業務介紹算法
展現廣告主要是在RTB程序化購買框架下運行的,首先有媒體方,好比新浪、搜狐等市面上的各大媒體,咱們都已經接入。媒體在一次曝光產生以前會把此次曝光發送給Ad Exchange(ADX)模塊進行拍賣,360的ADX平臺叫360 Max。ADX平臺將該曝光發送給多個競價平臺DSP,DSP來決定是否對該次曝光進行競價,360的DSP平臺叫360點睛DSP。廣告主們會針對本身的需求設置廣告投放策略,DSP平臺會從廣告主設置的廣告投放庫中根據該次曝光的特徵匹配出合適的創意(廣告)候選集返回給ADX。通常要求DSP的響應時間爲100ms左右,除去網絡傳輸耗時,留給DSP模型的時間只有幾十毫秒,其中間各個模塊的時間就更少。ADX從接收到的全部候選集中選擇出價最高的廣告進行曝光。api
1.2 常見展現廣告性能優化
目前在360場景下主要有這幾種廣告類型,第一種類型是廣告側邊欄,包含具體投放廣告的詳情頁。第二種類型是總體的開屏廣告,主要是品牌廣告。第三種類型是在信息流場景下,將廣告嵌入新聞上下文中且與文章內容形式保持一致。cookie
▌2. 展現廣告總體架構介紹網絡
總體架構流程:架構
流量從ADX發送給DSP端,DSP拿到流量以後交給檢索召回模塊,咱們叫Ad Search模塊,Ad Search模塊分爲兩個部分,一個是Ad Search Root模塊,對流量進行識別,判斷其爲哪一種類型的流量,好比keyword流量、信息流流量、banner流量,而後交給不一樣級別的Ad Search Leaf模塊對廣告進行召回,選出相匹配的廣告初步候選集,該模塊就是咱們今天的主題。而後將選出的廣告初步候選集交給Ad Selector模塊進行精排,即對廣告的CTR或者CVR預估,進行打分,選出top K個廣告返回給DSP Server。同時DSP Server會將點擊、曝光、後續的日誌寫入kafka,並進行日誌落地,日誌落地後會進入後續的ETL離線流程,用於後續粗排、點擊率等模型生成訓練樣本使用,另外一部實時數據日誌數據流會通過反做弊模塊後過一遍ctr預估的online learning和實時曝光反饋的online feedback。 app
▌3. 召回模塊的演進脈絡框架
3.1 檢索召回模塊
Ad Search Leaf召回模塊展開來主要分三個階段,召回、過濾和粗排。召回模塊接收到DSP給流量請求中包含了用戶信息和上下文信息。廣告主設置的廣告投放存在Ad Meta db中,而後建成相關的廣告索引(Ad index),投放更新後纔會實時更新這個索引。RTDB主要存儲用戶標籤數據,標籤數據一部分是經過請求中帶的用戶信息和上下文信息進行用戶標籤的實時更新,另外一部分是經過線下離線全量更新。召回模塊就是結合這兩端選出初步的廣告候選集,而後進入過濾模塊(正排模塊),過濾方法主要包括基於規則、黑白名單、廣告主預算pacing過濾。最後進入粗排模塊,對初選的廣告候選集按評價函數模型進行打分,但沒有精排模塊那麼複雜,相對比較簡單,好比最開始基於核心特徵的LR模型,後續升級過基於特徵交叉的FFM模型。
3.2 召回通路
接下來說一下在咱們檢索召回中一些通路,咱們是進行多路召回的,其中有三種類型,第一種是上下文、第二種是用戶行爲、第三種是當前正在作的深度召回。
上下文召回有這幾種類型,第一種是基於圖片的,在內容頁場景下,一個明星穿了哪件衣服,根據這件衣服投放哪件商品,作法是將圖片向量化,計算廣告商品與圖片向量的類似度進行召回。第二種是基於標題的,主要是基於文本NLP相關模型進行召回。第三種是基於lbs的,廣告主自身設定某個標籤區域進行投放,在該區域內進行標籤匹配召回,屬於布爾召回。
用戶行爲召回有這幾種類型,第一種是基於興趣的,基於用戶歷史行爲創建用戶畫像,打上興趣標籤,進行布爾召回。第二種是基於Query的,利用用戶的query歷史行爲,進行NLP相關模型進行召回,與基於標題的方式相似。第三種是基於訪問行爲,利用廣告主回傳的用戶商品行爲,採用Item CF、ALS、Neural MF等模型進行召回。
最後一種召回通路是深度召回,主要是把user profile、媒體特性、上下文特性等特徵結合起來進入深度模型中進行召回,接下來會具體介紹下這個是如何作的。
3.3 基於文本的召回
上面講的標題和query召回主要是文本召回,包括幾種,第一種是精準匹配,有徹底匹配和基於ngram的TF-IDF提取核心詞匹配,屬於比較簡單的方式。第二種是模糊匹配,有word2vec和DSSM語義化模型,將文本語義向量化,而後按向量化檢索召回。第三種是普遍匹配,是把語義化向量聚類成多個標籤,而後按標籤召回。
3.4 召回模塊演進
咱們的召回模塊演進主要是分紅三個階段,第一種是布爾召回,剛纔介紹的信息標籤類型和lbs都是基於這種。第二種是向量檢索召回,利用深度學習模型將廣告、用戶等信息都映射爲向量,而後進行向量檢索召回。第三種是咱們如今在作的基於深度樹的匹配。三個階段是由淺入深的過程,接下來咱們依次介紹這三個階段。
3.5 布爾召回
布爾召回在早期檢索中都會用到的,咱們的方式是基於樹+維度bitMap分組+哈希表。廣告主設置定向組合,好比訪問某些網站的人羣、有特定興趣的人羣等其餘各類定向組合。而請求中會帶有用戶標籤,問題就變成了怎麼找到與用戶標籤匹配的定向組合廣告?布爾召回本質就是基於倒排索引的布爾運算,因此關鍵在於倒排索引怎麼構建?構建的方式有兩個層級,第一層索引是將廣告主的投放配置進行分解分組,每個組爲一個conjunction,一個廣告投放會對應多個conjunction,這樣就能夠創建conjunction到廣告投放的倒排索引。第二層索引在於根據用戶標籤找到對應的conjunction,咱們的標籤是一個int64類型的整數,高八位的某些bit位會作一些標識,來區分這個標籤屬於哪一種類型,拿到用戶的標籤作位運算,找到bitMap分組的conjunction list。而後到基於每一個conjunction到第一層取出對應的廣告主集合,最後計算每一個集合的交併,獲得最終召回的廣告候選集。
在實際使用中布爾召回有一些問題,好比倒排表有部分conjunction對應的廣告集合很長(>1w),檢索性能不好,咱們的實際解決方案是在布爾表達式中作一個轉換,好比將先並後交的布爾運算改成先交後並的布爾運算,能極大優化檢索的性能。
3.6 向量化召回
咱們在實際向量化召回中有兩種類型,第一種是相似於早期YouTube DNN,把用戶變成一個向量,把item也變成一個向量,最後作一個向量類似度檢索。第二種是用戶相關歷史內容變成一個向量,再進行向量類似度檢索。兩種類型都是基於兩個向量的類似度進行檢索,獲得item候選集。
實際中咱們選擇的是微軟提出的基於深度語義檢索模型(DSSM),該模型的輸入是一組相關的Query和Document,以及兩個與Query不相關的Documents,而後分別dense爲embedding向量,再通過NN網絡,再作softmax打分,獲得三個分數,訓練目標就是是相關的Document分數儘量高,不相關的Documents儘量低。中間的NN層能夠有多個選擇,能夠爲多層FC,也能夠相似textcnn那樣爲CNN+FC,或者爲RNN。
在經過模型向量化以後就涉及到向量索引該怎麼選擇?下面介紹下常見的幾種。第一種爲LSH(局部敏感哈希)索引,方法就是基於多個哈希函數進行分桶,比較耗內存。第二種爲faiss提出的IVF Flat,方法是構建由聚類中心向量構成的倒排表,針對輸入向量,先找到聚類中心向量,再基於KNN方式進行檢索,這種類型是保留精度的,效果跟直接暴力檢索很是接近,但性能有很大的提高。第三種爲IVF PQ,也是基於倒排,與IVF Flat區別在於在向量上作了有損壓縮或PC降維,好處在於省內存,可是帶來了實際檢索精度的丟失。這三種能夠根據實際業務場景進行選擇。
3.7 深度樹召回
上面講的布爾召回和向量化召回都有必定的缺點,好比布爾召回的有些conjunction的候選列表很是長,容易遇到性能瓶頸。向量檢索會侷限於模型的向量表達是否準確,並且向量空間有限。這裏參考了阿里媽媽提出的深度樹匹配(TDM)模型,深度樹匹配能解決全量檢索的性能問題和模型精度問題。如何構建一個深度檢索樹呢?在廣告場景下選取的是基於ecpm(千次曝光預期收益)最大堆方式,最大堆樹下當前層最優Top K孩子節點的父親必然屬於上層父節點的最優Top K,這樣就能夠從根節點逐層遞歸向下挑選Top K直至葉子層,同時極大提升檢索速度。爲了簡便起見,咱們構建樹的形式是二叉樹,則檢索Top K的時間複雜度是2KlogN,N爲item個數,且隨着N的增加,相比暴力全量檢索的性能提高更爲明顯。
深度樹模型的大體結構如上圖所示,分爲兩部分,左邊是流量端,包括用戶profile特徵(如標籤、cookie歷史行爲、頻次等)和上下文特徵(如媒體廣告位屬性、黑白名單、時間特性等)。首先把這兩種特徵dense化變成embedding,再作特徵交叉組合,在咱們場景下特徵交叉採用的是IPNN方式,而後再通過FC+BN的多層NN網絡,最終轉化爲Search端的embedding,線上處理時,左邊這個流程每次請求都會計算。右邊是候選集端,是一顆檢索樹,葉節點就包含了廣告投放信息,包括尺寸、類目、行業等廣告主設置的定向信息,葉節點和父節點本質都是同一維度的embedding,都會進入FC+BN層,造成與Search端embedding相同維度的Tree Leaf和Parent embedding。左右兩端的embedding作交叉+concat操做進入FC+BN層,最終獲得一個分數,與樣本label按交叉熵損失進行訓練。總體的模型架構就是這樣。
那咱們要怎麼準備這顆樹模型的樣本呢?有兩部分,分別爲leaf樣本和parent樣本,對於leaf節點,選取精排模型輸出的虛擬曝光Top3做爲正例。負例包括兩部分,第一部分是在正例葉子節點同層,隨機選取其餘葉子節點做爲負例,第二部分是選擇粗排階段的一些負分item標識爲負例。對於父節點,基於ecpm最大堆原理,將正例向上回溯的全部父節點都標記爲正例,同層隨機標記爲負例。總體的樣本規模是用了7天的採樣曝光樣本,大概在5000千萬左右。
接下來咱們該如何生成這顆樹呢?咱們這裏有兩種方式,第一種方式就是隨機生成,將item隨機放到葉結點上,固然也能夠根據標籤體系手動把相近的item放在同一個父節點下,基於隨機深度樹訓練一輪,將葉子結點的embedding導出來,而後聚類,這樣就能夠根據聚類的結果從新生成一顆樹,而後再去訓練,一直迭代到指標變化不大。第二種方式就是自下而上合成樹,先當這顆樹不存在,只訓練每一個葉子結點的embedding,而後根據葉子結點的embedding向上聚類回溯成一顆樹(kmeans或者根據曝光頻次來聚類),接着就能夠與第一種方式同樣一直迭代直到指標變化不大。
咱們最後選擇方式是第二種方式,第一步就是先訓練葉子,把葉子embedding導出來,而後聚類生成一顆樹,基於這顆樹結構從新訓練葉子結點和父節點的embedding,獲得最終上線的模型。
接着咱們來說一下這個模型的loss,咱們的目標是去擬合曝光,因此loss由兩部分組成,第一部分是交叉熵損失,去擬合對應的樣本是否曝光。第二部分是triplet loss,含義是葉子節點中正樣本之間距離儘量相近,而與負樣本之間的距離儘量遠,來約束葉子距離。第一部分loss考慮的是擬合曝光,並無考慮點擊,因此能夠把實際用戶點擊的樣本進行加權,把即曝光又點擊的樣本着重考慮,體現重要性。
線上如何檢索呢?由於咱們是基於最大堆的,因此採用的基於beam search的方式,就是逐層往下的檢索,每一層都保留K個節點並往下擴展。
實際線上投放是有變化的,廣告主會新增一些投放,對於這種新增投放怎麼添加到這顆樹中呢?好比新增一個item,咱們會根據訓練好的模型把item對應特徵轉換爲embedding,而後把以前訓練的葉子節點的embedding set拿出來,而後作一個類似度檢索,獲得item embedding最接近的葉子節點37,將該新item加入這個葉子節點37的list中去(線上訓練時,葉子節點實際是一個cluster,一個列表,並非一個單一的節點)。
▌4. 性能優化
最後提一下咱們在實際中作的一些性能優化工做,主要介紹兩方面:
第一方面是在檢索階段,針對樹的最上幾層,會根據Top K的大小,進行跳層(父節點個數<K),針對最後幾層能夠返回cluster,交給後續精排業務去作排序。同時能夠進行多路並行,提升性能,也能夠將訓練的double數據裁剪爲uint8類型,針對相對排序指標影響不是很大。
第二方面是訓練階段,由於咱們是基於tensorflow構建模型的,因此有常見的幾種優化方式,好比避免使用feed dict,使用dataset這種高階能夠並行化的api。同時可使用Timeline Profile等工具分析性能瓶頸,進行鍼對性的優化。針對python GIL的缺點,將高頻調用的python function利用c++實現進行調用,並在調用前將GIL release掉,避免鎖帶來的性能影響。
▌參考資料:
1. Learning Tree-based Deep Model for Recommender Systems
https://arxiv.org/abs/1801.02294
2. 微軟DSSM項目地址:
https://www.microsoft.com/en-us/research/project/dssm/
分享嘉賓▬
王華呈
360 | 資深算法工程師
——END——
DataFun:
專一於大數據、人工智能領域的知識分享平臺。