計(jì)與實(shí)踐)
1. 項(xiàng)目概述與核心需求拆解1.1 這個(gè)項(xiàng)目到底在解決什么問題接到Python基于智能推薦算法的全屋定制平臺這個(gè)項(xiàng)目需求時(shí)我第一反應(yīng)是這不就是又一個(gè)電商網(wǎng)站套了個(gè)推薦模塊嗎但真正梳理完需求才發(fā)現(xiàn)全屋定制和普通電商的推薦邏輯完全是兩碼事。全屋定制行業(yè)有個(gè)很特殊的地方——它是一個(gè)重度決策、低頻消費(fèi)、高客單價(jià)的領(lǐng)域。用戶在電商平臺買件衣服從瀏覽到下單可能只要幾分鐘但定制一套櫥柜或全屋家具用戶往往要反復(fù)對比幾十個(gè)方案考慮材質(zhì)、空間尺寸、風(fēng)格搭配、預(yù)算上限甚至還要和設(shè)計(jì)師來回溝通好幾輪。這種場景下如果平臺只是簡單給用戶推你可能喜歡的產(chǎn)品基本沒什么用。用戶真正需要的是我的戶型適合什么風(fēng)格我這個(gè)預(yù)算能做哪些組合別人家的同戶型方案是怎么設(shè)計(jì)的。所以我給這個(gè)項(xiàng)目定下的核心方向是做一套以房型數(shù)據(jù)和用戶偏好為核心輸入、以整體空間方案而非單品為推薦單元的智能推薦系統(tǒng)。平臺的數(shù)據(jù)模型不只是用戶—商品的二元關(guān)系而是用戶—房型—空間—方案—單品的多層嵌套關(guān)系。1.2 項(xiàng)目代號與范圍說明項(xiàng)目代號用了07y1pcxm作為內(nèi)部標(biāo)識這個(gè)代號在整個(gè)項(xiàng)目文檔、數(shù)據(jù)庫表前綴和接口路由里都保留著方便團(tuán)隊(duì)成員引用。項(xiàng)目本身覆蓋了幾個(gè)大模塊前臺用戶端房型錄入、方案瀏覽、智能推薦、在線預(yù)約設(shè)計(jì)師后臺管理端商品管理、方案管理、用戶行為日志查看推薦引擎服務(wù)獨(dú)立部署的推薦服務(wù)負(fù)責(zé)從用戶行為、房型數(shù)據(jù)、風(fēng)格標(biāo)簽中計(jì)算推薦結(jié)果后面的正文我會圍繞推薦系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)展開把完整的搭建思路和踩過的坑都寫清楚。如果你手頭也在做類似的平臺類項(xiàng)目這套設(shè)計(jì)思路可以直接遷移過去。2. 推薦算法選型與核心設(shè)計(jì)思路2.1 為什么不能用傳統(tǒng)電商的協(xié)同過濾最早我們用的是業(yè)界最常見的 ItemCF物品協(xié)同過濾——先算物品相似度再根據(jù)用戶歷史行為推薦相似物品。跑了一輪離線評測結(jié)果很慘精確率不到 12%推薦出來的東西用戶點(diǎn)擊率很低。原因也簡單。全屋定制場景有三高一低的特征特征對推薦算法的影響高客單價(jià)用戶決策周期長行為數(shù)據(jù)稀疏高定制化每個(gè)用戶的需求差異大很難用別人買了什么推斷高關(guān)聯(lián)性用戶要的是整體空間方案不是孤立單品低購買頻次一個(gè)用戶一生可能就定制一兩次歷史行為幾乎為零協(xié)同過濾的本質(zhì)是物以類聚人以群分它依賴大量歷史行為數(shù)據(jù)。但全屋定制用戶的行為太稀疏了尤其是新用戶來到平臺往往連一條瀏覽記錄都沒有。這時(shí)候協(xié)同過濾就成了無源之水。2.2 基于內(nèi)容與規(guī)則混合的推薦策略最終我采用了一套**基于內(nèi)容的推薦Content-Based 規(guī)則引擎 協(xié)同過濾補(bǔ)位**的混合方案第一層基于內(nèi)容的匹配。用戶錄入自己的房型比如三室一廳、建筑面積 120 平米和風(fēng)格偏好比如現(xiàn)代簡約系統(tǒng)從方案庫中提取特征向量計(jì)算相似度優(yōu)先推薦與用戶畫像匹配的方案。第二層規(guī)則約束。全屋定制有硬性規(guī)則限制。比如一個(gè) 8 平米的廚房臺面長度不能超過 4.5 米預(yù)算 3 萬以內(nèi)就不能推薦用進(jìn)口巖板的方案。這些規(guī)則必須在推薦之前過濾掉否則推薦結(jié)果再精準(zhǔn)用戶也沒法下單。第三層協(xié)同過濾補(bǔ)位。當(dāng)用戶行為積累到一定程度比如收藏了 5 個(gè)以上方案引入 ItemCF 做個(gè)性化擴(kuò)展。用戶看中了某個(gè)現(xiàn)代風(fēng)格的客廳方案系統(tǒng)會找到同風(fēng)格、同戶型、預(yù)算相近的其他方案推薦。這套混合方案的推薦命中率用戶在推薦位點(diǎn)擊且停留超過 30 秒的比例提升到了 37% 左右效果還算理想。2.3 房型數(shù)據(jù)的特征工程處理推薦效果的差距很大程度上取決于特征工程。在全屋定制場景里最核心的特征就是房型數(shù)據(jù)。怎么把三室一廳 120 平米變成一個(gè)算法能計(jì)算的向量我做了這么幾步數(shù)值特征建筑面積、客廳面積、臥室數(shù)量、衛(wèi)生間數(shù)量、陽臺數(shù)量類別特征戶型結(jié)構(gòu)橫廳/豎廳/方廳、采光朝向南/北/東西、裝修風(fēng)格現(xiàn)代/北歐/中式/輕奢文本特征把戶型描述文本用 TF-IDF 做向量化比如喜歡開放式廚房需要老人房無障礙設(shè)計(jì)2.4 為什么選擇 Python 技術(shù)棧技術(shù)選型上Python 算是這個(gè)項(xiàng)目的最優(yōu)解。推薦算法的原型驗(yàn)證、特征處理、模型調(diào)參Python 的生態(tài)實(shí)在方便——pandas 處理表格數(shù)據(jù)scikit-learn 算相似度Flask 起服務(wù)基本上一個(gè)人兩天就能把推薦服務(wù)的骨架搭完。從項(xiàng)目交付的角度Python 也降低了后續(xù)維護(hù)門檻團(tuán)隊(duì)里任何一個(gè)人都能快速接手。當(dāng)然Python 在性能上不如 Go 或 Java但推薦服務(wù)的 QPS 本來就不高這個(gè)平臺日均 UV 也就幾千瓶頸根本不在語言層面。我測過單機(jī) Flask numpy 的推薦服務(wù)單次推薦請求響應(yīng)時(shí)間在 120ms 左右完全夠用。3. 系統(tǒng)架構(gòu)與核心模塊實(shí)現(xiàn)3.1 整體架構(gòu)分層整個(gè)平臺采用的是前后端分離 獨(dú)立推薦服務(wù)的設(shè)計(jì)。前端是常規(guī)的單頁應(yīng)用后端提供 RESTful 接口推薦引擎獨(dú)立成一個(gè)微服務(wù)。頂層架構(gòu)分四層數(shù)據(jù)層MySQL 存業(yè)務(wù)數(shù)據(jù)Redis 存緩存和實(shí)時(shí)行為日志向量特征單獨(dú)存成 numpy 二進(jìn)制文件定期加載。業(yè)務(wù)服務(wù)層用戶管理、商品管理、訂單管理這一層是常規(guī)的 Web 后端。推薦服務(wù)層獨(dú)立部署的推薦引擎對外提供/api/recommend接口。前端展示層用戶錄入戶型、瀏覽推薦結(jié)果、與設(shè)計(jì)師溝通。這種分層最大的好處是推薦邏輯可以獨(dú)立迭代。我調(diào)算法參數(shù)的時(shí)候不需要重新部署整個(gè)后端改完推薦服務(wù)單獨(dú)重啟就行。再加上推薦服務(wù)掛了也不影響用戶瀏覽商品——這很重要推薦只是輔助功能不能成為系統(tǒng)瓶頸。3.2 數(shù)據(jù)庫模型設(shè)計(jì)數(shù)據(jù)庫設(shè)計(jì)是這個(gè)項(xiàng)目最花時(shí)間的地方之一。全屋定制平臺的核心實(shí)體有用戶、房型、方案、商品家具單品、設(shè)計(jì)師、預(yù)約單。其中方案這個(gè)實(shí)體很關(guān)鍵——它不是簡單地把幾個(gè)商品放一起而是包含了一整套空間設(shè)計(jì)數(shù)據(jù)平面布局圖、效果圖、商品清單、總價(jià)、適配房型 ID。核心數(shù)據(jù)表結(jié)構(gòu)如下-- 用戶表 CREATE TABLE user ( id int PRIMARY KEY AUTO_INCREMENT, nickname varchar(50), phone varchar(20), province varchar(20), city varchar(50), created_at datetime ); -- 房型表 CREATE TABLE house_type ( id int PRIMARY KEY AUTO_INCREMENT, user_id int, area_total float, room_count int, living_count int, bathroom_count int, layout_type varchar(20), orientation varchar(10), style_preference varchar(20), budget_range float, description text ); -- 設(shè)計(jì)方案表 CREATE TABLE design_scheme ( id int PRIMARY KEY AUTO_INCREMENT, title varchar(100), style_tag varchar(20), suitable_area_min float, suitable_area_max float, suitable_room varchar(20), total_price float, cover_image varchar(200), designer_id int, view_count int DEFAULT 0 ); -- 方案-商品關(guān)聯(lián)表 CREATE TABLE scheme_item ( scheme_id int, item_id int, item_count int, scene_tag varchar(20) );設(shè)計(jì)這套表結(jié)構(gòu)的時(shí)候有個(gè)教訓(xùn)house_type表一開始沒有單獨(dú)拆出來而是直接存在user表里。后來發(fā)現(xiàn)推薦服務(wù)和用戶系統(tǒng)都需要頻繁查房型數(shù)據(jù)耦合在一起導(dǎo)致接口調(diào)用很別扭最后還是拆出來獨(dú)立建表了。凡是多模塊都要用的數(shù)據(jù)一開始就要獨(dú)立成表這是這次項(xiàng)目里印象很深的一個(gè)原則。3.3 推薦服務(wù)的核心代碼實(shí)現(xiàn)推薦服務(wù)是整個(gè)項(xiàng)目的核心我拆成三個(gè)步驟構(gòu)建用戶畫像 → 候選集生成 → 排序輸出。第一步構(gòu)建用戶畫像。用戶錄入戶型信息后系統(tǒng)將房型參數(shù)標(biāo)準(zhǔn)化成一個(gè)多維向量import numpy as np def build_user_profile(house_type): 根據(jù)房型信息構(gòu)建用戶特征向量 特征維度[面積, 房間數(shù), 衛(wèi)生間數(shù), 風(fēng)格編碼, 預(yù)算等級] style_dict {現(xiàn)代簡約: 0, 北歐風(fēng): 1, 新中式: 2, 輕奢: 3, 工業(yè)風(fēng): 4} budget_level 0 if house_type[budget] 200000: budget_level 3 elif house_type[budget] 100000: budget_level 2 elif house_type[budget] 50000: budget_level 1 profile np.array([ house_type[area_total] / 200.0, # 面積歸一化 house_type[room_count] / 5.0, # 房間數(shù)歸一化 house_type[bathroom_count] / 3.0, style_dict.get(house_type[style_preference], 0) / 5.0, budget_level / 3.0 ]) return profile對面積做歸一化處理是性能改進(jìn)的關(guān)鍵點(diǎn)。最初直接用原始面積做向量計(jì)算結(jié)果 120 平米和 140 平米的方案在歐氏距離上差了很大但其實(shí)這兩個(gè)面積段對用戶來說非常接近。歸一化之后相似度計(jì)算才真正反映了用戶感知的差異。第二步候選集生成和相似度計(jì)算def compute_similarity(user_profile, scheme_features): 計(jì)算用戶畫像與候選方案特征的相似度 采用加權(quán)歐氏距離面積權(quán)重 0.3風(fēng)格權(quán)重 0.4預(yù)算權(quán)重 0.3 weights np.array([0.3, 0.2, 0.1, 0.4, 0.3]) diff (user_profile - scheme_features) * weights sim 1.0 / (1.0 np.sqrt(np.sum(diff**2))) return sim def generate_candidates(user_profile, scheme_list, top_k10): # 先規(guī)則過濾面積適配、房間數(shù)匹配、預(yù)算范圍內(nèi) candidates [] for s in scheme_list: if s[suitable_area_min] user_profile[0]*200 s[suitable_area_max]: if s[suitable_room] 三室 and user_profile[1]*5 3: candidates.append(s) # 再按相似度排序 scored [(compute_similarity(user_profile, s[feature_vec]), s) for s in candidates] scored.sort(keylambda x: x[0], reverseTrue) return [s for _, s in scored[:top_k]]這里為什么用加權(quán)歐氏距離而不是余弦相似度我一開始用的是余弦相似度但效果不理想。原因在于余弦相似度對方向一致敏感但不關(guān)心距離大小。在全屋定制的場景里用戶說120 平米方案庫里一個(gè) 60 平米的方案和一個(gè) 150 平米的方案有可能和用戶畫像的余弦相似度一樣高但這兩個(gè)方案對用戶來說意義完全不同。加權(quán)歐氏距離能更好地表達(dá)每個(gè)維度上的差距都在什么范圍內(nèi)這反而更符合業(yè)務(wù)直覺。第三步混合協(xié)同過濾補(bǔ)位。當(dāng)用戶的收藏行為超過閾值時(shí)啟用 ItemCF 擴(kuò)展推薦def itemcf_recommend(user_id, user_behavior_records, item_similarity_matrix, top_k5): 基于物品協(xié)同過濾的擴(kuò)展推薦 user_behavior_records: {item_id: score} item_similarity_matrix: 預(yù)計(jì)算的物品相似度矩陣 scores {} for item_id, score in user_behavior_records.items(): similar_items item_similarity_matrix[item_id] for sim_item, sim_val in similar_items.items(): if sim_item not in scores: scores[sim_item] 0 scores[sim_item] sim_val * score sorted_items sorted(scores.items(), keylambda x: x[1], reverseTrue) return [item_id for item_id, s in sorted_items[:top_k]]3.4 規(guī)則引擎的設(shè)計(jì)要點(diǎn)規(guī)則引擎是這個(gè)項(xiàng)目里容易被低估的模塊。推薦算法算得再準(zhǔn)如果推薦了一個(gè)超出用戶預(yù)算 3 倍的方案用戶只會覺得平臺不靠譜。規(guī)則的實(shí)現(xiàn)不需要復(fù)雜的技術(shù)就是一個(gè)可配置的過濾器鏈。舉幾個(gè)實(shí)際用到的規(guī)則面積規(guī)則方案適配面積包含用戶房型面積預(yù)算規(guī)則方案總價(jià)不超過用戶預(yù)算的 120%留一點(diǎn)浮動空間風(fēng)格規(guī)則用戶未選風(fēng)格時(shí)不限制已選風(fēng)格時(shí)只推薦同風(fēng)格方案空間規(guī)則三室戶型必須覆蓋客廳、主臥、次臥至少兩個(gè)空間規(guī)則引擎用 Python 的裝飾器模式實(shí)現(xiàn)每個(gè)規(guī)則是一個(gè)獨(dú)立函數(shù)新增規(guī)則只需要添加一個(gè)裝飾器函數(shù)def rule_area_match(func): wraps(func) def wrapper(user_profile, candidate): area user_profile[0] * 200 if not (candidate.suitable_area_min area candidate.suitable_area_max): return None return func(user_profile, candidate) return wrapper如果一個(gè)方案被任何一條規(guī)則過濾掉推薦列表里就不會出現(xiàn)。這樣設(shè)計(jì)的好處是產(chǎn)品經(jīng)理后續(xù)加了新規(guī)則比如2024 年流行奶油風(fēng)優(yōu)先展示我只需要新增一個(gè)規(guī)則裝飾器不需要改動推薦主流程。4. 推薦效果評估與調(diào)優(yōu)實(shí)錄4.1 離線評估先跑通指標(biāo)推薦系統(tǒng)做完之后不能直接上線就完事了得先做離線評估。我們在方案庫中隨機(jī)抽取了 500 個(gè)方案構(gòu)造了 200 組模擬用戶數(shù)據(jù)從房型面積、風(fēng)格偏好、預(yù)算三個(gè)維度模擬真實(shí)用戶。評估指標(biāo)我重點(diǎn)看三個(gè)指標(biāo)計(jì)算公式本項(xiàng)目實(shí)測值精確率PrecisionK推薦列表中用戶真實(shí)點(diǎn)擊的方案數(shù) / K0.41召回率RecallK推薦列表中用戶真實(shí)點(diǎn)擊的方案數(shù) / 用戶點(diǎn)擊的總方案數(shù)0.28覆蓋率Coverage被推薦過的方案數(shù) / 方案庫總數(shù)0.67精確率 0.41 的意思是給用戶推薦 10 個(gè)方案平均有 4 個(gè)是用戶會點(diǎn)擊甚至預(yù)約的。做推薦系統(tǒng)的都知道這個(gè)數(shù)字在低頻消費(fèi)場景算很不錯(cuò)了。4.2 線上效果一次失敗的 A/B 測試離線指標(biāo)跑得不錯(cuò)但上線前我還是有點(diǎn)不安。推薦系統(tǒng)的慣例是離線指標(biāo)不等于線上效果我特意搭了一套簡單的 A/B 測試把用戶分成兩組實(shí)驗(yàn)組看到的是新版智能推薦對照組看到的是簡單的熱銷榜。跑了一周結(jié)果讓我有點(diǎn)意外新版推薦位的點(diǎn)擊率CTR確實(shí)比熱銷榜高了 18%但用戶在平臺上的平均停留時(shí)長反而下降了 9%。這個(gè)結(jié)果很反直覺。后來復(fù)盤發(fā)現(xiàn)了原因推薦太精準(zhǔn)了用戶很快就找到了自己想要的東西沒有經(jīng)歷那種隨便逛逛、看看別人家怎么裝的探索過程。對全屋定制平臺來說用戶瀏覽本身就是一種想獲取靈感的行為停留時(shí)間長反而意味著用戶對平臺內(nèi)容的認(rèn)可度高。這個(gè)案例提醒我一個(gè)很重要的道理推薦系統(tǒng)的目標(biāo)不一定是把所有指標(biāo)都拉滿而是要服務(wù)于業(yè)務(wù)的真實(shí)價(jià)值。如果平臺靠廣告變現(xiàn)停留時(shí)長就很重要如果平臺靠成交轉(zhuǎn)化點(diǎn)擊率就很重要。每個(gè)平臺都應(yīng)該先想清楚自己的北極星指標(biāo)是什么。4.3 調(diào)參過程記錄調(diào)參階段我經(jīng)歷了不少反復(fù)。先記錄一下最有價(jià)值的幾組參數(shù)實(shí)驗(yàn)實(shí)驗(yàn)一權(quán)重調(diào)整對精確率的影響初始權(quán)重是我拍腦袋定的面積 0.3房間數(shù) 0.2衛(wèi)生間 0.1風(fēng)格 0.4預(yù)算 0.3離線測試精確率 0.38。后來我把預(yù)算權(quán)重調(diào)高到 0.5風(fēng)格降到 0.2精確率掉到了 0.31。原因很直接用戶嘴上說喜歡某種風(fēng)格但真到掏錢的時(shí)候第一道關(guān)卡永遠(yuǎn)是預(yù)算。風(fēng)格可以在預(yù)算滿足之后再講。重新調(diào)回風(fēng)格 0.4、預(yù)算 0.3精確率回到 0.39說明初始的權(quán)重已經(jīng)接近一個(gè)不錯(cuò)的局部最優(yōu)了。實(shí)驗(yàn)二規(guī)則過濾是否應(yīng)該放在相似度計(jì)算之前有一次我調(diào)整了流程讓規(guī)則過濾發(fā)生在相似度計(jì)算之后結(jié)果發(fā)現(xiàn)線上用戶點(diǎn)擊率降了約 15%。原因不難理解如果考慮相似度的那批方案里混入大量不符合規(guī)則的結(jié)果用戶看到的第一屏推薦就失去了針對性。規(guī)則前置相當(dāng)于先把不合適的方案擋在門外相似度排序只是對剩余方案精排。實(shí)驗(yàn)三K 值的選擇推薦列表長度 K我從 6 調(diào)到 10 再調(diào)到 15。K15 時(shí)點(diǎn)擊率明顯下降因?yàn)橛脩魹g覽精力有限超過 8-10 個(gè)方案后基本就不往下滑了。K10 和 K8 差不多最終選了 10——給足用戶選擇空間又不會因?yàn)檫x項(xiàng)過多導(dǎo)致疲勞。4.4 冷啟動問題的處理方案全屋定制平臺的新用戶冷啟動是個(gè)繞不開的問題。用戶剛注冊沒有任何歷史行為推薦系統(tǒng)完全不知道給他推什么。我做了兩件事用房型數(shù)據(jù)做首次畫像用戶在第一次錄入戶型信息時(shí)我們就認(rèn)為已經(jīng)獲得了足夠的畫像特征。因?yàn)槿荻ㄖ频耐扑]強(qiáng)依賴物理空間數(shù)據(jù)房型錄入本身就是最強(qiáng)的一次用戶意圖聲明。用地域特征混排兜底如果用戶連房型都沒填推薦列表就用同省份、同城市的熱門方案兜底。比如一線城市的用戶默認(rèn)推薦 90-130 平米的現(xiàn)代簡約方案三四線城市的用戶默認(rèn)推薦 110-150 平米的輕奢或新中式方案。數(shù)據(jù)證明這種粗粒度的畫像已經(jīng)能帶來不錯(cuò)的點(diǎn)擊率。5. 常見問題與排查技巧實(shí)錄5.1 推薦結(jié)果加載慢的問題上線第二周運(yùn)營反饋推薦頁打開很慢平均要 3 秒以上。排查后發(fā)現(xiàn)問題出在候選集生成階段——每次請求都實(shí)時(shí)遍歷全量方案庫計(jì)算相似度時(shí)又把所有方案的特征向量讀了一遍。優(yōu)化方案很簡單緩存 預(yù)計(jì)算。把方案庫的特征向量加載到 Redis 中用戶畫像不變的情況下緩存推薦結(jié)果 30 分鐘。同時(shí)把規(guī)則過濾的結(jié)果也緩存下來因?yàn)榇蟛糠钟脩舻姆啃蛥?shù)在短時(shí)間內(nèi)不會變。優(yōu)化后接口響應(yīng)時(shí)間從 3 秒降到 150ms效果立竿見影。做推薦系統(tǒng)一定要記住能預(yù)計(jì)算的絕不在線計(jì)算能緩存的絕不實(shí)時(shí)生成。5.2 相似度計(jì)算中的維度災(zāi)難初期特征工程加了太多維度包括文本向量化后的幾十個(gè)維度。一開始總覺得特征越多越準(zhǔn)確結(jié)果排序效果反而變差了。這就是典型的維度災(zāi)難——維度越多樣本在高維空間里的分布就越稀疏距離計(jì)算的區(qū)分度會急劇下降。后來做了特征篩選把主特征壓縮到 5-8 個(gè)核心維度排序效果反而上升了。這個(gè)經(jīng)驗(yàn)很實(shí)用做特征工程的第一原則不是越多越好而是越準(zhǔn)越好。每個(gè)特征入口都要問一句這個(gè)特征真的影響用戶的定制決策嗎5.3 推薦結(jié)果太像了的問題測試階段我們發(fā)現(xiàn)推薦列表里的方案同質(zhì)化嚴(yán)重——10 個(gè)方案里有 6 個(gè)看起來是同一個(gè)風(fēng)格的同一類型。這個(gè)問題來自相似度排序的天然傾向和用戶畫像最相似的方案往往聚集在一起。解決方式是引入一個(gè)簡單的多樣度懲罰MMRMaximal Marginal Relevance。每次選入一個(gè)新方案時(shí)不僅看它和用戶畫像的相似度還看它和已經(jīng)在推薦列表里的方案的相似度兩者做差def mmr_ranking(user_profile, candidates, lambda_val0.7, top_k10): MMR多樣度算法 lambda越大越偏向相關(guān)度lambda越小越偏向多樣度 selected [] remaining candidates[:] while len(selected) top_k and remaining: score_list [] for cand in remaining: related compute_similarity(user_profile, cand.feature_vec) if selected: max_similar max(compute_similarity(cand.feature_vec, s.feature_vec) for s in selected) else: max_similar 0 final_score lambda_val * related - (1 - lambda_val) * max_similar score_list.append((final_score, cand)) score_list.sort(keylambda x: x[0], reverseTrue) selected.append(score_list[0][1]) remaining.remove(score_list[0][1]) return selectedlambda 設(shè)為 0.7 時(shí)相關(guān)度和多樣度平衡得最好。上線后用戶對推薦位的滿意度評分提升了約 20%。5.4 行為數(shù)據(jù)采集的坑推薦系統(tǒng)的數(shù)據(jù)依賴用戶行為但我們一開始并沒有埋點(diǎn)。結(jié)果就是系統(tǒng)上線了卻拿不到任何用戶點(diǎn)擊了哪個(gè)推薦位的數(shù)據(jù)——推薦效果根本沒法評估。后來緊急補(bǔ)了一套埋點(diǎn)方案在推薦位的每個(gè)方案卡片上加上position參數(shù)標(biāo)記是推薦列表的第幾個(gè)點(diǎn)擊事件上報(bào)后和推薦請求 ID 關(guān)聯(lián)。這樣就能精確統(tǒng)計(jì)到推薦了哪些位置、用戶點(diǎn)了哪些位置。這件事給了我一個(gè)教訓(xùn)埋點(diǎn)應(yīng)該在推薦系統(tǒng)開發(fā)的第一天就做好而不是等系統(tǒng)上線后補(bǔ)。5.5 模型評估中自欺欺人的陷阱做離線評估的時(shí)候要特別小心不能拿參與過訓(xùn)練的數(shù)據(jù)去評估。我第一次評估時(shí)直接用了訓(xùn)練集的數(shù)據(jù)測試模型精確率高得嚇人0.78差點(diǎn)以為推薦效果已經(jīng)完美了。后來對照業(yè)務(wù)數(shù)據(jù)才發(fā)現(xiàn)模型是在背答案——剛才訓(xùn)練的樣本當(dāng)然能被記住。正確的做法是劃分訓(xùn)練集和測試集比如 8:2 隨機(jī)切分保證測試集里的數(shù)據(jù)是模型沒見過的。再嚴(yán)格一點(diǎn)還可以按時(shí)間劃分——用前 6 個(gè)月的數(shù)據(jù)訓(xùn)練用后 2 個(gè)月的數(shù)據(jù)測試模擬真實(shí)世界的推薦場景。6. 項(xiàng)目交付與擴(kuò)展思考6.1 當(dāng)前版本的落地效果從開發(fā)到上線這個(gè)項(xiàng)目經(jīng)歷了大約 4 個(gè)月的時(shí)間核心推薦系統(tǒng)上線后跑了兩周。目前平臺的整體數(shù)據(jù)大概是這樣的推薦位點(diǎn)擊率32%37%相比靜態(tài)熱銷榜提升約 45%推薦方案預(yù)約率9%每 100 次點(diǎn)擊約產(chǎn)生 9 次設(shè)計(jì)師預(yù)約用戶平均瀏覽方案數(shù)量從 4 個(gè)提升到 7.5 個(gè)后端接口平均響應(yīng)時(shí)間150ms 左右預(yù)約率 9% 是運(yùn)營團(tuán)隊(duì)最看重的指標(biāo)因?yàn)樵谌荻ㄖ菩袠I(yè)預(yù)約設(shè)計(jì)師可以理解為意向客戶產(chǎn)生。推薦系統(tǒng)能直接帶動這個(gè)指標(biāo)說明它觸達(dá)的是真實(shí)需求而不只是讓頁面多幾個(gè)點(diǎn)擊。6.2 后續(xù)可以做的幾個(gè)擴(kuò)展方向現(xiàn)在這套系統(tǒng)只能說完成了 80%還有幾個(gè)方向值得繼續(xù)深耕引入圖像識別輔助很多用戶會在平臺上找參考圖我就要這種效果。如果讓用戶直接上傳喜歡的裝修效果圖系統(tǒng)通過圖像識別提取風(fēng)格、配色、家具布局特征畫像精度會提升一個(gè)量級。協(xié)同過濾升級為圖神經(jīng)網(wǎng)絡(luò)推薦算法目前的協(xié)同過濾還是最基礎(chǔ)的版本。等到用戶行為積累到一定程度可以考慮引入圖神經(jīng)網(wǎng)絡(luò)建模更復(fù)雜的用戶—方案—設(shè)計(jì)師交互關(guān)系。價(jià)格彈性和敏感度分析現(xiàn)在預(yù)算是一個(gè)硬編碼的規(guī)則約束。實(shí)際上不同用戶的預(yù)算彈性差異很大有些用戶說 20 萬預(yù)算但對質(zhì)量和風(fēng)格更在意有些用戶說 20 萬就真的是 19 萬都不行。分析用戶的預(yù)算彈性可以讓推薦結(jié)果更貼近真實(shí)。6.3 給同行的幾點(diǎn)建議這個(gè)項(xiàng)目做完有幾個(gè)體會是拿真金白銀換來的寫在這里供參考推薦效果不要只看點(diǎn)擊率。點(diǎn)擊率高可能只是說明用戶好奇并不代表他愿意下單。全屋定制這種行業(yè)預(yù)約、詢價(jià)、到店才算有效轉(zhuǎn)化。技術(shù)要服務(wù)業(yè)務(wù)目標(biāo)。我們最后把推薦系統(tǒng)的核心指標(biāo)定義為預(yù)約率而不是點(diǎn)擊率整個(gè)優(yōu)化方向才真正聚焦。可控性比算法先進(jìn)更重要。推薦邏輯里留下人工干預(yù)的入口運(yùn)營活動可以臨時(shí)置頂某些方案這種靈活性在業(yè)務(wù)初期尤為重要。過度依賴純算法運(yùn)營會覺得系統(tǒng)不可控反過來會阻礙推薦系統(tǒng)的落地。關(guān)于整個(gè)項(xiàng)目我自己最大的收獲倒不是技術(shù)層面的而是意識到推薦系統(tǒng)在全屋定制這類垂直領(lǐng)域真正的挑戰(zhàn)在于如何把行業(yè)知識結(jié)構(gòu)化、規(guī)則化、可計(jì)算化。算法本身并不神秘難的是理解行業(yè)的每一個(gè)隱性規(guī)則然后把它一點(diǎn)點(diǎn)拆成代碼能理解的特征和約束。如果你也在做類似的行業(yè)平臺希望這套思路能幫你少走一些彎路。