深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

美團技術團隊發表於2018-12-21

搜尋、推薦和廣告等這些AI主流應用背後的一個核心技術,是排序學習(Learning to Rank)。本文從系統開發工程師的角度做了非常系統通俗的解讀,主要概念,連美美都看懂啦,推薦給大家。

引言

我們正處在一個知識爆炸的時代,伴隨著資訊量的劇增和人工智慧的蓬勃發展,網際網路公司越發具有強烈的個性化、智慧化資訊展示的需求。而資訊展示個性化的典型應用主要包括搜尋列表、推薦列表、廣告展示等等。

很多人不知道的是,看似簡單的個性化資訊展示背後,涉及大量的資料、演算法以及工程架構技術,這些足以讓大部分網際網路公司望而卻步。究其根本原因,個性化資訊展示背後的技術是排序學習問題(Learning to Rank)。市面上大部分關於排序學習的文章,要麼偏演算法、要麼偏工程。雖然演算法方面有一些系統性的介紹文章,但往往對讀者的數學能力要求比較高,也比較偏學術,對於非演算法同學來說門檻非常高。而大部分工程方面的文章又比較粗糙,基本上停留在Google的Two-Phase Scheme階段,從工程實施的角度來說,遠遠還不夠具體。

對於那些由系統開發工程師負責線上排序架構的團隊來說,本文會採用通俗的例子和類比方式來闡述演算法部分,希望能夠幫助大家更好地理解和掌握排序學習的核心概念。如果是演算法工程師團隊的同學,可以忽略演算法部分的內容。本文的架構部分闡述了美團點評到店餐飲業務線上執行的系統,可以作為線上排序系統架構設計的參考原型直接使用。該架構在服務治理、分層設計的理念,對於保障線上排序架構的高效能、高可用性、易維護性也具有一定的參考價值。包括很多具體環節的實施方案也可以直接進行借鑑,例如流量分桶、流量分級、特徵模型、級聯模型等等。

總之,讓開發工程師能夠理解排序學習演算法方面的核心概念,併為線上架構實施提供細顆粒度的參考架構,是本文的重要目標。

演算法部分

機器學習涉及優化理論、統計學、數值計算等多個領域。這給那些希望學習機器學習核心概念的系統開發工程師帶來了很大的障礙。不過,複雜的概念背後往往蘊藏著樸素的道理。本節將嘗試採用通俗的例子和類比方式,來對機器學習和排序學習的一些核心概念進行揭祕。

機器學習

什麼是機器學習

典型的機器學習問題,如下圖所示:


深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

機器學習模型或演算法(Model/Algorithm)會根據觀察到的特徵值(Feature)進行預測,給出預測結果或者目標(Prediction/Target)。這就像是一個函式計算過程,對於特定X值(Feature),演算法模型就像是函式,最終的預測結果是Y值。不難理解,機器學習的核心問題就是如何得到預測函式。

Wikipedia的對機器學習定義如下:

Machine learning is a subset of artificial intelligence in the field of computer science that often uses statistical techniques to give computers the ability to learn with data, without being explicitly programmed.

機器學習的最重要本質是從資料中學習,得到預測函式。人類的思考過程以及判斷能力本質上也是一種函式處理。從資料或者經驗中學習,對於人類來說是一件再平常不過的事情了。例如人們通過觀察太陽照射物體影子的長短而發明了日晷,從而具備了計時和制定節氣的能力。古埃及人通過尼羅河水的漲落髮明瞭古埃及曆法。

深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

又比如人們通過觀察月亮形狀的變化而發明了陰曆。


深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

如果機器能夠像人一樣具備從資料中學習的能力,從某種意義上講,就具備了一定的“智慧”。現在需要回答的兩個問題就是:

  • 到底什麼是“智慧”?

  • 如何讓機器具備智慧?

什麼是智慧?

在回答這個問題之前,我們先看看傳統的程式設計模式為什麼不能稱之為“智慧”。傳統的程式設計模式如下圖所示,它一般要經歷如下幾個階段:

  • 人類通過觀察資料(Data)總結經驗,轉化成知識(Knowledge)。

  • 人類將知識轉化成規則(Rule)。

  • 工程師將規則轉化成計算機程式(Program)。

深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

在這種程式設計模式下,如果一個問題被規則覆蓋,那麼計算機程式就能處理。對於規則不能覆蓋的問題,只能由人類來重新思考,制定新規則來解決。所以在這裡“智慧”角色主要由人類來承擔。人類負責解決新問題,所以傳統的程式本身不能稱之為“智慧”。

所以,“智慧”的一個核心要素就是“舉一反三”。

如何讓機器具備智慧?

在討論這個問題之前,可以先回顧一下人類是怎麼掌握“舉一反三”的能力的?基本流程如下:

  • 老師給學生一些題目,指導學生如何解題。學生努力掌握解題思路,儘可能讓自己的答案和老師給出的答案一致。

  • 學生需要通過一些考試來證明自己具備“舉一反三”的能力。如果通過了這些考試,學生將獲得畢業證書或者資格從業證書。

  • 學生變成一個從業者之後將會面臨並且處理很多之前沒有碰到過的新問題。

機器學習專家從人類的學習過程中獲得靈感,通過三個階段讓機器具備“舉一反三”的能力。這三個階段是:訓練階段(Training)、測試階段(Testing)、推導階段(Inference)。下面逐一進行介紹:

訓練階段

訓練階段如下圖所示:

  • 人類給機器學習模型一些訓練樣本(X,Y),X代表特徵,Y代表目標值。這就好比老師教學生解題,X代表題目,Y代表標準答案。

  • 機器學習模型嘗試想出一種方法解題。

  • 在訓練階段,機器學習的目標就是讓損失函式值最小。類比學生嘗試讓自己解題的答案和老師給的標準答案差別最小,思路如出一轍。

深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

測試階段

測試階段如下圖所示:

  • 人類給訓練好的模型一批完全不同的測試樣本(X,Y)。這就好比學生拿到考試試卷。

  • 模型進行推導。這個過程就像學生正在考試答題。

  • 人類要求測試樣本的總損失函式值低於設定的最低目標值。這就像學校要求學生的考試成績必須及格一樣。

深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

推導階段

推導階段如下圖所示:

  • 在這個階段機器學習模型只能拿到特徵值X,而沒有目標值。這就像工作中,人們只是在解決一個個的問題,但不知道正確的結果到底是什麼。

  • 在推導階段,機器學習的目標就是預測,給出目標值。


深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

排序學習

什麼是排序學習?

Wikipedia的對排序學習的定義如下:

Learning to rank is the application of machine learning, typically supervised, semi-supervised or reinforcement learning, in the construction of ranking models for information retrieval systems. Training data consists of lists of items with some partial order specified between items in each list. This order is typically induced by giving a numerical or ordinal score or a binary judgment (e.g. "relevant" or "not relevant") for each item. The ranking model's purpose is to rank, i.e. produce a permutation of items in new, unseen lists in a way which is "similar" to rankings in the training data in some sense.

排序學習是機器學習資訊檢索系統裡的應用,其目標是構建一個排序模型用於對列表進行排序。排序學習的典型應用包括搜尋列表、推薦列表和廣告列表等等。

列表排序的目標是對多個條目進行排序,這就意味著它的目標值是有結構的。與單值迴歸和單值分類相比,結構化目標特點要求解決兩個被廣泛提起的概念:

  • 列表評價指標

  • 列表訓練演算法

列表評價指標

以關鍵詞搜尋返回文章列表為例,這裡先分析一下列表評價指標要解決什麼挑戰。

  • 第一個挑戰就是定義文章與關鍵詞之間的相關度,這決定了一篇文章在列表中的位置,相關度越高排序就應該越靠前。

  • 第二個挑戰是當列表中某些文章沒有排在正確的位置時候,如何給整個列表打分。舉個例子來說,假如對於某個關鍵詞,按照相關性的高低正確排序,文件1、2、3、4、5應該依次排在前5位。現在的挑戰就是,如何評估“2,1,3,4,5”和“1,2,5,4,3”這兩個列表的優劣呢?

列表排序的評價指標體系總的來說經歷了三個階段,分別是Precision and Recall、Discounted Cumulative Gain(DCG)和Expected Reciprocal Rank(ERR)。我們逐一進行講解。

Precision and Recall(P-R)

本評價體系通過準確率(Precision)和召回率(Recall)兩個指標來共同衡量列表的排序質量。對於一個請求關鍵詞,所有的文件被標記為相關和不相關兩種。

Precision的定義如下:

深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

Recall的定義如下:


深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

舉個列子來說,對於某個請求關鍵詞,有200篇文章實際相關。某個排序演算法只認為100篇文章是相關的,而這100篇文章裡面,真正相關的文章只有80篇。按照以上定義:

準確率=80/100=0.8

召回率=80/200=0.4

Discounted Cumulative Gain(DCG)

P-R的有兩個明顯缺點:

  • 所有文章只被分為相關和不相關兩檔,分類顯然太粗糙。

  • 沒有考慮位置因素。

DCG解決了這兩個問題。對於一個關鍵詞,所有的文件可以分為多個相關性級別,這裡以rel1,rel2...來表示。文章相關性對整個列表評價指標的貢獻隨著位置的增加而對數衰減,位置越靠後,衰減越嚴重。基於DCG評價指標,列表前p個文件的評價指標定義如下:

深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

對於排序引擎而言,不同請求的結果列表長度往往不相同。當比較不同排序引擎的綜合排序效能時,不同長度請求之間的DCG指標的可比性不高。目前在工業界常用的是Normalized DCG(nDCG),它假定能夠獲取到某個請求的前p個位置的完美排序列表,這個完美列表的分值稱為Ideal DCG(IDCG),nDCG等於DCG與IDCG比值。所以nDCG是一個在0到1之間的值。

nDCG的定義如下:

深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

IDCG的定義如下:

深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

|REL|代表按照相關性排序好的最多到位置p的結果列表。

Expected Reciprocal Rank(ERR)

與DCG相比,除了考慮位置衰減和允許多種相關級別(以R1,R2,R3...來表示)以外,ERR更進了一步,還考慮了排在文件之前所有文件的相關性。舉個例子來說,文件A非常相關,排在第5位。如果排在前面的4個文件相關度都不高,那麼文件A對列表的貢獻就很大。反過來,如果前面4個文件相關度很大,已經完全解決了使用者的搜尋需求,使用者根本就不會點選第5個位置的文件,那麼文件A對列表的貢獻就不大。

ERR的定義如下:

深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

列表訓練演算法

做列表排序的工程師們經常聽到諸如Pointwise、Pairwise和Listwise的概念。這些是什麼東西呢,背後的原理又是什麼呢?這裡將逐一解密。

仍然以關鍵詞搜尋文章為例,排序學習演算法的目標是為給定的關鍵詞對文章列表進行排序。做為類比,假定有一個學者想要對各科學生排名進行預測。各種角色的對應關係如下:

深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

首先,我們要告訴學者每個學生的各種屬性,這就像我們要告訴排序演算法文件特徵。對於目標值,我們卻有三種方式來告訴學者:

  • 對於每個學科,我們可以告訴學者每個學生的成績。比較每個學生的成績,學者當然可以算出每個學生的最終排名。這種訓練方法被稱為Pointwise。對於Pointwise演算法,如果最終預測目標是一個實數值,就是一個迴歸問題。如果目標是概率預測,這就是一個分類問題,例如CTR預估。

  • 對於每個學科,我們可以告訴學者任意兩個學生的相互排名。根據學生之間排名的情況,學者也可以算出每個學生的最終排名。這種訓練方法被稱為Pairwise。Pairwise演算法的目標是減少逆序的數量,所以是個二分類問題

  • 對於每個學科,我們可以直接告訴學者所有學生的整體排名。這種訓練方法被稱為Listwise。Listwise演算法的目標往往是直接優化nDCG、ERR等評價指標。

這三種方法表面看上去有點像玩文字遊戲,但是背後卻是工程師和科學家們不斷探索的結果。最直觀的方案是Pointwise演算法,例如對於廣告CTR預估,在訓練階段需要標註某個文件的點選概率,這相對來說容易。Pairwise演算法一個重要分支是Lambda系列,包括LambdaRank、LambdaMart等,它的核心思想是:很多時候我們很難直接計算損失函式的值,但卻很容易計算損失函式梯度(Gradient)。這意味著我們很難計算整個列表的nDCG和ERR等指標,但卻很容易知道某個文件應該排的更靠前還是靠後。Listwise演算法往往效果最好,但是如何為每個請求對所有文件進行標註是一個巨大的挑戰。

線上排序架構

典型的資訊檢索包含兩個階段:索引階段和查詢階段。這兩個階段的流程以及相互關係可以用下圖來表示:

深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

索引階段的工作是由索引器(Indexer)讀取文件(Documents)構建索引(Index)。

查詢階段讀取索引做為召回,然後交給Topn Retriever進行粗排,在粗排後的結果裡面將前n個文件傳給Reranker進行精排。這樣一個召回、粗排、精排的架構最初是由Google提出來的,也被稱為“Two-Phase Scheme”。

索引部分屬於離線階段,這裡重點講述線上排序階段,即查詢階段。

三大挑戰

線上排序架構主要面臨三方面的挑戰:特徵、模型和召回。

  • 特徵挑戰包括特徵新增、特徵運算元、特徵歸一化、特徵離散化、特徵獲取、特徵服務治理等。

  • 模型挑戰包括基礎模型完備性、級聯模型、複合目標、A/B實驗支援、模型熱載入等。

  • 召回挑戰包括關鍵詞召回、LBS召回、推薦召回、粗排召回等。

三大挑戰內部包含了非常多更細粒度的挑戰,孤立地解決每個挑戰顯然不是好思路。線上排序作為一個被廣泛使用的架構值得采用領域模型進行統一解決。Domain-driven design(DDD)的三個原則分別是:領域聚焦、邊界清晰、持續整合。

基於以上分析,我們構建了三個線上排序領域模型:召回治理、特徵服務治理和線上排序分層模型。

召回治理

經典的Two-Phase Scheme架構如下圖所示,查詢階段應該包含:召回、粗排和精排。但從領域架構設計的角度來講,粗排對於精排而言也是一種召回。和基於傳統的文字搜尋不同,美團點評這樣的O2O公司需要考慮地理位置和距離等因素,所以基於LBS的召回也是一種召回。與搜尋不同,推薦召回往往基於協同過濾來完成的,例如User-Based CF和Item-Based CF。

深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

綜上所述,召回總體而言分成四大類:

  • 關鍵詞召回,我們採用Elasticsearch解決方案。

  • 距離召回,我們採用K-D tree的解決方案。

  • 粗排召回。

  • 推薦類召回。

特徵服務治理

傳統的視角認為特徵服務應該分為使用者特徵(User)、查詢特徵(Query)和文件特徵(Doc),如下圖:

深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

這是比較純粹的業務視角,並不滿足DDD的領域架構設計思路。由於特徵數量巨大,我們沒有辦法為每個特徵設計一套方案,但是我們可以把特徵進行歸類,為幾類特徵分別設計解決方案。每類技術方案需要統一考慮效能、可用性、儲存等因素。從領域視角,特徵服務包含四大類:

  • 列表類特徵。一次請求要求返回實體列表特徵,即多個實體,每個實體多個特徵。這種特徵建議採用記憶體列表服務,一次性返回所有請求實體的所有特徵。避免多次請求,從而導致數量暴增,造成系統雪崩。

  • 實體特徵。一次請求返回單實體的多個特徵。建議採用Redis、Tair等支援多級的Key-Value服務中間鍵提供服務。

  • 上下文特徵。包括召回靜態分、城市、Query特徵等。這些特徵直接放在請求記憶體裡面。

  • 相似性特徵。有些特徵是通過計算個體和列表、列表和列表的相似度而得到的。建議提供單獨的記憶體計算服務,避免這類特徵的計算影響線上排序效能。本質上這是一種計算轉移的設計。

線上排序分層模型

如下圖所示,典型的排序流程包含六個步驟:場景分發(Scene Dispatch)、流量分配(Traffic Distribution)、召回(Recall)、特徵抽取(Feature Retrieval)、預測(Prediction)、排序(Ranking)等等。

深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

按照DDD的設計原則,我們設計瞭如下線上排序分層模型,包括:場景分發(Scene Dispatch)、模型分發(Model Distribution)、排序(Ranking)、特徵管道(Feature Pipeline)、預測管道(Prediction Pipeline)。我們將逐一進行介紹。

深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

場景分發(Scene Dispatch)

場景分發一般是指業務型別的分發。對於美團點評而言包括:分平臺、分列表、分使用場景等。如下圖所示:

深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

模型分發(Model Distribution)

模型分發的目標是把線上流量分配給不同的實驗模型,具體而言要實現三個功能:

  • 為模型迭代提供線上流量,負責線上效果收集、驗證等。

  • A/B測試,確保不同模型之間流量的穩定、獨立和互斥、確保效果歸屬唯一。

  • 確保與其他層的實驗流量的正交性。

流量的定義是模型分發的一個基礎問題。典型的流量包括:訪問、使用者和裝置。

如何讓一個流量穩定地對映到特定模型上面,流量之間是否有級別?這些是模型分發需要重點解決的問題。

流量分桶原理

採用如下步驟將流量分配到具體模型上面去:

  • 把所有流量分成N個桶。

  • 每個具體的流量Hash到某個桶裡面去。

  • 給每個模型一定的配額,也就是每個策略模型佔據對應比例的流量桶。

  • 所有策略模型流量配額總和為100%。

  • 當流量和模型落到同一個桶的時候,該模型擁有該流量。

深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

舉個例子來說,如上圖所示,所有流量分為32個桶,A、B、C三個模型分別擁有37.5%、25%和37.5%的配額。對應的,A、B、C應該佔據12、8和12個桶。

為了確保模型和流量的正交性,模型和流量的Hash Key採用不同的字首。

流量分級

每個團隊的模型分級策略並不相同,這裡只給出一個建議模型流量分級:

  • 基線流量。本流量用於與其他流量進行對比,以確定新模型的效果是否高於基準線,低於基準線的模型要快速下線。另外,主力流量相對基線流量的效果提升也是衡量演算法團隊貢獻的重要指標。

  • 實驗流量。該流量主要用於新實驗模型。該流量大小設計要注意兩點:第一不能太大而傷害線上效果;第二不能太小,流量太小會導致方差太大,不利於做正確的效果判斷。

  • 潛力流量。如果實驗流量在一定週期內效果比較好,可以升級到潛力流量。潛力流量主要是要解決實驗流量方差大帶來的問題。

  • 主力流量。主力流量只有一個,即穩定執行效果最好的流量。如果某個潛力流量長期好於其他潛力流量和主力流量,就可以考慮把這個潛力流量升級為主力流量。

做實驗的過程中,需要避免新實驗流量對老模型流量的衝擊。流量群體對於新模型會有一定的適應期,而適應期相對於穩定期的效果一般會差一點。如果因為新實驗的上線而導致整個流量群體的模型都更改了,從統計學的角度講,模型之間的對比關係沒有變化。但這可能會影響整個大盤的效果,成本很高。

為了解決這個問題,我們的流量分桶模型優先為模型列表前面的模型分配流量,實驗模型儘量放在列表尾端。這樣實驗模型的頻繁上下線不影響主力和潛力流量的使用者群體。當然當發生模型流量升級的時候,很多流量使用者的服務模型都會更改。這種情況並不是問題,因為一方面我們在嘗試讓更多使用者使用更好的模型,另一方面固定讓一部分使用者長期使用實驗流量也是不公平的事情。

排序(Ranking)

排序模組是特徵模組和預測模組的容器,它的主要職責如下:

  • 獲取所有列表實體進行預測所需特徵。

  • 將特徵交給預測模組進行預測。

  • 對所有列表實體按照預測值進行排序。

特徵管道(Feature Pipeline)

特徵管道包含特徵模型(Feature Model)、表示式(Expression)、原子特徵(Atomic Feature)、 特徵服務代理(Feature Proxy)、特徵服務(Feature Service)。如下圖所示:

深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

特徵管道要解決兩個核心問題:

  • 給定特徵名獲取對應特徵值。這個過程很複雜,要完成特徵名->特徵服務->特徵類->特徵值的轉化過程。

  • 特徵運算元問題。模型使用的特徵往往是對多個原子特徵進行復合運算後的結果。另外,特徵離散化、歸一化也是特徵運算元需要解決的問題。

完整的特徵獲取流程如下圖所示,具體的流程如下:

  • Ranking模組從FeatureModel裡面讀取所有的原始特徵名。

  • Ranking將所有的原始特徵名交給Feature Proxy。

  • Feature Proxy根據特徵名的標識去呼叫對應的Feature Service,並將原始特徵值返回給Ranking模組。

  • Ranking模組通過Expression將原始特徵轉化成複合特徵。

  • Ranking模組將所有特徵交給級聯模型做進一步的轉換。


深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

特徵模型(Feature Model)

我們把所有與特徵獲取和特徵運算元的相關資訊都放在一個類裡面,這個類就是FeatureModel,定義如下:

深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

表示式(Expression)

表示式的目的是為了將多種原始特徵轉換成一個新特徵,或者對單個原始特徵進行運算子轉換。我們採用字首法表示式(Polish Notation)來表示特徵運算元運算。例如表示式(5-6)*7的字首表示式為* - 5 6 7。

複合特徵需要指定如下分隔符:

  • 複合特徵字首。區別於其他型別特徵,我們以“$”表示該特徵為複合特徵。

  • 表示式各元素之間的分隔符,採用“_”來標識。

  • 用“O”表示運算子字首。

  • 用“C”表示常數字首。

  • 用“V”表示變數字首。

例如:表示式v1 + 14.2 + (2*(v2+v3)) 將被表示為 $O+_O+_Vv1_C14.2_O*_C2_O+_Vv2_Vv3

原子特徵(Atomic Feature)

原子特徵(或者說原始特徵)包含特徵名和特徵值兩個部分。原子特徵的讀取需要由4種實體類共同完成:

  • POJO用於儲存特徵原始值。例如DealInfo儲存了所有與Deal實體相關的特徵值,包括Price、maxMealPerson、minMealPerson等等。

  • ScoringValue用於儲存從POJO中返回的特徵值。特徵值包含三種基本型別,即數量型(Quantity)、序數型(Ordinal)、類別型(Categorical)。

  • ScoreEnum實現特徵名到特徵值的對映。每類原子特徵對應於一個ScoreEnum類,特徵名通過反射(Reflection)的方式來構建對應的ScoreEnum類。ScoreEnum類和POJO一起共同實現特徵值的讀取。

  • FeatureScoreEnumContainer用於儲存一個演算法模型所需所有特徵的ScoreEnum。

一個典型的例子如下圖所示:

  • DealInfo是POJO類。

  • DealInfoScoreEnum是一個ScoreEnum基類。對應於平均用餐人數特徵、價格等特徵,我們定義了DIAveMealPerson和DIPrice的具體ScoreEnum類。

  • FeatureScoreEnumContainer用於儲存某個模型的所有特徵的ScoreEnum。


深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

複雜系統設計需要充分的利用語言特性和設計模式。建議的優化點有三個:

  • 為每個原子特徵定義一個ScoreEnum類會導致類數量暴增。優化方式是ScoreEnum基類定義為Enum型別,每個具體特徵類為一個列舉值,列舉值繼承並實現列舉類的方法。

  • FeatureScoreEnumContainer採用Build設計模式將一個演算法模型的所需特徵轉換成ScoreEnum集合。

  • ScoreEnum從POJO類中讀取具體特徵值採用的是Command模式。

這裡稍微介紹一下Command設計模式。Command模式的核心思想是需求方只要求拿到相關資訊,不關心誰提供以及怎麼提供。具體的提供方接受需求方的需求,負責將結果交給需求方。

在特徵讀取裡面,需求方是模型,模型僅僅提供一個特徵名(FeatureName),不關心怎麼讀取對應的特徵值。具體的ScoreEnum類是具體的提供方,具體的ScoreEnum從POJO裡面讀取特定的特徵值,並轉換成ScoringValue交給模型。

特徵服務代理(Feature Proxy)

特徵服務代理負責遠端特徵獲取實施,具體的過程包括:

  • 每一大類特徵或者一種特徵服務有一個FeatureProxy,具體的FeatureProxy負責向特徵服務發起請求並獲取POJO類。

  • 所有的FeatureProxy註冊到FeatureServiceContainer類。

  • 在具體的一次特徵獲取中,FeatureServiceContainer根據FeatureName的字首負責將特徵獲取分配到不同的FeatureProxy類裡面。

  • FeatureProxy根據指定的實體ID列表和特徵名從特徵服務中讀取POJO列表。只有對應ID的指定特徵名(keys)的特徵值才會被賦值給POJO。這就最大限度地降低了網路讀取的成本。

預測管道(Prediction Pipeline)

預測管道包含:預測(Prediction)、級聯模型(Cascade Model)、表示式(Expression)、特徵轉換(Transform)、計分(Scoring)和原子模型(Atomic Model)。

預測(Prediction)

預測本質上是對模型的封裝。它負責將每個列表實體的特徵轉化成模型需要的輸入格式,讓模型進行預測。

級聯模型(Cascade Model)

我們構建級聯模型主要是基於兩方面的觀察:

  • 基於Facebook的《Practical Lessons from Predicting Clicks on Ads at Facebook》的XGBoost+LR以及最近很熱門的Wide&Deep表明,對一些特徵,特別是ID類特徵通過樹模型或者NN模型進行轉化,把轉化後的值做為特徵值交給預測模型進行預測,往往會能實現更好的效果。

  • 有些訓練目標是複合目標,每個子目標需要通過不同的模型進行預測。這些子目標之間通過一個簡單的表示式計算出最終的預測結果。

舉例如下圖所示,我們自上而下進行講解:

  • 該模型有UserId、CityId、UserFeature、POI等特徵。

  • UserId和CityId特徵分別通過GBDT和NN模型進行轉換(Transform)。

  • 轉換後的特徵和UserFeature、POI等原始特徵一起交給NN和LR模型進行算分(Scoring)。

  • 最終的預測分值通過表示式Prediction Score = α*NNScore + β*LRScore/(1+γ)來完成。表示式中的 α 、β和γ是事先設定好的值。

深入淺出排序學習:寫給程式設計師的演算法系統開發實踐

原子模型(Atomic Model)

在這裡原子模型指的是一種原子計算拓撲結構,比如線性模型、樹模型和網路模型。

常用的模型像Logistic Regression和Linear Regression都是線性模型。GBDT、Random Forest都是樹模型。MLP、CNN、RNN都是網路模型。

這裡定義的原子模型主要的目的是為了工程實施的便利。一個模型被認定為原子模型有如下兩個原因:

  • 該模型經常做為獨立預測模型被使用。

  • 該模型有比較完整的實現程式碼。

總結

本文總結了作者在美團點評解決到店餐飲個性化資訊展示的相關經驗,從演算法和架構兩方面進行闡述。在演算法部分,文章採用通俗的例子和類比方式進行講解,希望非演算法工程師能夠理解關鍵的演算法概念。架構部分比較詳細地闡述了到店餐飲的排序架構。

根據我們所掌握的知識,特徵治理和召回治理的思路是一種全新的視角,這對於架構排序系統設計有很大的幫助。這種思考方式同樣也適用於其他領域模型的構建。與Google提供的經典Two-Phase Scheme架構相比,線上排序分層模型提供了更細顆粒度的抽象原型。該原型細緻的闡述了包括分流、A/B測試、特徵獲取、特徵運算元、級聯模型等一系列經典排序架構問題。同時該原型模型由於採用了分層和層內功能聚焦的思路,所以它比較完美地體現了DDD的三大設計原則,即領域聚焦、邊界清晰、持續整合。

作者簡介

劉丁,目前負責到店餐飲演算法策略方向,推進AI在到店餐飲各領域的應用。2014年加入美團,先後負責美團推薦系統、智慧篩選系統架構、美團廣告系統的架構和上線、完成美團廣告運營平臺的搭建。曾就職於Amazon、TripAdvisor等公司。

歡迎加入美團機器學習技術交流群,跟作者零距離交流。進群方式:請加美美同學微信(微訊號:MTDPtech02,回覆:機器學習,美美會自動拉你進群。

參考文章:

[1] Gamma E, Helm R, Johnson R, et al.  Design Patterns-Elements of Reusable Object-Oriented Software. Machinery Industry, 2003 [2] Wikipedia,Learning to rank. [3] Wikipedia,Machine learning. [4] Wikipedia,Precision and recall. [5] Wikipedia,Discounted cumulative gain. [6] Wikipedia,Domain-driven design. [7] Wikipedia,Elasticsearch. [8] Wikipedia,k-d tree. [9] 百度百科,太陽曆. [10] 百度百科,陰曆.

[11] Xinran H,  Junfeng P, Ou J, et al. Practical Lessons from Predicting Clicks on Ads at Facebook

[12] Olivier C, Donald M, Ya Z, Pierre G. Expected Reciprocal Rank for Graded Relevance

[13] Heng-Tze C, Levent K, et al.  Wide & Deep Learning for Recommender Systems

相關文章