頻繁項集挖掘之apriori和fp growth

2021-08-20 12:58:37 字數 4639 閱讀 4320

apriori和fp-growth是頻繁項集(frequent itemset mining)挖掘中的兩個經典演算法,主要的區別在於乙個是廣度優先的方式,另乙個是深度優先的方式,後一種是基於前一種效率較低的背景下提出來的,雖然都是十幾年前的,但是理解這兩個演算法對資料探勘和學習演算法都有很大好處。在理解這兩個演算法之前,應該先了解頻繁項集挖掘是做什麼用的。

頻繁項集挖掘是關聯規則挖掘中的首要的子任務。關聯規則挖掘是要找出乙個資料集上,滿足一定條件的項集。這些項的集合能構成 形如蘊含式"a=>b"這樣的「規則」。這個"=>"符號是通過一些條件來定義的,如果沒有條件那當然所有的項組合都能形成這樣的關係。頻繁程度就是一種要求,也就是ab共同次數出現了超過閾值。 a和b能否形成"=>"規則, 就要根據定義來算,那首先就得把a、b需要的條件挖出來,也就是頻繁項集挖掘要做的。

關聯規則定義比較容易能搜到,頻繁項集挖掘簡單的說就是:給定乙個項列表list = ,乙個資料集d的每條記錄都是list 的子集,要找出資料集中頻繁共同出現次數超過閾值t,也就是支援度,的所有組合。

這個挖掘其實不好做,因為結果可能是list 中所有項的組合,有2^|list|個可能,搜尋空間是個組合**的空間。看下圖,先別看紅字紅線:

要弄好這件事不僅需要有效減小搜尋空間,而且對每個可能的搜尋都必須快速完成。所以頻繁項集挖掘在演算法實踐和編碼實現上就要有非常強的技巧。我們就來深入學習apriori和fp-growth中的搜尋方式和技巧。這兩個演算法很容易找到完整的步驟,這裡會更注重裡面一些精彩之處,但是可能書寫不會那麼規範,建議和完整演算法對照來讀。

先來看看apriori。apriori採用廣度優先的搜尋方式,縮小搜尋空間用到了乙個稱為apriori的性質。apriori性質是這麼說的:頻繁項集的所有非空子集必然也是頻繁的。這是很顯然的,比如 同時包含項ab的記錄條數肯定比只包含a的記錄少。這條性質反過來也可以這麼說:如果乙個項集是非頻繁的,那麼它的超集必然也是非頻繁的。

演算法過程如下:

輸入:資料集d,支援度minsup

輸出:滿足支援度的所有項集的集合l

l1 = 

發現1-項集

(d);

for (k=2;lk-1 ≠φ ;k++) 

l = l 

∪ lk

}return l

其中演算法精華在於 連線剪枝生成(lk-1, minsup) 這一步, 包含連線步和剪枝步兩個動作:

1. 連線步:長度k-1的項連線成長度k的項;具體就是對兩個k-1長的項l1和l2,必須滿足前k-2個項都相同才能連線,最後把l1和l2剩下的最後一項加起來,形成k的長度的項。

2. 剪枝步:k項連線完成後,檢查其所有k-1子集是否是頻繁的,如果是,才保留作為候選項。

可以通過一張截圖來演示一下apriori的過程:

對應第一張圖,連線步是從第k層的項集,向下擴充套件一層的候選項集,剪枝步能夠通過apriori性質過濾掉那些肯定非頻繁的項集。

apriori的框架其實很小,但是可以想象得到主要的兩個步驟: 連線+剪枝(也就是候選集生成),以及,候選集統計是很耗費時間的。

剪枝步也需要對每個k-候選項集的k-1子集都進行一次檢測,也很耗費時間;統計頻繁次數是必須的,因此需要掃瞄資料庫,經歷i/o。那麼有必要剪枝,直接統計會不會更好呢,雖然沒有試驗過,但我估計還是剪枝以後減少候選集的統計更划算。而這兩個耗時的步驟在實現上如果能使用到技巧,對演算法時間影響最直接。比如剪枝步中k-1候選項集需要逐一向已有的k-1頻繁項集查詢,這用什麼資料結構最好?又如掃瞄資料庫的時候是否能過進行一些壓縮,相同的記錄進行合併減少遍歷次數,以及過濾掉對統計沒用的記錄?

面對apriori的問題,感覺fp-growth突然間就冒出來了,它是乙個挖掘方式和apriori完全不一樣的演算法,直接看可能不那麼像apriori直觀,因為演算法一開始就介紹了它採用的資料結構和挖掘方式。所以我們先對比下apriori和fp-growth的差異在哪,再介紹它的演算法。

簡單的說apriori是先產生一批候選項集,再通過原資料集去過濾非頻繁項集:先找a、b、c,檢查一下通過了,再找ab、ac、ab,檢查又通過了,再到abc... 這樣的廣度優先的方式。而fp-growth是先從資料集中找頻繁的項,再從包含這個頻繁項的子資料集中找其他的頻繁項,把它們倆連起來也肯定是頻繁的:先找a,再在找包含a的子資料庫裡,找到b,就得到ab是頻繁,再再包含ab的子資料庫裡,找到c,abc就是頻繁了。

在了解了fp-growth的大致思路以後,我們就能介紹它採用的資料結構和演算法了。

首先fp-growth採用了乙個叫fp-tree 的資料結構去壓縮儲存資料集,放到記憶體裡,這樣以後過問原資料集的事就不必經過io了。

fp-tree主要是一種字首樹,和字典樹(trie)接近,並且節點把項的次數也記錄下了。字元的順序有所不同,字典樹用的是字母表順序,fp-tree (frequent pattern tree)用的是字母表的頻率降序,這樣的好處是出現次數多的項作為共享字首的概率也大,fp-tree的壓縮率就高(後面還會提到),根據apriori性質,非頻繁的項沒用,因此fp-tree上可以沒有它們。

根據前面提到fp-growth步驟,需要找資料庫上包含某個項的子資料庫,不能從樹根開始搜尋,因此為了方便,需要把fp-tree中所有枝幹、葉子上相同的項全串一起,這樣項從乙個起點開始,向樹根遍歷,就能得到包含這個項的子資料庫了。這些起點和串起相同節點的鏈就是fp-tree的另乙個部分:頭表和兄弟鏈。頭表包含樹上所有的單項,並是兄弟鏈的起點,那麼fp-tree不僅完整記錄了資料庫裡所需的資訊,還能找到對任一項找到包含了它的子資料庫。

有了fp-tree,挖掘頻繁項集就變得直觀了。首先是壓縮資料庫,過濾非頻繁項,得到一棵fp-tree 1號,對於乙個項,比如a,通過兄弟鏈,遍歷樹找出 包含a的子樹(庫),又稱a的條件模式樹(庫),英文原文叫condition pattern tree(base)。然後把這個子庫當做乙個新的資料庫2號,過濾2號庫非頻繁項,建立乙個小點的fp-tree 2號,那麼那個a與這個2號樹里的所有項,連起來肯定也是頻繁的;比如有b,同理把b的條件樹找出,也建立個fp-tree 3號,就能得到ab和3號樹上的項連起來也肯定是頻繁的。這個過程遞迴完成,建立不出條件子樹遞迴就跳出去。

演算法包含兩個部分:

1. 是建立fp-tree:掃瞄一遍資料庫,得到每個項的支援度,排序並過濾非頻繁項;再掃瞄一次資料庫,每條記錄按順序排序,新增到fp-tree上。

2. 呼叫演算法fp_growth(fp-tree, null)。

function fp_growth(fp-tree, a)else}}

fp_growth是個遞迴演算法,期間需要反覆遍歷樹和構建fp-tree。fp-growth中判斷單路徑部分可以不要,最後實際結果其實是和下面部分是一樣的,但是直接計算單路徑產生所有組合會便捷很多。另外一點,fp-tree要按支援度降序的順序的好處有幾點?前面說了可以提高共享字首的可能,提高壓縮率,樹小了,遍歷的步數還能減小,尋找最優壓縮的順序是個np難問題,因此選這個辦法能有個比較好的壓縮率足夠了。

fp-growth雖然號稱不產生候選集,但是實際上候選集產生已經在尋找條件子庫的時候隱隱產生了,剪掉非頻繁候選項的時候是通過建樹步驟中的第一小步完成的。

fp-growth在實現上也可以有很多技巧,比如尋找條件子樹的時候,同一條路徑會被遍歷很多次,如何有效避免(後來han自己提出,遇到掃把型的fp-tree,即上面是單路徑、下面分叉的,可以把單路徑所有的組合分別連線到下面的部分挖掘結果上輸出,那就不用遍歷上面的單路徑了) 另外樹上節點用什麼資料結構儲存指向子孫節點的指標,能同時兼顧查詢時間和空間?

最後我們總結一下apriori和fp-growth之間的聯絡和差異。

初讀fp-growth演算法,估計都感覺不到它和apriori有什麼關係,但我個人猜測fp-growth是從apriori的統計候選集太耗時的那裡 改良開始的,希望實現候選項集的更快速的計算支援度,最後就徹底的改變的搜尋頻繁項集的方式。我覺得兩個演算法的最根本的差異在於apriori是以搜尋項集組合的空間作為基礎,通過資料庫來對照。而fp-growth是以資料庫為基礎,在裡面尋找項集是否頻繁,表現為搜尋方式乙個是廣度優先乙個是深度優先。

。apriori的那剪枝步和統計支援度在fp-growth上就是不斷的建fp-tree和遍歷。而前者的統計需要經過的io,後者已經壓縮到記憶體了;但fp-growth不是在所有資料集上都比apriori強,比如在稀疏的資料集上,fp-tree每個節點可能包含非常多子孫,因此儲存子孫節點的指標也是很大開銷,fp-tree本來就是通過壓縮使得資料集能被記憶體容納,結果導致最後fp-tree起不到壓縮效果適得其反。優化實現的apriori在稀疏資料集上也往往比fp-growth要快。

這裡fp-growth在大部分地方是完勝了apriori,後面很多改進都是基於深度優先的思想,並且更注重實現上的技巧。現在我們也沒必要去費太多精力去改進這兩個演算法了,因為頻繁項集挖掘是個組合**的時間複雜度。在2003 2023年icdm舉辦過兩個workshop就是專門比誰實現的頻繁項集挖掘最好(搜"fimi 03",**裡有很多的原始碼)。在這裡想多提一點,資料探勘中,沒有演算法能在所有資料集上pk掉其他演算法。因此我們應該了解一種任務的多種演算法,看看它們為什麼和如何在不同的資料集上體現出自己的優勢,這樣,通過比較我們不僅能更好的理解和掌握它們的精華,更能在當我們遇到新的資料集的時候,選取合適演算法甚至做出針對性的優化措施。

關聯規則(頻繁項集) Apriori

1.該問題最初是對 購物籃 提出來的,著名例子是 尿布與啤酒 2.相關概念 關聯規則的支援度 support a,b 包含a和b的事務數 事務總數 關聯規則的置信度 confidence a,b 包含a和b的事務數 包含a事務數 頻繁項集 項集的頻率大於等於最小支援度。強相關規則 同時滿足最小支援度...

Spark 頻繁項集挖掘

同步於buracag的部落格 挖掘頻繁專案,專案集,子串行或其他子結構通常是分析大規模資料集的第一步,這是資料探勘多年來一直活躍的研究課題。可以參考一下維基百科中關於關聯規則學習的基礎知識。fp growth演算法在han等人的文章中描述,挖掘頻繁模式而沒有候選生成,其中 fp 代表頻繁模式。給定資...

頻繁模式挖掘 Apriori演算法

dm實驗,寫的比較二,好多情況還沒有考慮,後續有時間會修改。開始的時候資料結構沒設計好導致寫到後面費了很大的勁 不過還好python的列表有起死回生的功效 資料集 database.txt i1,i2,i5 i2,i4 i2,i3 i1,i2,i4 i1,i3 i2,i3 i1,i3 i1,i2,i...