首頁  >  文章  >  科技週邊  >  美團綜合業務推薦系統的品質模型與實踐

美團綜合業務推薦系統的品質模型與實踐

王林
王林轉載
2023-04-14 12:43:021351瀏覽

作者:勇皓根根王欣等

1 前言

美團到店綜合業務(以下簡稱到綜)是美團到店業務的重要部門之一,涵蓋洗浴、KTV、美業、醫美、親子、結婚、運動健身、玩樂、教育培訓、家居、寵物、酒吧、生活服務等數十個重點細分行業,滿足數以億計用戶多樣化的本地生活需求。

推薦系統在其中是實現供給和需求高效匹配的重要環節,是傳遞資料價值的出口,而推薦系統的品質決定了匹配效果的折損。如下圖1 所示,數據經過數倉處理、演算法加工,再透過數據服務到各個業務系統,最後透過客戶端埋點又重新流轉回數倉,形成了數據的“飛輪效應”,而質量恰恰是這條鏈路中齒輪嚙合的關鍵點,是提升效率與保障效果的重要前提。

品質保障要圍繞著度量開展,才能「看得見」、「理得清」、「改得準」。但是傳統的後台服務品質指標並不能很好地描述目前「資料飛輪」的品質。我們希望透過綜合業務推薦系統的品質模型建設,為類似多業務線、效果導向的系統品質測量提供一個新的思考角度和實踐參考。

美團綜合業務推薦系統的品質模型與實踐

圖1 推薦系統的「資料飛輪」

2 現況分析

推薦系統是效果類系統,品質特點與功能類系統有所不同。功能類系統一般降級後會較顯性影響使用者體驗,但建議結果回 A 或 A',使用者很難有明顯感知。但實際上,如果配對效果變差,就會直接影響使用者的隱性體驗,就需要被辨識。功能類別系統一般以可用性為核心來建構品質指標體系,在綜合業務推薦系統的業務實踐中,我們發現可用性等指標有以下的限制:

  • 可用性對部分缺陷不敏感:可用性是中斷頻率和持續時間的函數,體現的是系統持續提供服務的能力。只要係統的缺陷不會影響對外提供服務,就不會影響可用性,但有些實際上影響了使用者體驗。這裡的缺陷可能是意料中的(如主動降級),也可能是意料外的(模型更新延遲),都應該納入品質的測量中。
  • 可用性難以覆蓋資料的全連結:推薦系統的連結涵蓋了資料生產、加工、應用、分析等環節。一是可用性並不涉及資料表的質量,二是在可用性能度量的地方無法反應資料品質的全貌。資料品質需要考慮完整性、準確性、時效性、安全性等特徵,超越了可用性的範疇。國際知名學者吳恩達曾說過,人工智慧的價值80% 取決於數據,推薦系統交付推薦效果(點擊轉換率、交易轉換率、用戶停留時間等)的質量,也主要取決於數據的品質。
  • 可用性難以反映業務差異性:美團到綜覆蓋上百個產業、數十個頻道頁,推薦系統出於效率和成本考慮,業務間無法完全進行隔離,可用性的串並聯計算方式難以區分業務進行單獨評估。到綜不同業務差異很大,訪問頻率、流量高峰期、業務策略各不相同,從而品質的特徵和問題分佈也不同。目前可用性的指標缺乏業務維度信息,不利於指導精細化的品質運營。

在品質建構中,過去以故障等級為目標,驗證週期長,具備偶然性,且目標和動作邏輯推導關係不強。另外,故障本身偏事後,這種問題驅動的想法不利於持續運作。總的來說,以可用性為目標,在實際落地計算時存在種種問題,所以我們考慮進行推薦系統的質量模型建設,以可用性為基礎,然後調整計算方式,進而指導精細化的質量運營。

3 建設想法

3.1 業務情境下的品質

#建設品質模型,先回到對品質本質的理解。根據國際標準化組織(ISO)的定義,品質是反映實體滿足明確或隱含「需求」能力的特徵總和。另一個常用的品質概念是穩定性,穩定性的核心是讓系統長時間運作在「預期」狀態。無論是品質還是穩定性,都要搞清楚系統需要滿足誰的需要和預期。在推薦的場景下,這個物件是產品和演算法。業務產品透過理解使用者場景,抽象使用者需求,向推薦團隊提出產品需求,體現為對外的產品迭代;同時推薦系統團隊內部相互協作,學習最佳最佳化模型策略,體現為資料團隊內部的演算法迭代。

如下圖 2 所示,在可用性的計算公式中,強調了長時間,而「需要」 和「預期」 只體現在對外提供服務上。這裡具有一定的合理性,一是可用性作為業界通用的指標,定義必然是泛化的,那麼品質的共性和底線就是對外提供服務;二是大多數後台系統交付功能,對外提供服務大多在「有」和「無」之間,也有一定的空間給到服務降級。但對於以效果為核心目標的建議系統,在功能「有」和「無」之間,存有很長的效果「好」和「壞」的光譜。我們對推薦系統品質的思考迭代,核心改變就是從對外提供服務的“有”“無”,變更到對外提供服務的“好”“壞”,這也是改造可用性計算方式的出發點。

美團綜合業務推薦系統的品質模型與實踐

圖2 對缺陷的認知影響品質測量

3.2 缺陷的考量與選擇

不符合「需要」或「預期」則會產生缺陷,缺陷是質量折損的原因。 ISO/IEC 25010 Software Quality Model (2011) 軟體品質模型定義了軟體缺陷,可視為缺陷的全集,它包含了功能適用性、效能效率、相容性、可用性、可靠性、安全性、可維護性、可移植性8 個特徵及31 個子特徵。這裡面有一些品質特徵後台服務不涉及(使用者介面美學、易學性等),有些在當下認知中不構成C 端品質的突出要素(模組性、共存性、不可抵賴性、可重複使用性等)。結合推薦系統的業務特色和高頻品質問題,現階段我們將重點放在如下圖 3 所示的品質特徵作為缺陷來源。

美團綜合業務推薦系統的品質模型與實踐

圖3 推薦系統的品質特徵

我們發現傳統可用性的量測,大多集中在可靠性、功能完整性、正確性方面,但對於大部分的功能準確性、適當性以及安全性都缺乏度量,這些都與建議的品質和效果緊密相關。準確度、適當性對效果的影響較直觀,其他則較間接。例如安全性,以安全性中的爬蟲訪問為例,爬蟲由於訪問行為不符合真實人類的行為習慣,會影響UVCTR 等核心指標的回收,從而造成效果誤判;同時如果不能識別和剔除爬蟲數據,噪音會進一步影響模型訓練的準確性。數據品質問題是數據“飛輪效應”中的“毒丸”,會產生正回饋不斷放大缺陷。我們將在第四章計算規則中,量化上述的缺陷,拓展可用性的外延。

3.3 度量和計算的選型

可用性可以分為度量方式和計算方式:度量即我們常說的N 個9,計算則用平均故障間隔時間和平均恢復時間的函數來衡量。在度量方式上,業界常用的品質度量方式如下圖4 所示:美團綜合業務推薦系統的品質模型與實踐

美團綜合業務推薦系統的品質模型與實踐

#圖4 度量方式

度量方式選幾分制,不是現階段品質分的重點,可用性本身採用的 N 個 9 也足夠簡單可比較,我們重點考慮計算方式。由於到綜業務線眾多,推薦系統作為平台型產品,系統與業務是 N:N 的關係,當下系統的可用性難以計算每個產業、專案和業務的可用性。一個流量位置,它可以歸屬於休閒玩樂這個業務,可以歸屬於劇本殺這個項目,可以歸屬於核心展示主路徑的一環,也可以歸屬於內容推薦的一種,這種靈活的歸屬性,用請求來聚合計算是最適合的。如下圖 5 所示,如果可用性是請求的函數,它既可以包括上一節中我們關心的品質特徵,也可以在多個維度統計有業務意義的品質情況。

美團綜合業務推薦系統的品質模型與實踐

圖5 從請求的角度度量品質

4 計算方式

根據上一章的建設思路,從故障到缺陷,從推薦結果的“有”、“無”到推薦效果的“好”、“壞”,從整體到各個業務,我們描述了一個好的質量分應該有的特徵。這一章節我們著重在指標的計算邏輯上,選取關鍵缺陷,定義“成功的請求回應”,並增加品質分數的業務聚合維度。

4.1 計算公式

結合3.2 章節中描述的質量特徵,從成功請求佔比的角度評估系統質量,在實際落地計算時可以分成以下四個層面的缺陷:

  • 系統層面:此請求觸發了系統異常,則為缺陷回應。常見的如召回超時、召回失敗、召回空結果等。
  • 資料層面:此請求用到的資料出現異常,則為缺陷回應。常見的如供給數量異常、標籤分佈異常等,資料對使用者請求的實際影響,依賴資料血緣關係的建立和影響面評估。
  • 演算法層面:此請求在召回和排序過程中,使用的特徵、模型、策略異常,則為缺陷回應。常見的如模型更新延遲、特徵缺失等,影響建議的效果表達。
  • 業務層面:此請求觸發了業務適當性或安全性合規要求,則結果包含上述結果的請求均為缺陷回應。常見的如營運回饋有供給品質、內容安全等嚴重的 Bad Case。

一條請求,在生命週期的任意環節經歷了缺陷,則在結果上定義為缺陷回應,具體的缺陷環節是分析下鑽的維度。我們從3.2 章節的品質特徵和上述缺陷的四個層面選取典型問題(業務痛點、高頻品質問題)進行計算,以下圖6 為例:

美團綜合業務推薦系統的品質模型與實踐

圖6 品質分數計算方法

4.2 業務泛化

到綜推薦系統的業務特色是多業務線,行業差異大,推薦物料位置多,這折射到品質度量上,我們需要各個層次的聚合分析,進而指導精細化的運營,如下圖7 所示:

美團綜合業務推薦系統的品質模型與實踐

圖7 各業務層級的聚合分析

到綜有很多中低頻業務,此時比值的波動受請求絕對值影響較大。針對這些場景,可以聚合部分小流量位,只在產業或專案層級進行分鐘級的監控。

4.3 指標系統

如下圖8 所示,我們將推薦系統回應的一條請求作為一次產品交付行為看待,這些請求中無缺陷的比例,就是推薦系統的品質分,是頂層的品質輸出指標。可根據請求的生命週期,建立一級輸入指標,衡量核心流程的品質現狀,如召回缺陷率、排序缺陷率等。還可以再將一級指標進一步拆解,得到二級輸入指標,例如召回缺陷率比較高時,可以再去衡量召回空值率、召回超時率等。使用者的請求也可以依照業務進行垂直、橫向、時間維度聚合,得到有業務屬性的品質分,這樣會更有針對性,更聚焦。

美團綜合業務推薦系統的品質模型與實踐

圖8 品質指標系統

這套改進的品質分,以請求為基本單位,相較於最初的可用性計算方式,在一定範圍內解決了它的局限性:對缺陷敏感,可以包括數據鏈路帶來的影響,方便進行多業務維度的聚合分析。

4.4 血緣拓展

品質分數以請求的粒度統計,在資料應用服務中,請求只是資料對外輸出的形式之一。在完成基礎的品質分數後,請求的生命週期應該延展到資料全鏈路,這樣品質的量測才完整。這時就依賴資料的血緣關係,將資料表- 業務系統- C 端流量關聯起來,建構全景的品質畫像,如下圖9 所示:

美團綜合業務推薦系統的品質模型與實踐

圖9 推薦系統的資料血緣

血緣關係是人類社會由婚姻和生育產生的人際關係,如父母和子女的關係、兄弟和姊妹的關係,以及由此衍生出的其他親屬關係,數據也可以透過融合、轉換產生數據的血緣關係。資料的血緣關係分資料庫、資料表、欄位不同級別,一般用於資料資產(引用熱度計算、理解資料上下文)、資料開發(影響分析、歸因分析 )、資料治理(連結狀態追蹤、數倉治理)、資料安全(安全合規檢查、標籤傳播)四個面向。在目前推薦系統品質分的思路下,主要用影響分析去拓展質量分,將所有途徑故障節點的請求都打上標記,扣除對應分數。

在推薦系統的業務語意下,我們定義了六種業務元資料:快照、方案、元件、索引、模型、特徵,基於元資料我們建構血緣,可以分為任務接入、血緣解析、資料導出。任務接取分為採集模組和入庫模組,當任務接取完成後,將透過圖資料庫儲存節點及節點的關係,利用圖演算法建立血緣。建立血緣之後,節點本身的異常支持系統發現和人工標記,影響分析則可以自動完成。當節點出現異常則進行訊息通知,異常訊息會沿著血緣傳播,進而影響下游環節的質量分計算。

當異常波及到用戶端,我們嘗試用業務語言重新描述損失。根據到綜收入模型,可以計算出各個業務線每個意向UV 的價值(用戶訪問商戶詳情頁、團單詳情頁稱之為意向訪問),再利用該流量位周同比的訪問情況,自動推導業務損失。

5 指標運作

5.1 系統實作

品質分的系統實作方式依賴埋設和診斷。建議全鏈路包含參數輸入、召回前置處理、召回、召回後置處理、粗排、精排、重排等多個環節,每個環節都有可能出故障,因此資料收集需要覆蓋運行時異常、各環節的關鍵輸入輸出資訊等。如下圖10 所示,我們透過Kafka 非同步收集埋點數據,然後分場景進行數據處理:生產環境下,近實時ES 構建索引,提供近4 天快速查詢服務,4 天前的日誌入Hive 歸檔,另外透過Flink 引擎解析埋點數據,經過必要的診斷後,即時計算分數並推送警報訊息;測試環境下,日誌即時分類至MySQL,方便測試排查。最後,結構化展示推薦不同階段的品質狀況,提高了結果的可讀性。

美團綜合業務推薦系統的品質模型與實踐

圖10 品質分的系統實作

分數系統的完善需要逐步推進,對於推薦系統,沒有推薦結果是最嚴重的品質問題。我們首先收集和計算的是推薦空結果,對應一級指標裡的結果缺陷率、召回缺陷率和二級指標裡的結果空值率、召回空值率等。同時由於到綜的業務特性,行業眾多、供給時空分佈不均,大量可交叉的篩選條件也會有空結果,影響品質分的計算。

如何剔除符合業務預期的空結果,消除品質分噪聲,在實現埋點的基礎上,診斷就變得非常重要。以空結果為例,我們主要從參數診斷、資料診斷、連結診斷三個環節去辨識。其中數據診斷指的是當線上篩選條件出現空結果時,回源二次校驗底層數據,查詢底表數據是否為空。如果底表確實沒有相關供給,則沉澱免告警規則,設定免告警有效期,在一段時間內,當前城市當前行業確實缺少相關供給,該空結果不納入質量分計算。

如果底表存在供給,則說明是資料加工或服務過程中出現了異常,導致無法召回,則再經過連結診斷確定出錯環節,納入對應品質分數計算。如何建立規則配對機制(即規則引擎)是診斷引擎的關鍵。當下的規則引擎選擇非常多,例如 EasyRule、Drools、Zools、Aviator 等。根據上文分析,診斷引擎需要能夠對請求參數、建議連結以及底層資料進行規則診斷。對於請求參數、推薦鏈路的診斷均可透過內存參數進行診斷,而數據診斷則需要從第三方存儲中獲得信息,因此必然有一部分需要定制開發。考慮人員工具使用成熟度以及便利性來說,Aviator 表示式引擎較為合適。為契合需要診斷的內容,設計的表達式診斷原語如下:

<span style="color: rgb(106, 115, 125); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">//参数诊断-原语表达</span><br><span style="color: rgb(106, 115, 125); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">//是否符合一定参数的诊断原语</span><br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">check</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">aviator</span>[<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">cityId</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">!=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">nil</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">&&</span> <span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">include</span>(<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">string</span>.<span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">split</span>(<span style="color: rgb(102, 153, 0); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">'1,2,3,4,5,6,7,8,9,10,16,17'</span>,<span style="color: rgb(102, 153, 0); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">','</span>),<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">str</span>(<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">cityId</span>))]<br><br><span style="color: rgb(106, 115, 125); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">//链路诊断-原语表达</span><br><span style="color: rgb(106, 115, 125); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">//1、召回异常诊断原语</span><br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recallException</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">param</span>[<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">$</span>{<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recall</span><span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">#exception</span>#}],<br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">check</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">aviator</span>[<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recallException</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">!=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">nil</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">&&</span> <span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recallException</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">!=</span><span style="color: rgb(102, 153, 0); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">''</span> ]<br><span style="color: rgb(106, 115, 125); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">//2、召回空无异常的诊断原语</span><br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recallEmpty</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">param</span>[<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">$</span>{<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recall</span><span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">#after</span>#}],<br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">check</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">aviator</span>[<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recallEmpty</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">!=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">nil</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">&&</span> <span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recallEmpty</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">!=</span><span style="color: rgb(102, 153, 0); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">''</span> ]<br><span style="color: rgb(106, 115, 125); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">//3、召回不为空,过滤规则执行后为空的诊断原语</span><br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recallEmptyCode</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">param</span>[<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">$</span>{<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recall</span><span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">#after</span>#}],<br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">predictFiltersEmptyCode</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">param</span>[<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">$</span>{<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">predict</span><span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">#after</span><span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">#filters</span>#}],<br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">check</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">aviator</span>[(<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recallEmptyCode</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">==</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">nil</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">||</span> <span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">recallEmptyCode</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">==</span><span style="color: rgb(102, 153, 0); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">''</span>)<span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">&&</span> <span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">predictFiltersEmptyCode</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">!=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">nil</span>]<br><span style="color: rgb(106, 115, 125); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">//4、执行某一具体过滤规则后,导致无结果的匹配</span><br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">filterEmptyCode</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">param</span>[<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">$</span>{<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">PredictStage</span><span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">#filter</span><span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">#after</span><span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">#_compSkRef</span>#}],<br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">check</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">aviator</span>[<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">filterEmptyCode</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">!=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">nil</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">&&</span> <span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">filterEmptyCode</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">==</span><span style="color: rgb(102, 153, 0); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">'deleteItemByConditionalFilter'</span> ]<br><br><span style="color: rgb(106, 115, 125); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">//数据诊断-原语表达(判断底层是否有数据,若没有则为true,否则为false)</span><br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">keys</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">keySpread</span>[<span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">@</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">prefix</span> <span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">138_</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">ymtags_</span>][<span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">@</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">crossOrder</span> <span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">city_$</span>{<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">cityId</span>}<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">_platform_$</span>{<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">platformNo</span>}<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">_surgery_prj_$</span>{<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">genericLvlIds</span>}],<br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">cnt</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">cellar</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">@</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">cellar</span>[<span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">@</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">count</span> <span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">$</span>{<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">keys</span>}],<br><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">global</span>:<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">check</span><span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">aviator</span>[<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">cnt</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">!=</span><span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">nil</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">&&</span> <span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">cnt</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">!=</span><span style="color: rgb(102, 153, 0); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">''</span> <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">&&</span> <span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">long</span>(<span style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">cnt</span>) <span style="color: rgb(215, 58, 73); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);"> <span style="color: rgb(0, 92, 197); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">0</span> ]</span>

5.2 警告跟進

品質分數可以用於即時監控和運作複盤,需要團隊成員及時跟進異動。一般公司通用的告警系統,都是基於服務名稱粒度配置告警接收人。推薦系​​統這類平台型的服務,透過統一的介面提供服務,但是模型策略卻是由不同的同學維護,業務間存在一定的產業知識和理解門檻。預設廣播式的告警,容易引起警告風暴,每個人無法專注於自己模組的問題,有時也會遺漏警告。出於跟進率的考慮(如下圖11 所示),我們基於現有告警二次開發了跟進功能,將特定流量位的告警路由到專屬負責人,並記錄跟進狀態流轉,便於及時週知及事後復盤。在營運方面,我們透過數據報表建立品質分看板,定期回顧不同業務的品質波動。

美團綜合業務推薦系統的品質模型與實踐

圖11 警告跟進流程

5.3 治理效果

品質分的落地以結果空值率為抓手,依流程拆解採集召回空值率、模型預測空值率、重排算子空值率,並依業務聚合成平台、業務、形態、專案、流量位多個維度。治理動作和成果分為以下幾個面向:

#
  • 透過埋藏點和診斷,判斷目前的空結果是供給問題還是品質問題,排除98% 的空結果不納入質量分計算,避免誤告警,日均空結果告警數從40 個降低到5 個。
  • 基於分析連結過程中各環節的空值率,採取治理措施,包括資料規格(資料分層標準化、標籤打標規範)、服務架構(業務隔離、底層資料雙媒體、降級)、變更規格(配置上線管線檢查、流量回放),將空結果系統發現率維持在60% 以上。
  • 客製化開發警告路由,避免警告廣播,支援標記跟進狀態,空結果警告跟進率由無法統計,到核心流量位 100% 跟進。

經過空結果的治理與識別,目前核心流量位空值率為0.01%,即保證核心流量位99.99% 的請求有結果,在建設品質分的同時,確保系統發現率和告警跟進率。

5.4 資產沉澱

推薦系統傳遞的是資料的價值,只有資料被資產化,這種價值才是可持續可增值的。建構推薦系統質量模型的過程,其實也在做數據資產化沉澱。資料在採集後變成資產,一般要滿足以下四個條件:可流動、可計量、可管控、可增值,這些在第四章計算方式中都有所涉及。指標運作的過程,同時也是沉澱品質知識資產的過程。軟體缺陷模型究竟如何影響最終的產品交付質量,他們之間是否有相關性、因果性,這種影響是明確地參與分數計算,還是間接影響的。在品質分營運過程中,我們可以逐漸填補腦中的品質地圖,形成指標間、缺陷間、指標和缺陷間的拓樸關係,這是一個將品質資產化的過程。例如透過推薦系統的業務實踐,我們發現 80% 的線上故障是由於發布引起的,發布故障中的 80% 又是由於資料發布引起的,這可以指導我們透過治理資料發布減少線上故障。

6 未來規劃

我們以可用性為基礎,調整計算方式,建立了多層次的推薦系統品質分,並拓展到各種推薦物料、各個業務模組,核心是我們完成了從對外提供服務的「有無」到對外提供服務的「好壞」的認知迭代,這也是品質精細化營運的基礎。後續的規劃,一方面是繼續充實品質模型的計算與連結覆蓋;另一方面,我們會基於品質模型做更多的品質治理工作,後續將重點思考與迭代的一些方向包括:

  • 透過完善埋設與診斷,逐步落地品質分體系中的各層指標,豐富品質分的內涵,容納更多的品質問題。
  • 透過建立多層次的建議柔性降級,迭代對於品質分的理解,量化不同降級對於系統的影響。
  • 優化資料血緣的準確性、覆蓋率和時效性,更正確快速評估某一個環節品質問題的影響面。

7 本文作者

勇皓、根根、王欣、賀賀、俐聰等,皆來自美團到店平台技術部/到綜業務數據團隊。

以上是美團綜合業務推薦系統的品質模型與實踐的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:51cto.com。如有侵權,請聯絡admin@php.cn刪除