1. 程式人生 > >餓了麼推薦系統的從0到1

餓了麼推薦系統的從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進行排序。
這裡寫圖片描述
這裡需要說明的是:任何一個模型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配置格式如下表:
這裡寫圖片描述

線性加權模式:rankType=linear

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

當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, 都需要實現以下介面:

可供ModelManager進行Model例項化的BaseModel() 和初始化的init()

可供Scorer/InstanceBuilder獲取特徵項的 getFieldNames()/getFeatures();

可供Scorer呼叫進行打分的predict(Map<K,V>values)predict(List<Map<K,V>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_等, 詳細說明如下表,其從本質上來講等同於特徵操作符(複合特徵)。
這裡寫圖片描述
2. 實時特徵:

實時特徵來源於Kafka與Storm的日誌實時計算,存於Redis,比如:使用者食物搜尋與點選資訊,例項如下表。
這裡寫圖片描述
3. 自定義特徵:

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

  1. 複合特徵(CompositeFeature):

使用者特徵組合的複雜操作, 比如下表所示(部分)
這裡寫圖片描述
三、洗牌邏輯

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(線上配置修改系統), 能夠快速、便捷地更改洗牌邏輯,下面給出了一個配置例項。

[
{“name”: “recfoods”, “topnum”: 15, “foodcnt”: 3},
{“name”: “category”, “topnum”: 15, “shopcnt”: 2},
{“name”: “shopflag”, “topnum”: 20, “span”: 3, “exclude”: “XXX”}},
{“name”: “recfoods”, “topnum”: 15, “foodcnt”: 3},
{“name”: “dinner”,”topnum”:5,”interval”: [“10:30~12:30”,”16:30~18:30”]},
{“name”: “mixture”,”topnum”:12, “include”: “XXX”}
]
四、總結

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