餓了麼推薦系統:從0到1(天天外賣,你可不知道的程序裡面原理)

隨著移動互聯網的發展,用戶使用習慣日趨碎片化,如何讓用戶在有限的訪問時間裡找到想要的產品,成為了搜索/推薦系統演進的重要職責。作為外賣領域的獨角獸, 餓了麼擁有百萬級的日活躍用戶,如何利用數據挖掘/機器學習的方法挖掘潛在用戶、增加用戶粘性,已成為迫切需要解決的問題。

個性化推薦系統通過研究用戶的興趣偏好,進行個性化計算,發現用戶的興趣點,從而引導用戶發現自己的信息需求。一個好的推薦系統不僅能為用戶提供個性化的服務,還能和用戶之間建立密切關係,讓用戶對推薦產生依賴。

本次分享介紹餓 了麼如何從0到1構建一個可快速迭代的推薦系統,從產品形態出發,包括推薦模型與特徵工程、日誌處理與效果評估,以及更深層次的場景選擇和意圖識別

在攜程個性化推薦與人工智能meetup上,已經就以上幾部分做了整體上的說明,本文將就其中模型排序與特徵計算的線上實現做具體說明,同時補充有關業務規則相關的洗牌邏輯說明,力圖從細節上還原和展示餓了麼美食推薦系統。

一、模型排序

1.設計流程

對於任何一個外部請求, 系統都會構建一個QueryInfo(查詢請求), 同時從各種數據源提取UserInfo(用戶信息)、ShopInfo(商戶信息)、FoodInfo(食物信息)以及ABTest配置信息等, 然後調用Ranker排序。以下是排序的基本流程(如下圖所示):

  • 調取RankerManager, 初始化排序器Ranker:
  • 根據ABTest配置信息, 構建排序器Ranker;
  • 調取ScorerManger, 指定所需打分器Scorer(可以多個); 同時, Scorer會從ModelManager獲取對應Model, 並校驗;
  • 調取FeatureManager, 指定及校驗Scorer所需特徵Features。
  • 調取InstanceBuilder, 彙總所有打分器Scorer的特徵, 計算對應排序項EntityInfo(餐廳/食物)排序所需特徵Features;
  • 對EntityInfo進行打分, 並按需對Records進行排序。
餓了麼推薦系統:從0到1(天天外賣,你可不知道的程序裡面原理)

這裡需要說明的是:任何一個模型Model都必須以打分器Scorer形式展示或者被調用。主要是基於以下幾點考慮:

  • 模型迭代:比如同一個Model,根據時間、地點、數據抽樣等衍生出多個版本Version;
  • 模型參數:比如組合模式(見下一小節)時的權重與輪次設定,模型是否支持並行化等;
  • 特徵參數:特徵Feature計算參數,比如距離在不同城市具有不同的分段參數。

2.排序邏輯

對於機器學習或者學習排序而言, 多種模型的組合(Bagging, Voting或Boosting等)往往能夠帶來穩定、有效的預測結果。所以, 針對當前美食推薦項目, 框架結合ABTest系統, 支持single、linear及multi三種組合模式, 具體說明如下:

  • single:單一模式, 僅用一個Scorer進行排序打分;
  • linear:線性加權模式, 指定一系列Scorer以及對應的權重, 加權求和;
  • multi:多輪排序模式, 每輪指定Scorer, 僅對前一輪的top N進行排序。

具體說明如下:

單一模式:rankType=single

對於單一模式, 僅有一個Scorer, 且不存在混合情況, 所以只要簡單對Scorer的打分進行排序即可, 故在此不做詳細展開。ABTest配置格式如下表:

餓了麼推薦系統:從0到1(天天外賣,你可不知道的程序裡面原理)

線性加權模式:rankType=linear

對於線性加權模式, 在單一模式配置的基礎上,需要在ABTest配置每個Scorer的權重, 格式如下表所示:

餓了麼推薦系統:從0到1(天天外賣,你可不知道的程序裡面原理)

當LinearRanker初始化時, 會校驗和初始化所有打分器Scorer。之後, 按照以下步驟對餐廳/食物列表進行排序, 詳見下圖(左):

  • 特徵計算器InstanceBuilder調用ScorerList, 獲取所有所需特徵Feature並去重;
  • InstanceBuilder對所有餐廳/食物進行特徵計算, 詳見特徵計算;
  • ScorerList中所有Scorer對所有餐廳/食物依次進行打分;
  • 對所有Scorer打分進行加權求和, 之後排序。

多輪排序模式:rankType=multi

對於多輪排序模式, 每輪設定一個Scorer, 對前一輪top=Num個餐廳/食物進行排序, 故在ABTest中需要設定每個Scorer的輪次(round)和排序數(num), 格式如下表。

MultiRanker初始化與特徵計算與LinearRanker類似, 具體步驟詳見上圖(右):

  • 特徵計算器InstanceBuilder調用ScorerList, 獲取所有所需特徵Feature並去重;
  • InstanceBuilder對所有餐廳/食物進行特徵計算, 詳見特徵計算;
  • Scorer按輪次(round)對top=Num餐廳/食物進行打分;
  • 對top=Num餐廳/食物按當前Scorer的打分進行排序。

重複步驟3、4, 直到走完所有輪次。

在初始化階段, Ranker根據ABTest配置信息指定算法版本(algoVersion)、排序類型(rankType)、排序層級(rankLevel)及相關打分器(ScorerList)。

3.模型定義

對於線上任何Model,ModelManager 都會通過以下流程獲取相應實例和功能(如下圖所示):

  • 模型實例化時的構造函數BaseModel()和校驗函數validate();
  • 通過FeatureManager獲取對應Model的特徵Feature:abstract getFieldNames()/getFeatures();
  • 傳入Model的特徵, 獲取預測分數:abstract predict(Map

對於Model的迭代和更新、以及之後的Online Learning等, 通過ModelManager對接相應服務來實現。

如上圖所示, 對於任何一個可被Scorer直接調用Model, 都需要實現以下接口:

  1. 可供ModelManager進行Model實例化的BaseModel() 和初始化的init();
  2. 可供Scorer/InstanceBuilder獲取特徵項的 getFieldNames()/getFeatures();
  3. 可供Scorer調用進行打分的 predict(Map values) 和 predict(List values)。

二、特徵計算

1.設計流程

不同於離線模型訓練,線上特徵計算要求低延遲、高複用、強擴展,具體如下:

  • 低延遲:針對不同請求Query,能夠快速計算當前特徵值,包括從各種DB、Redis、ES等數據源實時地提取相關數據進行計算;
  • 高複用:對於同類或者相同操作的特徵,應該具有高複用性,避免重複開發,比如特徵交叉操作、從USER/SHOP提取基本字段等;
  • 強擴展:能夠快速、簡單地實現特徵,低耦合,減少開發成本。

根據以上系統設計要求, 下圖給出了特徵計算的設計流程和特徵基類說明。

具體說明如下:

  • FeatureManager:特徵管理器, 用於特徵管理, 主要功能如下:

a.特徵管理:包括自定義特徵、基礎特徵、實時特徵、複合特徵等;


b.特徵導入:自定義特徵靜態代碼註冊,其他特徵數據庫導入;
c.特徵構建:CompsiteFeature類型特徵構建。

  • InstanceBuilder:特徵構建器, 用於計算餐廳/食物特徵, 具體步驟如下:

a.從每個Scorer獲取Feature列表, 去重, 依賴計算, 最後初始化;
b.層級、並行計算每個EntityInfo的特徵值(之後會考慮接入ETL, 用於Online Learning)。

2.特徵定義

上圖給出了特徵基類說明, 以下是具體的字段和方法說明:

  • type: 特徵類型, 現有query、shop、food, 表示Feature的特徵維度(粒度);
  • operate: CompositeFeature專屬, 特徵操作, 指定當前特徵行為, 比如ADD、MAPGET等;
  • name: 特徵名稱;
  • weight: 權重, 簡單線性模型參數;
  • retType: 特徵返回字段類型;
  • defValue: 特徵默認返回值;
  • level: CompositeFeature專屬, 當前特徵層次, 用於特徵層次計算;
  • operands: CompositeFeature專屬, 特徵操作數, 前置特徵直接依賴;
  • dependencies: CompositeFeature專屬, 特徵依賴;
  • *init(): 特徵初始化函數;
  • *initOther(QueryInfo): InstanceBuilder調用時實時初始化, 即傳入當前特徵參數;
  • *evaluate(QueryInfo, EntityInfo, StringBuilder): 用於餐廳/食物維度的特徵計算。

根據上兩小節設計流程和基類定義的說明, 我們能夠非常快速、簡便地實現一個自定義特徵, 具體流程如下(score為例, 對應類名XXXFeature):

特徵類實現:

  • 建立XXXFeature, 並繼承BaseFeature/CompositeFeature;
  • 實現init(), 設置type\\name(defValue\\weight可選)等;
  • 實現initOther(), 設置特徵參數, 包括infoMap;
  • 實現evaluate(), 具體包括特徵計算的詳細邏輯, 對於返回數值的特徵。

特徵註冊:

  • 在FeatureManager中註冊, 或者在後臺特徵管理系統中註冊;
  • 考慮到代碼中不允許出現明文常量, 故需在FeatureConsts中添加常量定義。

3.特徵分類

(1) 基礎特徵:

基礎特徵為線上可以通過配置特徵名直接從SHOP/USER獲取特徵值的特徵, 比如:shop_meta_、user_meta_、food_meta_等, 詳細說明如下表,其從本質上來講等同於特徵操作符(複合特徵)。

餓了麼推薦系統:從0到1(天天外賣,你可不知道的程序裡面原理)

(2)實時特徵:

實時特徵來源於Kafka與Storm的日誌實時計算,存於Redis,比如:用戶食物搜索與點擊信息,實例如下表。

餓了麼推薦系統:從0到1(天天外賣,你可不知道的程序裡面原理)

(3) 自定義特徵:

線上除CompositeFeature特徵外, 所有XXXFeature均為自定義特徵, 在此不再累述。

(4)複合特徵(CompositeFeature):

用戶特徵組合的複雜操作, 比如下表所示(部分)

餓了麼推薦系統:從0到1(天天外賣,你可不知道的程序裡面原理)

三、洗牌邏輯

1.洗牌類型

很多時候, 基於算法模型的結果能夠給出數據層面的最佳結果, 但是不能保證推薦結果符合人的認知, 比如基於CTR預估的邏輯, 在結果推薦上會傾向於用戶已點過或已購買過的商戶/食物, 這樣就使得推薦缺少足夠的興趣面。所以, 為了保證推薦結果與用戶的相關性, 我們會保留算法模型的結果; 同時, 為了保證結果符合認知, 我們會人為地添加規則來對結果進行洗牌; 最後, 為了擴展用戶興趣點、引導用戶選擇, 將會人工地引入非相關商戶/食物, 該部分將是我們後續優化點之一。下面將詳細介紹“猜你喜歡”模塊線上生效的部分洗牌邏輯,其他洗牌規則類似。

餐廳類目洗牌:

考慮到餐廳排序時, 為避免同類目餐廳扎堆問題, 我們設定了餐廳類目洗牌, 基本規則如下:

針對 top = SHOP_CATE_TOPNUM 餐廳, 不允許同類目餐廳連續超過 MAX_SHOP_SHOPCNT。

餐廳推薦食物數洗牌:

在餐廳列表排序時, 總是希望排在前面的商戶具有更好的展示效果、更高的質量。針對 1*餐廳+3*食物 模式, 如果前排餐廳食物缺失(少於3個)時, 頁面的整體效果就會大打折扣, 所以我們制定了食物數洗牌, 具體規則如下:

所有1個食物的餐廳沉底;
針對top=SHOP_FOODCNT_TOPNUM餐廳, 食物數 < SHOP_FOODCNT_FOODCNT(3) 的餐廳降權

餐廳名稱洗牌:

正常時候, 推薦需要擴展和引導用戶的興趣點, 避免同類扎堆, 比如蓋澆飯類目餐廳等。同樣的, 我們也不希望相同或相似名稱的餐廳扎堆, 比如連鎖店、振鼎雞等。針對此問題, 考慮到餐廳名稱的不規則性, 我們通過分詞和統計, 把所有餐廳名稱做了結構化歸類(distinct_flag), 比如所有“XXX黃燜雞”都歸為“黃燜雞”、“星巴克 XX店”歸為“星巴克”等。之後類似於餐廳類目洗牌, 做重排, 具體規則如下:

對top=SHOP_FLAG_TOPNUM 餐廳進行標籤(flag)洗牌, 使得同一標籤的餐廳排序位置差不得小於 SHOP_FLAG_SPAN

2.線上邏輯

從上一節中可知, 各個洗牌之間存在相互制約, 即洗牌不能並行、只能串行, 誰前誰後就會導致不同的排序結果, 所以, 這裡需要考慮各個洗牌對排序的影響度和優先級:

  • 影響度:即對原列表的重排力度, 比如對於連鎖店少的區域, 名稱洗牌的影響度就會小, 反之, 比如公司周邊有25家振鼎雞, 影響度就會變大;
  • 優先級:即洗牌的重要性, 比如前排餐廳如果食物少於規定數量, 其實質是浪費了頁面曝光機會, 所以食物數洗牌很有必要。

考慮到洗牌的串行邏輯, 越靠後的洗牌具有更高優先級。為了能夠靈活變更線上的洗牌規則, 系統結合Huskar System(線上配置修改系統), 能夠快速、便捷地更改洗牌邏輯,下面給出了一個配置實例。

四、總結

對於一個處於業務快速增長期的互聯網企業,如何能夠在最短時間內構建一個可快速迭代的推薦系統,是擺在眼前的現實問題。此次分享從餓了麼自身業務出發,結合推薦系統的常見問題和解決方案,給出了從產品形態出發, 包括推薦模型與特徵工程、日誌處理與效果評估, 以及更深層次的場景選擇和意圖識別等在內多方面的線上實踐,力圖從整體及細節上還原和展示推薦系統的本質,以期能夠為大家今後的工作提供幫助。


分享到:


相關文章: