設(shè)計:從POS小票到關(guān)聯(lián)規(guī)則實戰(zhàn))
上周去某家社區(qū)超市結(jié)賬收銀員遞給我一張打印小票底部多了一行字“您常買的某品牌鮮牛奶下周有促銷搭配全麥面包更劃算”。我愣了一下——這家店什么時候開始懂我了后來一問才知道他們用一套上線不到三個月的商品推薦系統(tǒng)把小票、會員卡、促銷記錄和貨架數(shù)據(jù)全部串了起來。市面上聊推薦系統(tǒng)默認(rèn)語境全是電商千人千面、點擊率、實時會話。但超市這套邏輯完全不同顧客在線下推著購物車逛貨架位置本身就是推薦位買單瞬間才是黃金窗口。我結(jié)合自己做的“超市智能商品推薦系統(tǒng)設(shè)計”項目把這幾個月踩過的坑和趟出來的規(guī)律整理成文希望能幫到正在做線下零售數(shù)據(jù)化的朋友。1. 超市推薦和電商推薦的底層邏輯差異先別急著上算法1.1 三個本質(zhì)不同的購買場景電商推薦的整個鏈路是搜索—點擊—加購—支付每一步都有明確的用戶反饋用戶在網(wǎng)站上的行為密度非常高一天能產(chǎn)生幾十上百次交互。超市不一樣顧客進(jìn)店之后的逛、拿、放回這些動作在沒有攝像頭和貨架級傳感器的情況下基本采集不到你手里最干凈、最完整的信號只有一張張POS小票——也就是結(jié)賬那一刻的購物籃。第二個差異是商品生命周期。電商絕大部分商品可以長期掛售算法有充足時間學(xué)習(xí)。超市里生鮮、季節(jié)品、臨期品每天都在變土豆白菜保質(zhì)期就幾天一個上架周期內(nèi)可能根本攢不夠算法學(xué)習(xí)所需的樣本量。如果照搬電商那種“積累用戶行為——更新模型——再推薦”的閉環(huán)等模型學(xué)會的時候貨架早換了一輪。第三個差異藏在推薦位的物理屬性里。線上貨架無限長推薦錯了成本極低線下貨架和動線是有限的顧客要走到你推薦的那個貨架前才算數(shù)。這意味著超市推薦系統(tǒng)輸出的任何結(jié)果都必須考慮到物理可達(dá)性和當(dāng)下的購物階段不是算法算出來就完事的。1.2 從“猜你想要什么”到“幫你把這趟買順”電商推薦的核心是猜用戶當(dāng)前想要什么點擊率是生命線。超市推薦的目標(biāo)則完全不同業(yè)務(wù)上你要回答的問題是這位顧客這趟來大概要買哪些品類購物籃里還缺什么把缺的那部分補(bǔ)上客單價自然就上去了。舉個很直觀的例子顧客明明已經(jīng)選了牛排購物籃里大概率還缺黑胡椒醬、西藍(lán)花、意面甚至一瓶紅酒。這不是猜他想買什么而是幫他完成“這頓飯”的拼圖。經(jīng)典零售故事里的“啤酒與尿布”雖然被營銷敘事夸張過但原理是成立的——買尿布的年輕父親和買啤酒的人群高度重合把啤酒放在尿布附近雙方都受益。所以設(shè)計系統(tǒng)的第一件事不是選算法而是和目標(biāo)業(yè)務(wù)方對齊KPI你到底想提升客單價、消化臨期庫存、還是提高會員復(fù)購這三個目標(biāo)會導(dǎo)向完全不同的推薦策略我在后面會展開說。順序搞反了算法再漂亮也沒人買單。1.3 誰真的需要這套系統(tǒng)以及誰不需要不是所有超市都需要推薦系統(tǒng)。我見過一些便利店老板拿著幾百個SKU的Excel表來找我聊協(xié)同過濾說實話沒必要那個規(guī)模下貨架擺放和補(bǔ)貨策略帶來的收益遠(yuǎn)大于推薦算法。門店類型SKU規(guī)模數(shù)據(jù)基礎(chǔ)推薦方案建議社區(qū)便利店幾百個單店P(guān)OS流水暫不需要做好陳列即可中型社區(qū)超市幾百到幾千會員覆蓋率低關(guān)聯(lián)規(guī)則 規(guī)則引擎大型商超/連鎖上萬會員體系完善多門店召回—排序完整架構(gòu)分店建模超市到家小程序同上有在線瀏覽行為可以參照電商思路但注意生鮮差異先對號入座再決定投入多少資源這是我給所有線下零售從業(yè)者的第一條建議。2. 整體架構(gòu)與數(shù)據(jù)鏈路從POS小票到推薦結(jié)果2.1 數(shù)據(jù)采集POS小票是主干但別忽略輔助信號超市里能采的數(shù)據(jù)其實比想象中多按重要性排序大概是POS訂單明細(xì)、會員檔案、促銷檔期、退貨記錄、庫存與缺貨記錄。POS明細(xì)是絕對的主干它能給出訂單號、商品編碼、數(shù)量、金額、門店、交易時間和會員ID。這中間最關(guān)鍵的一點是訂單粒度——同一張小票里的所有商品天然構(gòu)成一個“購物籃”這是所有后續(xù)分析的地基。輔助信號同樣不能忽略。會員領(lǐng)了哪些優(yōu)惠券、核銷了沒有能反映價格敏感度退貨記錄能告訴你顧客對哪些品類不滿意庫存和缺貨記錄能幫你過濾掉“算法推薦了但庫里沒有”的尷尬。這里有個典型的坑很多超市的POS系統(tǒng)沒有把退貨單和原銷售單關(guān)聯(lián)起來“買了又退”被當(dāng)成了真實購買這會嚴(yán)重污染關(guān)聯(lián)規(guī)則的統(tǒng)計。我在項目初始化階段花了整整一周做數(shù)據(jù)清洗剔除退貨、剔除員工內(nèi)部購買、剔除批發(fā)團(tuán)購單后面模型效果才正常。數(shù)據(jù)質(zhì)量決定算法上限這句話在超市場景體現(xiàn)得淋漓盡致。2.2 特征體系商品、顧客、門店三個維度缺一不可推薦特征分三大塊商品側(cè)、顧客側(cè)、門店側(cè)。商品側(cè)要梳理類目層級、品牌、規(guī)格、價格帶、保質(zhì)期類型、冷鏈要求、是否促銷品、季節(jié)標(biāo)簽。這里特別要注意“保質(zhì)期類型”這個特征——長保商品適合被推薦短保生鮮商品推薦要非??酥迫思也粫驗槟愕耐扑]就多買一堆容易爛的菜。顧客側(cè)依賴會員體系包括RFM最近購買時間、頻次、金額、平均籃單價、品類偏好分布、常購時段、價格帶偏好。沒有會員體系的門店可以把匿名小票按“同一時段、同一收銀臺、連續(xù)單號”做臨時會話聚合效果打折但聊勝于無。門店側(cè)特征常被忽略門店面積、商圈類型、周邊客群結(jié)構(gòu)、歷史銷售增速這些決定了同一套模型在不同門店的表現(xiàn)差異。別指望用市中心大店的數(shù)據(jù)訓(xùn)練一個模型去服務(wù)所有門店后面踩坑部分會專門講這個問題。2.3 離線管道和實時管道的分工我的做法是把系統(tǒng)切成兩條管道各干各的。離線管道每天凌晨跑T1任務(wù)產(chǎn)出每日推薦候選集、關(guān)聯(lián)規(guī)則、商品向量、用戶偏好畫像服務(wù)于小程序首頁、電子價簽、次日促銷推薦這些場景對時效性要求不高。實時管道專門服務(wù)收銀臺結(jié)賬前的那幾秒鐘邏輯非常簡單但要求響應(yīng)在幾十毫秒內(nèi)完成。實時管道的推薦不是跑復(fù)雜模型而是基于“當(dāng)前購物籃”的規(guī)則匹配把購物籃里的商品編碼拿過來去查離線算好的規(guī)則表命中哪些關(guān)聯(lián)規(guī)則按置信度排序取前三個結(jié)果返回。整個鏈路用到的存儲也就一個關(guān)系型數(shù)據(jù)庫加一個緩存。把緩存打開熱點規(guī)則全部提前加載接口P99延遲能壓到50毫秒以內(nèi)——這套組合便宜、可控、維護(hù)成本低是我最推薦的中小型超市起步方案。3. 算法選型把關(guān)聯(lián)規(guī)則、協(xié)同過濾和深度模型放在正確的位置3.1 關(guān)聯(lián)規(guī)則Apriori 與 FP-Growth 的取舍關(guān)聯(lián)規(guī)則是超市場景最容易出效果、也最好解釋的算法。核心概念就三個支持度、置信度、提升度。我拿項目里的真實例子說明。假設(shè)有一千張小票其中兩百張包含牛奶一百五十張包含面包兩者同時出現(xiàn)的有六十張。那么支持度就是60 / 1000 6%代表這個組合在全部訂單中出現(xiàn)的比例置信度牛奶→面包是60 / 200 30%代表買了牛奶的顧客里有多少也買了面包提升度是30%除以面包的整體購買比例15%等于2。提升度大于1就說明“牛奶和面包”之間有正向關(guān)聯(lián)大于1.5基本可以放心上推薦位。閾值怎么定數(shù)據(jù)量大時支持度可以設(shè)低一些比如1%數(shù)據(jù)量小時要設(shè)高一些避免偶然共現(xiàn)。置信度建議至少20%到30%低于這個值推薦出去的效果很差。算法實現(xiàn)上Apriori在幾千個SKU時還能跑SKU上萬后性能下降非常明顯因為它要反復(fù)掃描數(shù)據(jù)集生成候選集FP-Growth用樹結(jié)構(gòu)壓縮事務(wù)不生成候選集內(nèi)存友好我最終選了FP-Growth。但不管用哪個生鮮短保商品都建議直接擋在推薦結(jié)果之外。3.2 協(xié)同過濾沒有評分時如何定義“相似”超市的用戶—商品矩陣?yán)餂]有評分只有“買沒買”。解決思路是把購買次數(shù)當(dāng)作隱式反饋構(gòu)造用戶—商品共現(xiàn)矩陣。這個矩陣極其稀疏一個普通會員一年也就幾十張小票和電商用戶動輒上百次點擊完全不是一個密度。所以我強(qiáng)烈建議超市場景以Item-based協(xié)同過濾為主User-based只能做輔助。Item-based的思路是先算商品與商品之間的相似度——把每個商品在所有小票里的共現(xiàn)向量當(dāng)作它的“畫像”用余弦相似度計算兩兩相似度顧客買了A就推薦與A最相似且不在當(dāng)前購物籃里的TopN。相比User-based它不依賴某個用戶的豐富歷史新用戶只要有一張小票就能推薦實用性高得多。這里有個業(yè)務(wù)細(xì)節(jié)計算相似度時要忍住誘惑把礦泉水、百元充值卡這類“人人都會買”的商品從共現(xiàn)向量里剔除否則它們會和所有商品都相似推薦出來毫無信息量變成了變相的熱銷榜。3.3 淺層模型還是深度模型按數(shù)據(jù)量決定我的判斷標(biāo)準(zhǔn)很簡單先看每月有效小票量級。小于十萬張關(guān)聯(lián)規(guī)則加Item-based協(xié)同過濾足夠別折騰深度學(xué)習(xí)幾十萬到百萬張可以在排序?qū)由弦粋€梯度提升樹模型比如XGBoost和LightGBM這類效果穩(wěn)定且好調(diào)到千萬張以上才需要考慮商品向量化、圖神經(jīng)網(wǎng)絡(luò)這類更復(fù)雜的方案。別迷信深度學(xué)習(xí)。超市的數(shù)據(jù)密度遠(yuǎn)低于互聯(lián)網(wǎng)深度模型在小數(shù)據(jù)上很容易過擬合或者把促銷期的噪音當(dāng)成規(guī)律學(xué)進(jìn)去。一個清醒的判斷是推薦系統(tǒng)的離線指標(biāo)提升和業(yè)務(wù)收益之間隔著一條巨大的鴻溝簡單模型更容易解釋、更容易排查、更容易讓業(yè)務(wù)方信任這就夠了。整體框架我采用的是標(biāo)準(zhǔn)的召回—排序結(jié)構(gòu)。召回階段把三類候選合并關(guān)聯(lián)規(guī)則候選、相似商品候選、門店熱銷補(bǔ)充排序階段用GBDT對候選打分特征拼接顧客、商品、門店、時間四類信息最后過一道規(guī)則過濾把臨期品、缺貨品、禁止推薦品類全部擋掉。這個結(jié)構(gòu)在超市場景完全夠用也方便后續(xù)擴(kuò)展。4. 工程實現(xiàn)從購物籃向量到渠道下發(fā)4.1 把購物籃當(dāng)成“句子”商品向量的訓(xùn)練細(xì)節(jié)排序模型再好也依賴召回質(zhì)量。我后來做了升級把每個會員的購物籃按時間排序當(dāng)成“句子”把商品當(dāng)成“詞”用詞向量那套方法訓(xùn)練商品向量。道理很簡單——經(jīng)常出現(xiàn)在同一個購物籃里的商品和經(jīng)常出現(xiàn)在同一句話里的詞一樣語義上是相近的。這樣算出來的商品相似度比itemCF的共現(xiàn)矩陣平滑得多還能通過向量運(yùn)算做一些有意思的探索。幾個訓(xùn)練細(xì)節(jié)值得記錄。一是購物籃序列構(gòu)造時同一張小票里商品的先后順序其實沒有意義要隨機(jī)打亂否則模型會學(xué)到假的“順序信息”二是窗口大小設(shè)3到5比較合適太大會把無關(guān)商品強(qiáng)行拉近三是負(fù)采樣時必須剔除礦泉水、抽紙這種超高頻率商品否則它們會成為萬能的“負(fù)樣本中心”把向量空間搞壞。構(gòu)建訓(xùn)練樣本時我曾經(jīng)踩過數(shù)據(jù)泄漏的坑把當(dāng)天的退貨數(shù)據(jù)直接拿來當(dāng)負(fù)樣本結(jié)果模型學(xué)會了“識別那些會被退貨的商品”上線之后推薦質(zhì)量奇差。后來把退貨數(shù)據(jù)單獨清洗作為過濾規(guī)則而不是訓(xùn)練樣本才恢復(fù)正常。4.2 排序?qū)犹卣?、樣本與模型排序模型的作用是把召回來的候選商品按“顧客當(dāng)下會買”的概率重新打分。我的特征分三組。商品側(cè)有價格、折扣力度、歷史轉(zhuǎn)化率顧客側(cè)有該顧客對這個類目的歷史購買頻率、價格帶匹配度、距離上次購買該類目的時間。最后一組特別關(guān)鍵超市有周期購買屬性牛奶喝完大概一周米面油大概一個月距離上次購買時間太近就不推薦太遠(yuǎn)也不必追著推。場景側(cè)特征包括當(dāng)前時刻、門店編號、當(dāng)前購物籃已有品類數(shù)量。比如晚上七點購物籃里已經(jīng)躺著一盒雞蛋排序模型會傾向于把配菜類商品排前面。訓(xùn)練樣本定義要小心。正樣本是推薦后被加入購物籃或者直接購買的商品負(fù)樣本是展示了但沒買。這里必須等A/B測試閉環(huán)跑起來才能拿到真實的曝光數(shù)據(jù)所以第一個月往往是冷啟動模型效果不會太好別慌先把曝光日志埋點埋全。4.3 渠道與時機(jī)推薦位不是越多越好推薦結(jié)果要分發(fā)到具體渠道每個渠道的特性完全不同。渠道時機(jī)展示數(shù)量適合內(nèi)容收銀臺屏幕結(jié)賬前幾秒1-3個湊單型、滿減門檻補(bǔ)齊電子價簽顧客在貨架前1個關(guān)聯(lián)提示當(dāng)前商品的搭配品到家小程序下單前/次日提醒3-6個生鮮補(bǔ)貨、促銷預(yù)告打印小票結(jié)賬后1-2個優(yōu)惠券二維碼線下回流時機(jī)規(guī)則比模型重要。不要顧客一進(jìn)門就推冷飲他還沒逛到飲料區(qū)不要顧客剛買完某個品類就推同類目他短時間內(nèi)不會再買生鮮補(bǔ)貨提醒適合在晚上八點后推送第二天的菜。我的經(jīng)驗是一籃子推薦不要超過三個超過三個顧客根本看不過來還會覺得被冒犯。4.4 規(guī)則過濾與人工兜底這一層最不智能但往往決定系統(tǒng)能不能上線。無論模型給什么結(jié)果都必須過一遍硬規(guī)則缺貨商品不推、臨期商品不推、有合規(guī)風(fēng)險的商品煙酒、處方性質(zhì)保健品堅決不推、兒童營養(yǎng)品類目不能推薦給沒有小孩的顧客。價格帶也要兜底——一個習(xí)慣買高端有機(jī)食品的顧客你不該給他推性價比散裝貨反之也一樣。規(guī)則兜底還有一個隱藏好處業(yè)務(wù)方敢把推薦位交給你。零售店長和采購最怕系統(tǒng)亂推影響庫存和毛利有了硬規(guī)則他們才愿意把真實曝光數(shù)據(jù)開放給算法團(tuán)隊形成正向循環(huán)。5. 冷啟動、效果評估與踩坑實錄5.1 三類冷啟動的應(yīng)對方案新商品、新門店、新會員三類冷啟動要分開處理。新商品沒有歷史數(shù)據(jù)我按價格帶和品類把它歸入已有商品簇先給一個保守的推薦位積累三十次展示之后再進(jìn)入正常候選池。新門店沒有本地數(shù)據(jù)先用同商圈、同面積級別的老門店模型做遷移跑兩到四周后再用本店數(shù)據(jù)微調(diào)。新會員沒有歷史記錄先用門店熱銷TopN加時段熱銷頂上同時在會員注冊時加一道“偏好品類勾選”——收集的信息量不大但對冷啟動很有幫助。5.2 評估指標(biāo)怎么定推薦系統(tǒng)上線后我每周看四類指標(biāo)。直接指標(biāo)是推薦位的點擊、掃碼、加入購物籃率生意指標(biāo)是客單價、購物籃商品數(shù)、連帶率庫存指標(biāo)是臨期品清倉率和滯銷品動銷率體驗指標(biāo)是退貨率和投訴率。只看點擊率會騙自己超市推薦的價值必須落到生意指標(biāo)上。A/B測試建議按門店組或會員ID分桶至少跑兩周。兩周是線下購買周期的約數(shù)——很多家庭的超市大采購是兩周一次跑不夠一個完整周期結(jié)論不可信。我不建議按“線上點擊用戶和線下到店用戶”來分實驗組這個口徑混在一起根本解釋不清楚。5.3 我踩過的幾個具體的坑第一個是數(shù)據(jù)泄漏。訓(xùn)練集里混入當(dāng)天退貨單據(jù)模型學(xué)會了推薦“要退的商品”。清洗退貨數(shù)據(jù)之后離線指標(biāo)和在線轉(zhuǎn)化率同步明顯提升這算是我印象最深的一次教訓(xùn)。第二個是促銷干擾。大促期間所有關(guān)聯(lián)規(guī)則都被打折商品主導(dǎo)跑出來一堆“滿減湊單”規(guī)則促銷一結(jié)束全失效。解決辦法是訓(xùn)練時剔除促銷占比過高的樣本或者干脆給促銷單獨建一套規(guī)則庫。第三個是推薦了顧客剛買過的東西。顧客上周剛拿一箱牛奶這周又給他推牛奶體驗極差。后來在排序特征里加入“購買間隔”長保商品設(shè)三十天冷靜期、短保設(shè)七天問題才解決。第四個是忽略門店差異??偟陻?shù)據(jù)訓(xùn)練的模型放在寫字樓店表現(xiàn)很差因為兩個店的顧客結(jié)構(gòu)完全不一樣。最終的方案是至少按大店、社區(qū)店、寫字樓店分三套模型效果才穩(wěn)定下來。最后再說句實在話如果你的超市還在用Excel看銷售排名別急著上推薦系統(tǒng)。先把POS數(shù)據(jù)規(guī)范化、打通會員體系、把品類和價格帶標(biāo)簽補(bǔ)全這些基礎(chǔ)工作決定了后面所有算法的上限。我做這個項目最大的體會是線下零售的推薦不是要炫技而是要在一趟購物已經(jīng)接近尾聲的時候恰到好處地遞上一個讓顧客覺得“這趟沒白來”的理由。先把關(guān)聯(lián)規(guī)則跑通親眼看到客單價在漲再考慮模型升級這個順序不會錯。