聯(lián)規(guī)則實(shí)戰(zhàn):從Apriori到FP-樹頻集算法的選型與落地)
有沒有遇到過這種情況業(yè)務(wù)方丟給你一份幾十萬行的訂單流水開口就問“哪些商品經(jīng)常被一起買能不能挖出點(diǎn)搭配銷售的規(guī)律”你如果用Excel透視表硬扛可能折騰一整天只得到一堆沒什么解釋力的數(shù)字。這時(shí)候關(guān)聯(lián)規(guī)則就該上場(chǎng)了。關(guān)聯(lián)規(guī)則算是機(jī)器學(xué)習(xí)里相當(dāng)經(jīng)典的一類無監(jiān)督學(xué)習(xí)方法核心任務(wù)是從交易數(shù)據(jù)、行為日志這類“事件集合”中找出高頻共現(xiàn)的模式。而提到關(guān)聯(lián)規(guī)則繞不開的兩個(gè)算法就是Apriori算法和FP-樹頻集算法FP-Growth。這篇文章不打算做教科書式的理論復(fù)述我就從實(shí)際項(xiàng)目里怎么用、這兩個(gè)算法各自好在哪里、坑在哪里的角度把關(guān)聯(lián)規(guī)則這件事講透。1. 關(guān)聯(lián)規(guī)則到底在解決什么問題從購(gòu)物籃到用戶行為1.1 一個(gè)被反復(fù)說起的案例購(gòu)物籃分析的本質(zhì)絕大多數(shù)講關(guān)聯(lián)規(guī)則的文章都會(huì)從“啤酒和尿布”的故事開始。這個(gè)故事的真實(shí)性其實(shí)一直被爭(zhēng)論但它能流傳這么多年確實(shí)精準(zhǔn)說明了關(guān)聯(lián)規(guī)則的應(yīng)用場(chǎng)景在一個(gè)購(gòu)物籃中出現(xiàn)了商品A那么商品B同時(shí)出現(xiàn)的概率是不是顯著變高了如果是那就可以據(jù)此做出商品陳列、捆綁銷售、促銷策略等業(yè)務(wù)決策。我在實(shí)際項(xiàng)目中接觸到的場(chǎng)景比“啤酒尿布”要寬泛得多。比如一個(gè)在線教育平臺(tái)用戶在一個(gè)月內(nèi)點(diǎn)擊了“Python入門”“數(shù)據(jù)分析實(shí)戰(zhàn)”“SQL基礎(chǔ)”三門課程這三門課之間構(gòu)成什么關(guān)系再比如一個(gè)內(nèi)容社區(qū)用戶讀了一篇“裝修避坑指南”之后會(huì)不會(huì)更大概率去讀“全屋定制選購(gòu)攻略”這些都可以建模為關(guān)聯(lián)規(guī)則問題。所謂購(gòu)物籃分析本質(zhì)上就是把“一個(gè)用戶的一次行為集合”看成一個(gè)“籃子”把籃子里的每一項(xiàng)看成“商品”。這個(gè)籃子可以是訂單也可以是會(huì)話甚至是一個(gè)時(shí)間段內(nèi)的行為序列。把數(shù)據(jù)抽象成這樣的結(jié)構(gòu)之后我們就不再被具體業(yè)務(wù)形態(tài)干擾可以用同一套算法去處理非常多的場(chǎng)景。1.2 三個(gè)核心指標(biāo)支持度、置信度、提升度學(xué)習(xí)關(guān)聯(lián)規(guī)則第一關(guān)就是理解三個(gè)指標(biāo)支持度Support、置信度Confidence、提升度Lift。很多人會(huì)背公式但真的拿到業(yè)務(wù)數(shù)據(jù)時(shí)還是搞不清楚該怎么用。我建議從業(yè)務(wù)含義去理解它們。支持度衡量一個(gè)項(xiàng)集比如“牛奶和面包同時(shí)出現(xiàn)”在所有訂單中的出現(xiàn)頻率。計(jì)算公式是包含該項(xiàng)集的訂單數(shù)除以總訂單數(shù)。支持度解決的是“這個(gè)規(guī)律到底覆蓋了多少人”的問題。如果一條規(guī)則只有5個(gè)訂單支持哪怕置信度100%也不值得投入資源。置信度表示在已經(jīng)包含前件比如牛奶的訂單中同時(shí)包含后件面包的比例。計(jì)算公式是“牛奶和面包同時(shí)出現(xiàn)的支持度”除以“牛奶出現(xiàn)的支持度”。置信度解決的是“有A時(shí)出現(xiàn)B的概率有多大”的問題它是衡量規(guī)則強(qiáng)度的指標(biāo)。提升度這是很多人最容易忽略但最重要的指標(biāo)。提升度的計(jì)算公式是規(guī)則置信度除以后件的支持度它衡量的是“A出現(xiàn)后B的概率”和“B本來就出現(xiàn)的概率”之間的比值。如果提升度大于1說明A對(duì)B有正向促進(jìn)作用小于1則說明更傾向于抑制等于1則代表A和B是獨(dú)立的所謂的規(guī)則只是巧合。舉個(gè)具體的例子來說明。假設(shè)一個(gè)超市有10000條訂單其中600條包含牛奶400條包含面包同時(shí)包含兩者的有200條。那么“牛奶→面包”的支持度是2%置信度是200/600約等于33.3%而提升度是33.3%除以4%等于8.33。這個(gè)提升度意味著買了牛奶的顧客買面包的概率是普通顧客的8倍多這顯然是條強(qiáng)規(guī)則。1.3 為什么“相關(guān)性”不等于“因果性”這個(gè)提醒必須放在最前面。關(guān)聯(lián)規(guī)則挖掘出來的只是“共現(xiàn)關(guān)系”它描述的是“這兩個(gè)東西傾向于一起出現(xiàn)”但它完全不能證明“一個(gè)導(dǎo)致另一個(gè)”。在業(yè)務(wù)實(shí)踐中我見過很多團(tuán)隊(duì)的誤用——把關(guān)聯(lián)規(guī)則的結(jié)果直接解釋成因果然后做出了錯(cuò)誤的策略。舉例來說數(shù)據(jù)分析發(fā)現(xiàn)“購(gòu)買嬰兒紙尿褲的用戶會(huì)在同一周內(nèi)購(gòu)買啤酒”真正的解釋很可能是“購(gòu)買紙尿褲的大概率是有夜間照顧嬰兒需求的中青年男性他們?cè)诓少?gòu)日常用品的同時(shí)順便給自己囤了啤酒”而不是“紙尿褲導(dǎo)致啤酒購(gòu)買”。這就是基于人群特征的共同驅(qū)動(dòng)而不是商品間的因果鏈條。所以在向業(yè)務(wù)方輸出結(jié)果時(shí)一定要把規(guī)則描述成“在某種人群或場(chǎng)景下存在強(qiáng)共現(xiàn)關(guān)系”而不是“A導(dǎo)致B”。關(guān)聯(lián)規(guī)則是用來發(fā)現(xiàn)組合機(jī)會(huì)的不是用來做因果推斷的。2. Apriori算法先找頻繁項(xiàng)集再生成規(guī)則的經(jīng)典路線2.1 算法的兩步走邏輯Apriori算法的思想其實(shí)非常直白整個(gè)過程可以拆成兩大步第一步是找出所有頻繁項(xiàng)集第二步是從頻繁項(xiàng)集中生成關(guān)聯(lián)規(guī)則。“頻繁”的定義就是支持度大于等于我們?cè)O(shè)定的最小支持度閾值。比如設(shè)定最小支持度0.02那么在所有訂單中出現(xiàn)次數(shù)至少達(dá)到總數(shù)2%的商品集合就是頻繁項(xiàng)集。Apriori算法的核心操作是一個(gè)逐層遞推的過程先生成所有單商品項(xiàng)集1項(xiàng)集篩選出滿足最小支持度的然后基于頻繁1項(xiàng)集兩兩組合生成2項(xiàng)集再篩選生成3項(xiàng)集再篩選……直到無法生成新的頻繁項(xiàng)集為止。這里的“生成候選集”是一個(gè)組合爆炸的過程如果商品種類有1000種那2項(xiàng)集就有約50萬種3項(xiàng)集更是上億量級(jí)。如果沒有優(yōu)化機(jī)制普通機(jī)器根本跑不動(dòng)。Apriori的聰明之處就在于它有一條非常重要的性質(zhì)——Apriori性質(zhì)。2.2 剪枝策略Apriori性質(zhì)與其推導(dǎo)Apriori性質(zhì)用一句話概括就是如果一個(gè)項(xiàng)集是頻繁的那么它的所有子集都是頻繁的反過來如果一個(gè)項(xiàng)集是非頻繁的那么它的所有超集都是非頻繁的。這個(gè)性質(zhì)看起來像廢話但它提供了一把極強(qiáng)的剪枝剪刀。假設(shè)“牛奶”本身不是頻繁項(xiàng)出現(xiàn)次數(shù)太少那么在生成2項(xiàng)集時(shí)所有包含“牛奶”的組合比如“牛奶面包”“牛奶蘋果”都可以直接扔掉根本不需要去統(tǒng)計(jì)它們的支持度。這就砍掉了大量的無效計(jì)算。在候選集生成的過程中還有一個(gè)可以優(yōu)化的點(diǎn)生成k1項(xiàng)集的候選項(xiàng)時(shí)只從“前k-1項(xiàng)相同”的頻繁k項(xiàng)集中做合并這樣能保證不會(huì)生成重復(fù)的組合同時(shí)確保新候選集的所有k-1項(xiàng)子集都已經(jīng)是頻繁的。正是這個(gè)“先剪枝再計(jì)數(shù)”的思路讓Apriori成為那個(gè)年代最有效的關(guān)聯(lián)規(guī)則挖掘算法。我在實(shí)現(xiàn)的時(shí)候會(huì)特別強(qiáng)調(diào)這個(gè)性質(zhì)因?yàn)楹芏喑鯇W(xué)者會(huì)忽略它直接做全量組合結(jié)果就是內(nèi)存爆炸、跑半天出不來根本沒有理解算法的精髓。2.3 一個(gè)可以跑起來的最小實(shí)現(xiàn)mlxtend在Python里我并不推薦從零手寫Apriori的底層代碼除非你是為了學(xué)習(xí)或者研究目的。工業(yè)場(chǎng)景下直接使用mlxtend庫(kù)就夠了它封裝了一套非常干凈的關(guān)聯(lián)規(guī)則挖掘接口。首先準(zhǔn)備數(shù)據(jù)。關(guān)聯(lián)規(guī)則的標(biāo)準(zhǔn)輸入是一個(gè)“事務(wù)”列表每行代表一個(gè)訂單每行的內(nèi)容是商品集合。import pandas as pd from mlxtend.preprocessing import TransactionEncoder from mlxtend.frequent_patterns import apriori from mlxtend.frequent_patterns import association_rules # 模擬一組訂單數(shù)據(jù) dataset [ [牛奶, 面包, 黃油], [牛奶, 面包], [啤酒, 尿布], [牛奶, 尿布, 啤酒], [面包, 黃油], [牛奶, 面包, 尿布], [啤酒, 尿布], ] # 將事務(wù)數(shù)據(jù)轉(zhuǎn)換為one-hot編碼的DataFrame te TransactionEncoder() te_ary te.fit(dataset).transform(dataset) df pd.DataFrame(te_ary, columnste.columns_) # 跑Apriori最小支持度設(shè)為0.2 frequent_itemsets apriori(df, min_support0.2, use_colnamesTrue) print(frequent_itemsets)接下來從頻繁項(xiàng)集生成規(guī)則并按照提升度排序rules association_rules(frequent_itemsets, metriclift, min_threshold1.0) rules.sort_values(lift, ascendingFalse, inplaceTrue) print(rules[[antecedents, consequents, support, confidence, lift]])這段代碼就能完成一次完整的Apriori挖掘。輸出結(jié)果里有幾個(gè)要注意的地方use_colnamesTrue是為了讓項(xiàng)集顯示為商品名稱而不是數(shù)字編號(hào)association_rules里的metriclift是篩選規(guī)則的最常用方式實(shí)際項(xiàng)目中我一般至少把提升度閾值設(shè)在1.5以上否則噪聲太多。2.4 Apriori的瓶頸為什么數(shù)據(jù)稍大就變慢Apriori算法夠經(jīng)典但它的弱點(diǎn)也非常明顯。它需要多次掃描全量數(shù)據(jù)集每生成一層的候選項(xiàng)集就要掃一次數(shù)據(jù)庫(kù)。假設(shè)最長(zhǎng)的頻繁項(xiàng)集長(zhǎng)度為L(zhǎng)那就要掃描L次。在數(shù)據(jù)量幾十萬上百萬條交易時(shí)I/O開銷就很可觀了。另一個(gè)問題是候選項(xiàng)集的數(shù)量依然可能很大。剪枝能去掉一部分但組合的基數(shù)擺在那里特別是商品種類多、數(shù)據(jù)稀疏的時(shí)候候選集的生成和計(jì)數(shù)仍然非常耗時(shí)。我在一個(gè)幾十萬訂單的數(shù)據(jù)集上實(shí)測(cè)過商品種類約5000種時(shí)Apriori跑完的耗時(shí)要幾十秒到幾分鐘這還算可以接受。但如果商品種類上到幾萬、訂單量上到幾百萬Apriori就有點(diǎn)扛不住了。這時(shí)候就該把目光轉(zhuǎn)向FP-樹頻集算法。3. FP-樹頻集算法從“多次掃描”到“一次構(gòu)建”的優(yōu)化思路3.1 FP樹的構(gòu)建過程拆解FP-樹頻集算法通常直接叫FP-Growth它的核心思路和Apriori完全不同。Apriori是“生成候選集掃描驗(yàn)證”FP-Growth則是“壓縮數(shù)據(jù)到內(nèi)存中的樹結(jié)構(gòu)遞歸挖掘”。FP-Growth只需要掃描數(shù)據(jù)庫(kù)兩次這是它性能碾壓Apriori的關(guān)鍵。第一次掃描統(tǒng)計(jì)每個(gè)商品的支持度過濾掉不滿足最小支持度的商品然后按照支持度從高到低排序。第二次掃描逐行處理每條交易把交易中出現(xiàn)且頻繁的商品按照全局頻率降序插入到一棵FP樹中同時(shí)在節(jié)點(diǎn)上累加計(jì)數(shù)。把事務(wù)壓縮進(jìn)樹后每個(gè)項(xiàng)集的所有必要信息都保存在了樹和頭指針表中不需要再反復(fù)回到原始數(shù)據(jù)里去數(shù)次數(shù)。用一個(gè)小例子說明。假設(shè)有三條交易(魚, 牛奶, 面包)、(面包, 牛奶)、(魚, 面包)最小支持度為1。第一次統(tǒng)計(jì)得到魚出現(xiàn)2次、面包3次、牛奶2次都是頻繁的排序順序?yàn)槊姘Ⅳ~、牛奶按頻次降序。第二條交易插入了面包和牛奶第三條插入面包和魚。最終樹的結(jié)構(gòu)是根節(jié)點(diǎn)下有面包計(jì)數(shù)3面包節(jié)點(diǎn)下有兩個(gè)分支牛奶計(jì)數(shù)2和魚計(jì)數(shù)1牛奶下還有魚計(jì)數(shù)1。路徑面包→魚和面包→牛奶→魚共享了節(jié)點(diǎn)“面包”這就壓縮了數(shù)據(jù)量。整個(gè)樹的結(jié)構(gòu)記錄了所有頻繁模式的關(guān)鍵信息。3.2 從FP樹挖掘頻繁項(xiàng)集條件模式基與遞歸生長(zhǎng)構(gòu)建完FP樹接下來就是遞歸挖掘的過程。FP-Growth的挖掘策略不像Apriori那樣顯式枚舉候選集而是通過構(gòu)造條件模式基Conditional Pattern Base來逐步生長(zhǎng)頻繁項(xiàng)集。以經(jīng)常被一起買的“牛奶魚”為例。要找到所有包含“牛奶”的頻繁項(xiàng)集需要先找到FP樹中所有包含“牛奶”的路徑即牛奶的前綴路徑。每條路徑上都帶著計(jì)數(shù)信息——路徑中每個(gè)節(jié)點(diǎn)恰好出現(xiàn)了多少次。把相同前綴路徑的計(jì)數(shù)累加就得到了“牛奶的條件模式基”?;跅l件模式基可以構(gòu)建一棵“牛奶的條件FP樹”。在條件FP樹上繼續(xù)遞歸挖掘把新挖掘到的節(jié)點(diǎn)和原模式合并就得到了更長(zhǎng)的頻繁項(xiàng)集。整個(gè)過程沒有任何重復(fù)掃描原數(shù)據(jù)全部在樹結(jié)構(gòu)上完成。理解FP-Growth的難點(diǎn)在于“遞歸生長(zhǎng)”的抽象感。我個(gè)人的學(xué)習(xí)經(jīng)驗(yàn)是先手工跑一遍小規(guī)模數(shù)據(jù)的挖掘過程把每一層的條件模式基畫出來然后再回來看代碼會(huì)順暢很多。這類算法的遞歸邏輯用空想來理解很容易繞暈。3.3 用同一份數(shù)據(jù)實(shí)測(cè)FP-Growthmlxtend同樣封裝了FP-Growth的實(shí)現(xiàn)接口和Apriori幾乎一致替換起來非常方便。from mlxtend.frequent_patterns import fpgrowth # 使用相同的數(shù)據(jù)格式 frequent_itemsets_fp fpgrowth(df, min_support0.2, use_colnamesTrue) print(frequent_itemsets_fp) # 生成規(guī)則 rules_fp association_rules(frequent_itemsets_fp, metriclift, min_threshold1.0) rules_fp.sort_values(lift, ascendingFalse, inplaceTrue) print(rules_fp[[antecedents, consequents, support, confidence, lift]])在數(shù)據(jù)規(guī)模較小時(shí)兩者的結(jié)果幾乎一致。但到幾十萬條交易、商品上萬的數(shù)據(jù)集上FP-Growth通常比Apriori快一到兩個(gè)數(shù)量級(jí)。我跑過的最典型的一個(gè)對(duì)比是Apriori需要40秒才能完成的挖掘任務(wù)FP-Growth大概3秒就出來了差別非常直觀。值得注意的是有少數(shù)的數(shù)據(jù)分布場(chǎng)景下兩者差距沒有這么明顯比如頻繁項(xiàng)集非常短所有項(xiàng)集長(zhǎng)度都在2以內(nèi)的時(shí)候Apriori的多次掃描開銷并不大差距就不顯著。但從實(shí)際經(jīng)驗(yàn)看大多數(shù)業(yè)務(wù)數(shù)據(jù)都是長(zhǎng)尾分布FP-Growth的優(yōu)勢(shì)是壓倒性的。3.4 結(jié)構(gòu)設(shè)計(jì)背后的工程考量FP-Growth真正聰明的地方是它構(gòu)建了一棵高度壓縮的前綴樹。這棵樹能把共享前綴的交易合并存儲(chǔ)從而大幅減少內(nèi)存占用。比如有一萬條交易都包含面包但后面的搭配各不相同在FP樹里面包節(jié)點(diǎn)只出現(xiàn)一次后面的分支再分別延伸。這種做法非常契合真實(shí)業(yè)務(wù)數(shù)據(jù)的特點(diǎn)頭部商品高度集中長(zhǎng)尾商品稀疏分散。FP樹利用的就是這個(gè)分布特征。另外FP-Growth在挖掘過程中也展現(xiàn)出良好的模塊化特性。每一個(gè)條件FP樹的構(gòu)建和挖掘都是獨(dú)立的天然適合并行化處理。在大數(shù)據(jù)場(chǎng)景下可以先對(duì)不同商品的條件模式基做分區(qū)再用分布式計(jì)算來加速。這一點(diǎn)在單機(jī)版的Apriori中是很難做到的。4. 兩個(gè)算法怎么選業(yè)務(wù)場(chǎng)景、數(shù)據(jù)規(guī)模和工程成本的權(quán)衡4.1 兩個(gè)算法的對(duì)比表格很多人在學(xué)習(xí)階段糾結(jié)“到底用哪個(gè)”實(shí)際上工程上的答案很明確默認(rèn)用FP-Growth。但搞清楚兩者的區(qū)別依然非常重要因?yàn)樵谀承┨囟l件下Apriori反而更有優(yōu)勢(shì)。維度AprioriFP-Growth核心思想候選集生成 逐層篩選前綴樹壓縮 遞歸挖掘掃描次數(shù)多次頻繁項(xiàng)集層數(shù)兩次內(nèi)存占用候選集常駐內(nèi)存開銷較大FP樹結(jié)構(gòu)較為緊湊但條件庫(kù)也占內(nèi)存適用數(shù)據(jù)量中小數(shù)據(jù)集幾千到幾十萬中到大規(guī)模數(shù)據(jù)集百萬級(jí)實(shí)現(xiàn)復(fù)雜度邏輯簡(jiǎn)單易理解樹構(gòu)建和遞歸較復(fù)雜算法效果結(jié)果一致結(jié)果一致有一點(diǎn)要特別說明兩個(gè)算法挖掘的頻繁項(xiàng)集在相同支持度閾值下是完全一致的差異只體現(xiàn)在效率和資源開銷上。所以選型時(shí)不需要擔(dān)心結(jié)果質(zhì)量不同只需要考慮計(jì)算資源。4.2 不同數(shù)據(jù)規(guī)模下的實(shí)測(cè)感受我個(gè)人的選型經(jīng)驗(yàn)大致是這樣的。數(shù)據(jù)量在1萬條訂單以內(nèi)商品種類不超過幾百個(gè)Apriori和FP-Growth的耗時(shí)幾乎都是毫秒到秒級(jí)。這時(shí)候想用哪個(gè)就用哪個(gè)我通常會(huì)選擇Apriori做快速驗(yàn)證因?yàn)榇a邏輯更直白出了問題好查。數(shù)據(jù)量到了10萬到100萬條交易、商品種類幾千的時(shí)候我會(huì)毫不猶豫地選擇FP-Growth。內(nèi)存方面也要心里有數(shù)如果商品種類特別多、交易特別長(zhǎng)FP樹的條件模式基可能會(huì)占用大量?jī)?nèi)存必要的時(shí)候得調(diào)大JVM或Python進(jìn)程的可用內(nèi)存。數(shù)據(jù)量上到千萬級(jí)甚至更大單機(jī)已經(jīng)很難支撐此時(shí)要考慮分布式方案。業(yè)界成熟的工具比如Spark的FP-Growth實(shí)現(xiàn)也是基于同樣的樹結(jié)構(gòu)思路去分布式化的。這種情況下Apriori基本沒有登場(chǎng)機(jī)會(huì)。4.3 規(guī)則篩選與業(yè)務(wù)落地的常見坑算法跑完只是第一步真正的難點(diǎn)在于從一大堆規(guī)則中淘出有價(jià)值的那幾條。我在實(shí)踐中總結(jié)下來規(guī)則篩選至少要過三道關(guān)。第一道關(guān)是統(tǒng)計(jì)顯著性。提升度大于1只是說正面關(guān)聯(lián)比隨機(jī)共現(xiàn)高但樣本量過小時(shí)這個(gè)“高”可能完全來自波動(dòng)。所以要額外關(guān)注支持度絕對(duì)值——一條規(guī)則至少要有幾十個(gè)以上的訂單做支撐否則不可信。比如支持度0.001看起來很合理但如果總共10000個(gè)訂單那只有10個(gè)訂單在支撐噪聲風(fēng)險(xiǎn)極高。第二道關(guān)是業(yè)務(wù)合理性。很多挖掘出的強(qiáng)規(guī)則其實(shí)是冗余的。比如“牛奶→乳制品”這種規(guī)則完全沒有指導(dǎo)意義因?yàn)楹蠹乔凹某?。更麻煩的是“交叉持單”?guī)則比如“A平臺(tái)優(yōu)惠券→B平臺(tái)使用”這可能是同一用戶在兩個(gè)平臺(tái)之間的自然流轉(zhuǎn)而并非主導(dǎo)關(guān)系。規(guī)則落地前需要業(yè)務(wù)方一起判斷信息的可執(zhí)行性。第三道關(guān)是動(dòng)作閉環(huán)。一條規(guī)則要能變成業(yè)務(wù)策略必須能轉(zhuǎn)化成具體的觸發(fā)動(dòng)作。比如“買了嬰兒車的用戶有較高概率購(gòu)買嬰兒座椅”那么動(dòng)作就是在購(gòu)物車結(jié)算頁對(duì)購(gòu)買嬰兒車的用戶彈嬰兒座椅的推薦。如果規(guī)則本身沒法映射到一個(gè)清晰的觸點(diǎn)上挖掘得再準(zhǔn)也是廢的。4.4 一個(gè)完整的簡(jiǎn)單分析流程示例最后用一個(gè)完整的流程把這些串起來。假設(shè)手頭有一個(gè)電商訂單表字段是order_id和product_name任務(wù)是想找出“搭配購(gòu)買”的機(jī)會(huì)。第一步是數(shù)據(jù)預(yù)處理把明細(xì)表聚合成事務(wù)列表每一行為一個(gè)訂單包含的商品集合。這里有兩點(diǎn)要注意一是確認(rèn)是否需要做類別合并比如“牛奶”和“脫脂牛奶”是放在一起分析還是分開分析二是決定要不要對(duì)高銷量商品做降采樣否則它們會(huì)“霸屏”。第二步是調(diào)參跑模型。先用一個(gè)相對(duì)寬松的最小支持度比如0.005跑一輪看看項(xiàng)集分布再根據(jù)結(jié)果調(diào)。我一般會(huì)從支持度0.01、置信度0.5、提升度1.5起步然后根據(jù)規(guī)則數(shù)量調(diào)整。規(guī)則過多就收緊支持度過少就放寬。第三步是寫一個(gè)簡(jiǎn)單的挑選函數(shù)按業(yè)務(wù)邏輯過濾掉同質(zhì)規(guī)則。比如計(jì)算前后件是否有包含關(guān)系或者是否屬于同一個(gè)品類。第四步是把通過篩選的規(guī)則輸出成一張報(bào)告表格配兩三個(gè)典型的商品組合案例一起提交給業(yè)務(wù)方讓業(yè)務(wù)方判斷是否有進(jìn)一步做捆綁促銷或商品推薦的價(jià)值。這套流程我反復(fù)用過很多次從技術(shù)實(shí)現(xiàn)上并不復(fù)雜難點(diǎn)全在數(shù)據(jù)理解和業(yè)務(wù)判斷上。建議初次接觸關(guān)聯(lián)規(guī)則的朋友先拿一份真實(shí)的訂單流水完整跑一遍不要用太干凈的演示數(shù)據(jù)。數(shù)據(jù)一臟你就能深刻理解為什么要花那么多時(shí)間做預(yù)處理了。5. 寫在最后的幾條實(shí)際經(jīng)驗(yàn)回頭再看關(guān)聯(lián)規(guī)則這件事它之所以到現(xiàn)在還沒被深度學(xué)習(xí)那套東西沖擊掉原因其實(shí)很簡(jiǎn)單它是一種自帶解釋性的方法。輸出的是“前提→結(jié)論”形式的規(guī)則業(yè)務(wù)方一看就懂不像神經(jīng)網(wǎng)絡(luò)給出一堆特征權(quán)重讓人摸不著頭腦。我個(gè)人在實(shí)戰(zhàn)中還有幾條小經(jīng)驗(yàn)值得分享。第一最小支持度的選擇不要拍腦袋可以先統(tǒng)計(jì)一下單商品的支持度分布取中位數(shù)附近的值作為起調(diào)點(diǎn)。第二FP-Growth跑出來的結(jié)果如果條數(shù)太多優(yōu)先去檢查數(shù)據(jù)質(zhì)量而不是繼續(xù)壓縮閾值很多“強(qiáng)規(guī)則”其實(shí)是重復(fù)錄入、別名亂造或者刷單造成的假象。第三年度大促期間的數(shù)據(jù)和日常運(yùn)營(yíng)數(shù)據(jù)混在一起時(shí)最好按活動(dòng)周期分開挖掘因?yàn)榇蟠倨陂g的用戶行為模式和非大促期間差異極大混在一起容易得到不符合日常規(guī)律的規(guī)則。如果非要在Apriori和FP-樹頻集算法里選一個(gè)入門我會(huì)建議新手先把Apriori弄懂因?yàn)樗鼛湍憬ⅰ绊?xiàng)集—支持度—置信度—提升度”這個(gè)基本框架實(shí)際干活的時(shí)候再用FP-Growth去跑大數(shù)據(jù)。這個(gè)組合是最穩(wěn)妥的路徑既能理解原理又能解決問題。