FP-Growth算法理解

第一次接觸FP-Growth是在《數據挖掘概念與技術》,當時對它的理解只停留在概念層面。後來又在《機器學習實戰》中接觸到了它,結合着書中的講解和代碼,跑了點結果,理解加深了一點。最近,工作中需要使用到它,又重新撿起,開始精讀和思考,發現收穫很大。

FP-Growth(Frequent Pattern Growth, 頻繁模式增長),它比Apriori算法效率更高,在整個算法執行過程中,只需要遍歷數據集2次,就可完成頻繁模式的發現。

大白話描述FP-Growth:

1 輸入

1.1 數據集dataset:假設有N條事務,每個事務是包含元素個數不定的集合,如

dataset=[['啤酒','牛奶','可樂'],
         ['尿不溼','啤酒','牛奶','橙汁'],
         ['啤酒','尿不溼'],
         ['啤酒','可樂','尿不溼']]

1.2 支持度sup

2 計算過程:

2.1 創建FP-tree

對於輸入的dataset,統計所有事項中各元素的出現頻次,即每個1項集的頻數,並將各元素按照頻數降序排序,刪除那些出現頻數少於設定支持度sup的元素,形成列表L,留下來的元素就構成了頻繁1項集。(這是對數據集的第一遍掃描)

對數據集中每個事務的元素按照列表L排序(按支持度降序排列),開始構造FP-tree。樹的根節點爲空,每個事務中的所有元素形成一條從根節點到葉子結點的路徑。若幾個事務的元素按列表L排序後,具有相同的前m個元素,則它們在FP-tree中共享前m個元素代表的節點。樹中每個節點的計數爲路徑經過該節點的事務集的個數。(這是對數據集的第二遍掃描)

在創建FP-tree的同時,headTable也就創建好了,headTable可以理解爲一個具有三列的表。第一列爲元素(項ID),第二列爲支持度計數,第三列爲節點鏈。如下所示

項ID 支持度計數 節點鏈
啤酒 4 nodelink1
尿不溼 3 nodelink2
牛奶 2 nodelinkn

headTable中的項也是按照支持度計數從大到小排列的。節點鏈則鏈接到FP-tree中這一項所在的各個葉子結點上,後面頻繁項集的發現就是靠的這些節點鏈。

2.2 尋找FP

從headTable中的最後一行開始,依次往上取項,比如最開始我們取‘牛奶’。尋找‘牛奶’的所有前綴路徑,這些前綴路徑形成‘牛奶’的CPB(Condition Pattern Base,條件模式基),而這些CPB又形成新的事務數據庫。將‘牛奶’這一項添加到我們的集合中,此時,‘牛奶’這個頻繁1-項集的支持度就是headTable中的支持度計數。然後用‘牛奶’的CPB形成的事務數據構造FP-tree,如2.1所述,構造的過程中將不滿足支持度的項刪除,而滿足支持度的項又會構成另外一個FP-tree和headTable,我們記爲FP-tree1和headTable1。同樣的從headTable1的最後一行開始,比如是可樂,那麼把‘可樂’和之前的‘牛奶’就形成了頻繁2-項集,此時,{‘牛奶’,‘可樂’}這個頻繁2-項集的支持度就是headTable1中‘可樂’的支持度。同時,我們也要尋找‘可樂’的前綴路徑形成的CPB構成的又一個事務數據集,仍舊是構造FP-tree,從headTable取元素項,將元素項加集合。。。

不知大家發現沒,FP的發現過程就是一個循環裏不斷遞歸的操作。循環,循環的是headTable中的各個元素項;遞歸,遞歸的是元素項的CPB構成的事務數據集形成的FP-tree中發現FP。

實踐出真知

起初,我用《機器學習實戰》中的代碼,稍加修改,得到了頻繁項集和關聯規則,但由於數據量大,程序跑起來有點慢,就往Spark上移植(Spark的MLlib庫中有現成的FP-Growth接口)。就在我移植完,跑了一遍後,尷尬的事情發生了,兩種實現跑出來的結果不一樣,而且差別還挺大的!經過自己確認,兩種實現輸入都是一樣的,Spark上直接調用的接口,輸入格式也是它要求的格式。於是就把目光轉到了FP-Growth算法原理和《機器學習實戰》中的代碼實現上。

在網上看了很多博主對FP-Growth算法的解讀,最後看到這篇時,http://www.cnblogs.com/qwertWZ/p/4510857.html  發現了一些端倪。

這篇博文最後提到,《機器學習實戰》中的createInitSet函數在統計事務時,簡單的將頻數都置爲1,如原函數所示。對於沒有重複事務的數據集,這種方法並沒有什麼問題,但是當數據集中存在相同事務時,這種頻數置1的做法就會帶來錯誤,正確的做法是應當對相同的事務頻數累加起來,如修改後的函數所示。

原函數:

def createInitSet(dataSet):
    retDict = {}
    for trans in dataSet:
        retDict[frozenset(trans)] = 1#如有相同事項,則計數出錯
    return retDict
修改後的函數:

def createInitSet(dataSet):
    retDict = {}
    for trans in dataSet:
        retDict[frozenset(trans)] = retDict.get([frozenset(trans)], 0) + 1#若沒有相同事項,則爲1;若有相同事項,則加1
    return retDict

將這一行代碼修改後,再運行,兩種實現的結果就一樣了。

完整的fp-growth及關聯規則代碼在這裏

FP-Growth的python包

由於我之前用sklearn比較多,但要實現頻繁項集挖掘時,發現sklearn並沒有包含apriori和fp-growth這類頻繁項集發現算法,於是就直接借用《機器學習實戰》裏的代碼了。後來在解決問題的過程中發現有一個名叫fp_growth包,安裝後,發現調用非常簡單。

import fp_growth as fpg
frequent_itemsets = fpg.find_frequent_itemsets(transactions, minimum_support, include_support=False)
其中,transactions是我們的輸入,格式可以是list of list;minimum_support是算法的最小支持度,這個是小數表示的;include_support設置爲True,則結果中包括頻繁項集的支持度,否則不包括;frequent_itemsets就是我們要獲得的頻繁項集。



發佈了25 篇原創文章 · 獲贊 14 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章