京東電商推薦系統實踐

  • 2019 年 11 月 21 日
  • 筆記

分享嘉賓:孟崇 京東 推薦架構負責人

編輯整理:Hoh Xil

內容來源:DataFun AI Talk

出品社區:DataFun

註:歡迎轉載,轉載請註明出處

今天為大家分享下京東電商推薦系統實踐方面的經驗,主要包括:

  • 簡介
  • 排序模組
  • 實時更新
  • 召回和首輪排序
  • 實驗平台

▌簡介

說到推薦系統,最經典的就是協同過濾,上圖是一個協同過濾的例子。協同過濾主要分為倆種:user-based 基於用戶的協同過濾和 item-based 基於商品的協調過濾。

但是,現在絕大多數推薦系統都不會直接使用協同過濾來做推薦。目前主要用的是 learning to rank 框架。

這裡,是推薦系統的框架,整個推薦系統可以分為兩部分,在線部分和離線部分。

  • 在線部分主要負責當用戶訪問時,如何把結果拼裝好,然後返回給用戶。主要模組有召回、排序和對結果的調整。
  • 離線部分主要是對用戶日誌的數據分析,應用於線上。

整個推薦系統大概就是這樣的一個框架。

和新聞、影片這類的內容推薦相比,電商推薦系統又有一些特殊的地方,比如:

優化方向(點擊、銷售額、時長、用戶留存等)。另外,電商中推薦的內容也會有很多種,尤其像是活動類的內容,很多推薦都是演算法和人工運營共同完成的。這就是電商推薦和新聞推薦等的區別之處。

我們展開看下在線推薦系統:

除了剛才說的召回和排序以及最終的調整之外,還有實踐過程中的一些細節。

  • 召回:這裡召回會有很多種方法,如協同過濾,熱門商品、實時促銷等和應用場景相關的召回,還有一些基於 KNN 的召回。
  • 過濾:召回之後,會進行過濾,主要是和應用場景相關,如已購商品過濾掉、沒有庫存的過濾掉,或者敏感的商品過濾掉等等這些邏輯。
  • 排序:排序目前主要用到的是 DNN 模型,某些流量比較小的地方會用到 GBDT。
  • 過濾:排序之後還會有些分頁、同商品過濾等邏輯。

調整:最終調整過程中,主要有兩部分邏輯,多樣性和探索邏輯。

▌排序模組

1. 模型結構

深度學習 ranking 模型結構我們不作為重點討論,這裡列舉了一種最經典的模型,它們都用到了很多 id 的 Embedding,然後這些 Embedding 規模都很大,這樣訓練和上線都比較耗時。因此,我們做了一些優化,比如做分散式的訓練,並且會有一套 Pipeline 來完成模型的上線。另外,雖然模型很複雜,並且能帶來很好的效果,但是特徵工程還是必不可少的,很多指標的提升還是依賴於特徵工程,當然也包括一些模型調整的工作。

2. 實踐

那麼如何把這些模型落地呢?我們看下整個模型的上線過程:

首先最重要的部分是模型訓練平台和排序服務,因為很多深度模型計算量要求很高,為了能達到比較快的效果,需要部署單獨的排序服務。目前比較流行的是 TensorFlow serving,可以很快速的來上線一個深度模型,並充分利用對分片、單機並行,達到很高的計算效率。

模型線上線下一致性問題對於模型效果非常重要,我們使用特徵日誌來實時記錄特徵,保證特徵的一致性。這樣離線處理的時候會把實時的用戶回饋,和特徵日誌做一個結合生成訓練樣本,然後更新到模型訓練平台上,平台更新之後在推送到線上,這樣整個排序形成了一個閉環。

3. 實時更新

我們的特徵和模型都需要做實時的更新。因為我們經常需要很快的 catch 一些實時的訊號,比如需要實時的用戶畫像來抓住實時的用戶興趣的變化,還比如需要抓住實時的商品畫像,因為經常會有一些活動或者爆品,我們需要快速的捕捉這些訊號,並應用到推薦中。另外還有一些實時的召回和特徵,比如一些交叉的特徵,實時的點擊率,實時訂單等特徵。

除了特徵外,模型也需要實時更新,對於電商場景來說這是有一定困難的,因為訂單是有延時的,延時可能是十幾分鐘到十幾小時不等,這樣實時模型更新上就會採取一些保守的策略,比如用點擊率對模型做些微調,然後訂單數據再通過離線來獲得,這屬於業務場景的限制。

▌思考

排序可以算是推薦系統中比較重要的一個環節,但是只有排序肯定是不夠的,事實上,有一些問題是目前的排序框架無法解決的:

  • 排序得到的結果非常相似,影響體驗。
  • 有多個優化目標,需要一個平衡(點擊率、訂單金額、用戶交互時長等)。
  • 計算能力有限,如果有無限的計算力,可以直接對全部候選集進行排序。

1. 多樣性

使用模型輸出的結果一般都會非常相似,如果直接給用戶看體驗會很差,因此在模型之後我們需要加入多樣性的邏輯。

比較通用的解決辦法是多樣性的 ranking,這是一個貪心演算法,從第一個商品開始選,當選第二個商品的時候,會重新計算下候選集中每個商品的 score,然後選擇一個 score 最高的。我們的方法是看 novelty score 候選商品的產品詞分布和之前 N 個商品的產品詞分布的 KL 距離。這樣做的思路,就是選一個和已有商品最不像的商品,來更好的保證商品推薦結果的多樣性。

由於純基於演算法的多樣性可能會出現 badcase,因此還需要一個規則來進行兜底,確保在極端情況下結果也能接受。

最後,我們思考一個問題,有沒有更好的方法實現多樣性的邏輯呢?當然有,比如是否可以考慮使用 list wise ranking。這裡只是為大家分享一個比較容易的,並且效果比較好的方法。

2. 多目標

我們的優化目標有很多,比如點擊、轉化、時長等,問題會變得比較複雜,單一的模型訓練很難覆蓋到所有指標。另外,經常我們需要在各個指標之間進行權衡,因此可調試性也非常重要。

一種很有用的方式是多模型 ranking,然後用某種方式把所有模型的結果 combine。

這也體現了一個思想,在演算法的實際應用中,其實需要在演算法的先進性和系統可維護性、可調試性之間做一個平衡。往往 paper 里很有創意的演算法落地的時候是有些困難的。

3. 多輪排序

下面我們討論一下多輪排序的問題。多輪排序是 learning to rank 實踐中很重要的一個思想。使用多輪排序主要是因為計算資源的限制,無法使用複雜的模型進行大規模的候選集排序。右圖描述了一個多輪排序的框架。這像是一個漏斗模型,從上往下模型的複雜度是遞增的,同時候選集是逐漸減少的,就是越到後面用越複雜的模型來保證效果更好,越到前面可能只需要簡單的模型來保證能拿到一些商品就可以了。

這樣會存在一個問題,由於訓練樣本可能有偏,導致只有被用戶看到的樣本才有 label,但是一般不會有太大的影響。

▌基於索引的首輪排序

1. 索引召回

下面我們重點介紹一下第一輪排序。倒排索引很常見,是資訊檢索里常用的工具。它通過把 doc 的內容索引到 doc id 的方式,快速通過內容來查找 doc。我們很多召回都是通過索引實現的。這裡我列舉了一些基於索引的召回方式,如 item cf 的 key、產品詞、熱門類目、促銷產品詞等。

雖然索引能夠很大程度上的縮小候選集的範圍,但是經常情況下,第一輪排序的 doc 數量仍然可能會很大。為了保證性能,截斷邏輯是必不可少的。通過情況下可以通過 quality score 截斷,保留品質好的 doc。經過線性的 LR 或者 GBDT 模型就可以有結果了。另外截斷之後需要有些多樣性的邏輯,因為只有在召回的時候保持多樣性,最終結果才會有多樣性。

基於 quality score 截斷是一種 naive 的演算法,這裡我們討論另一種業界也較常用的演算法,wand。wand 其實是 weak and,它的重點是 wand 操作符。wand 操作符是一個布爾操作符,當 Xi wi 比 θ 大時,它的值是1,否則是0。之所以叫做 weak-and,是因為當 w 都取1, θ 取 K 時,wand 操作符就變成了 and,當 w 取1,θ 取1時,wand 操作符就變成了 or。可以看出 wand 是介於 and 和 or 之間的操作。對 Xi wi 求和的操作其實和我們線性模型很相似。通過 wand 操作符,我們可以定義一些上界,因為是倒排索引,可以給每個索引鏈賦予一個估計值,這樣就可以拿到權重上界 UBt,這樣通過和 wand 操作符對比,就可以快速的判斷 UBt 是否滿足條件,如果滿足條件就可以快速的把一些 doc 扔掉,這樣就可以快速的使用線性模型對全戶做 ranking。可以看到,基於線性模型的分數做截斷,比完全基於 quality score 截斷的策略要稍微好一點。

這裡我列了 paper 中 wand 演算法的偽程式碼。出於時間關係,我們不會過演算法邏輯的細節。我認為它的主要的思路是通過快速使用 upper bound 做截斷和跳轉,可以略過很多明顯不符合的候選 doc,從而減少計算 score 的次數。當然這種方法對於線性模型來說,有一個缺點,當我們需要多樣性的時候,沒辦法很好的實現在模型中增加多樣性的。

wand 演算法目前已經應用非常廣泛了,在很多開源的索引如 lucene 中,也會用到這種方法快速計算文本相關分。

剛剛我們介紹了使用倒排索引做第一輪排序,以及一個常見的排序加速演算法,回過來我們思考一下倒排索引本身,它適用於什麼場景,不適用於什麼場景。

首先它適用於 kv 查找這種場景,並且 kv 查找也屬於實際應用很多的情況。但是對於更複雜的方式,類似 graph 的召回方式不友好,比如找用戶看過的商品中相似商品的相關商品,這時實現起來會比較麻煩,這是它的一些限制。再一個,我們需要有較好的截斷策略,例如底層使用 relevence score 截斷,排序使用 GBDT。

當然,索引還會受到機器本身的記憶體限制,限於機器的大小,很多時候我們需要多機分片部署索引,這樣會帶來一定的複雜性。雖然有些限制,但是索引是目前應用很廣泛、有效的方式,包括在推薦、搜索等領域都會使用到。

2. KNN 召回

除了索引召回,KNN 也是現在較常用的一種召回方式。首先,我們把所有的候選集轉換成 embedding,我們把用戶興趣也可以轉換成 embedding,通過定義 embedding 之間距離計算公式,我們可以定義 KNN 召回問題,也就是在全部候選池中,找到與用戶最接近的 k 個結果。

定義好 KNN 召回的問題,下一步就是如何找到最近的 K 個候選集。由於整個候選集非常大,每次都使用用戶的 embedding 去全量計算距離是不現實的,只能使用一種近似演算法。我們今天分享其中的一種近似演算法。是 facebook 開源的 KNN 計算庫 faiss 使用的。其原理:

首先需要對全部候選集進行分塊,每一塊都會有自己的質心。paper 中使用 Lloyd 演算法,將整個空間劃分開。分塊後,就需要對每一塊構建索引,進而通過索引實現快速檢索的功能。

右圖是索引構建和檢索的方法。

上半部分是如何構建索引(這裡的優化點是使用了二級索引):首先拿到 y 候選集之後,做一個 quantizer 分類得到一個一級索引,把它放到索引表中,另外還得到殘差 compute residual,可以對殘差再進行一次 quantizer,得到一個二級索引,通過兩級索引來加快檢索的速度,同理,在真正的 quary 的時候,拿到的是用戶的向量 x,先做一個 quantizer,得到 k 近鄰的一級索引,然後查找 k 個一級索引,同時拿到 k 個二級索引,然後在二級索引中查找,然後這裡還有很多加速的演算法(這裡就不展開了),通過這樣一種多層的查詢方式來做到加速 K 近鄰的演算法。

PS:關於 KNN 的一些思考,KNN 是一種有效的方式,但是不是唯一有效的方式。比如之後分享的 TDM,能夠比 KNN 更加靈活。

▌實驗平台

最後簡單介紹下分層實驗平台,因為大家想快速迭代特徵和模型,離不開實驗,經常會遇到的情況是實驗流量不夠用了,這時就需要對實驗做分層。分層的邏輯見右圖,通過在不同的 Layer 使用不同的哈希函數,保證每個 Layer 之間流量是正交的,這樣就可以在不同的 Layer 上做不同的實驗。

分層實驗的具體做法:召回->排序->後處理->業務,另外還有一部分對齊流量,用來做全量的驗證。

分層的優點,可以用於做實驗的流量多,適合快速迭代;缺點,需要嚴格控制層與層之間的關係,防止相互干擾。本次分享就到這裡,謝謝大家。

嘉賓介紹

孟崇,京東推薦架構負責人。負責京東推薦系統的 ranking 演算法架構和模型訓練。碩士畢業於中科院自動化所,先後在 yahoo、獵豹移動和京東從事推薦的工作,有豐富的推薦演算法經驗。