1. 程式人生 > >從啤酒和尿布講關聯規則,大資料集處理演算法Apriori以及改進的PCY演算法

從啤酒和尿布講關聯規則,大資料集處理演算法Apriori以及改進的PCY演算法

本文將講解關聯規則的相關概念、處理相關規則的一般演算法、改進的大資料處理關聯規則的Apriori演算法以及進一步優化的PCY演算法。

啤酒和尿布的故事已經廣為人曉。很多年輕的父親買尿布的時候會順便為自己買一瓶啤酒。亞馬遜通過使用者購買資料,使用關聯規則,使用大資料的處理手段得出了尿布和啤酒的關係。

除了啤酒和尿布,現實生活中存在很多類似的關聯關係,一般歸納為:

A事件發生,B事件很可能也會發生。

注意這裡存在“很可能”這個概率問題。也就是說A事件發生,B事件是有可能不發生的。正如顧客買了尿布之後有可能不買啤酒。那就下來就是如何尋找這樣的關係,也就是怎麼知道尿布和啤酒存在這樣的關係。所有的處理和分析都是建立在大量的使用者購買資料的基礎之上。為了講解物品或者事物之間的關聯規則,需要認識三個概念。

下面是使用者的購買歷史記錄,只列出了5條。
1、 Bread, Coke, Milk
2、 Beer, Bread
3 、Beer, Coke, Diaper, Milk
4 、Beer, Bread, Diaper, Milk
5 、Coke, Diaper, Milk
、、、

如果總共有n條使用者購買記錄,每一條購買記錄中的物品都不會重複出現,m(item)表示某物品存在的次數,那麼:

1 支援度suport = m(item)/n;

可以理解為支援度就是某物品的出現次數佔所有記錄條數的比例。比例越高表示該物品被購買的次數越多。比如上面的歷史記錄中,n = 5, m(Beer) = 3, 所以Beer的支援度為3/5.

2 自信度confidence:A事件出現的次數為m1, A和B事件同時出現的次數為m2, 那麼A事件發生之後,B事件也有可能發生的自信度為:m2/m1。

如果自信度越高,表示兩者的關聯性越強。也就是A事件發生,B事件也很可能也會發生。

單單的找到不同物品或者事物之間的關聯度是不夠的,因為生活中太多的東西之間具有很強的關聯性。比如雞蛋和牛奶。也就是說很多東西的強關聯性已經是一種常識,我們並不需要挖掘就可以知道。我們希望找到一些“不為人知”的隱藏著的關聯關係,就像n年前的啤酒和尿布。所以,我們應該從中剔除掉那些“眾所周知”的強關聯關係。如何剔除呢?就是下面將要講到的概念。

興趣度interest:關聯規則有 A -> B, 表示A事件發生,B事件也發生的情況。它們的自信度為c, B事件的支援度為s,那麼B事件的興趣度為(c - s).

可以看出,B相對於A的自信度很高,表示兩者的關聯性很強,s很低,表示產品B在購物清單中不常見,也就是字形度和支援度相差越大,insterest的值就越大。A和B之間的關聯性很可能就是我們所感興趣的。如果A表示牛奶,B表示麵包,那麼他們的自信度c會很大,B的s也很大,所以(c-s)的值相差不大,因此就不會是我們所感興趣的了。

舉一個栗子:
B1 = {m, c, b} B2 = {m, p, j}
B3 = {m, b} B4 = {c, j}
B5 = {m, p, b} B6 = {m, c, b, j}
B7 = {c, b, j} B8 = {b, c}

設定兩個閾值:支援度閾值為s = 3, 自信度閾值為c = 0.75
那麼頻繁的item為 {b,m} {b,c} {c,m} {c,j} {m,c,b}
相關關聯規則下的自信度為:
b→m: c=4/6
b→c: c=5/6
m→b: c=4/5
b→c,m: c=3/6
、、、
從而可以從中剔除不符合條件的item。

現在我們已經知道如何通過計算事物的興趣度來確定某些事物的關聯性。計算機所要做的工作是遍歷和統計,同時還需要對不同的項進行組合,組合之後再遍歷和統計,最後得到不同的項在各種組合下的自信度和支援度。對於組合問題,如果兩兩一組,n個物品就會有n*(n-1)/2種組合。如果n等於10億,每一項用一個整數表示,那麼組合之後需要的記憶體為1.8* 10^9 Gb,單臺計算機是無法容納的。
這裡寫圖片描述
上面這張圖片表示的是不同項之間的組合。

下面有兩種最原始的方法,用來計算和儲存兩個不同項之間同時出現的次數。
這裡寫圖片描述
第一種是使用二維陣列表示,其中橫座標和縱座標一樣,因為是對稱的,所以可以只取上三角或者下三角。

第二種方法是三欄位組合的方式,(item1, item2, count),這樣的話當兩個項之間的count為0的時候就可以不用表示出來。一個三欄位組合所需要的空間為12byte(使用整型)。

第二種方法比第一種方法的優越性在於當不同項之間的關聯性很少時,也就是很多項之間同時出現的數量為0時,使用第二種方法就無需申請過多的空間來儲存他們之間同時出現的次數。如果項之間關聯性很強,那麼第一種方法就比第二種方法好一點,因為它儲存一個統計數只需要4byte的空間。

上面兩種方法都存在單臺機器記憶體不夠用的問題。下面是改進的演算法。

Apriori演算法

Apriori演算法的思想很簡單,那就是,如果某個項不是頻繁項,那麼所有包含它的項都不是頻繁項。什麼意思?舉一個栗子:比如{A}, {A, B}, 如果{A}的支援度為0.4, 那麼{A, B}的支援度不可能大於0.4.所以,如果A不是頻繁項,那麼{A,B}這兩個元素所構成的項也不是頻繁項。
這裡寫圖片描述
如上面圖片所示,如果項AB不是頻繁項,那麼所有包含它的項都不是頻繁項,如上圖紅色虛線所示。
這裡寫圖片描述
相反,只有上一級是頻繁項的情況下,下一級的項才有可能是頻繁項。如上面圖片虛線部分所示。

Apriori演算法根據這一點,避免計算所有包含非頻繁項的項集。所有的工作只是在子集為頻繁項中進行。

下面是Apriori演算法的具體虛擬碼:
假設有一個購物清單,分別是{A, B, C}, 那麼k = 1表示只有一個商品的項,也叫項的大小為1(個人定義的,目的是便於說明),k=2表示兩兩組合的項,也叫項的大小為2,依次類推。其中Ck表示項的大小為k的待選項,Lk表示項的大小為k的頻繁項。需要注意,此處的待選項中有可能不是頻繁項。

1. k = 1, C1 = 所有的項
2. While C1 不為空
    3. 掃描找到所有大小為Ck的項,如果Ck是頻繁項,就將它加入Lk中。
    4. 使用Lk中的項產生新的待選項Ck+1。
5. k = k + 1;

這裡寫圖片描述

詳細的計算過程如上圖所示。當前的所有項都是上一步的頻繁項的組合。

相比於前面的普通演算法,Apriori演算法能夠根據前面的計算大量減少了記憶體空間的消耗。但是它不是很完善的。如果當k = 1的時候,如上圖所示的C1,如果此步驟所產生的頻繁項集C1很多,假設為m個,那麼當k = 2的時候的C2的個數會是m(m-1)/2個。很可能會撐爆記憶體。
這裡寫圖片描述

上面圖片指的是使用Apriori演算法計算的時候項的個數。開始的時候,也就是k = 1的時候項的個數為820, 到第二步也就是k = 2的時候項的個數約等於820*(820-1)/2。當k>2的時候,項的個數就很少。所以,我們應該研究,尋找一種方法,避免第二步可能撐爆記憶體的問題。這也是接下來將要講到的PCV演算法。

PCV演算法

PCV演算法是三個人名字的第一個字母。它是在Apriori演算法的基礎之上進行的。它使用到了bitmap。為了順利講解,先講一講什麼是bitmap。

這裡寫圖片描述
在計算機中數字是以二進位制的形式儲存和運算的。在32為系統中,一個整型數字可以儲存成32位的二進位制數,如下圖所示:
這裡寫圖片描述

上面圖片所示的二進位制數轉換為十進位制數為3. 每一個位置的數只能是0或者1。一個整型的二進位制長度為32。bitmap這種資料結構利用了計算機這樣的資料儲存形式。具體怎麼做呢?如果一個bitmap的長度為32,也就是上圖的長度。下標表示具體的數字,對應下標的值,0表示不存在,1表示存在.

這裡寫圖片描述

上圖中下標為31、30和28所對應的值都為1,所以該bitmap表示整數31、30和28是存在的,0到27和29這些整數都不存在。

這裡寫圖片描述

如果bitmap的長度超過32呢,比如需要表示36個數,那麼就使用兩個整型的空間。如上圖所示,數字32在第二個整數的最右邊被表示出來了。

總結一下:bitmap中,陣列的下標表示的是具體的數字,該下標下所對應的值為0或者1,0表示不存在,1表示存在。

下面簡單講一下hash函式。
這裡寫圖片描述

簡單地理解雜湊函式就是輸入不同的雜湊文字,能夠得到等長的字串或者數字。一般情況下,不同的輸入所對應的輸出是不一樣的,但是也不是百分之百。其中(y = x mod n)就是最簡單的雜湊函式。

把雜湊函式應用到本文所講的PCY演算法中,就是不同的項經過雜湊計算會得到一個整數,這個整數就是bitmap的下標(index)值。然後把該下標所對應的值由0變成1。但是,不同的項通過雜湊演算法所得到的下標可能是一樣的,這叫bitmap的衝突,如果出現這種情況,就無法辨別出到底是哪個項改變bitmap的了。pcy演算法考慮到了這個因素。除此之外,我們還需要計數的變數,計數的變數個數等於bitmap的長度,也就是需要統計每一個bitmap位變為1出現的次數。即使存在衝突的情況,也照樣讓計數變數加1.

比如『A,B,C』三個項,如果A和B項通過雜湊函式的下標都為1,那麼統計的數就是2。
所以,bitmap有多長就需要多少個統計變數。如果原始購買清單中有2.5億個項,那麼我們來計算一下存放這麼多個統計變數和bitmap需要多大的記憶體空間:

一個只有2Gb的空間完全可以存放2.5億個整數以及長度為2.5億d的bitmap。因為:
一個整型4位元組,那麼1G的記憶體可以存放2.5x10^8個整數,也就是2.5億一個整數,具體計算為:

1Gb = 1000Mb = 1000 000Kb = 1000 000 000 Byte = 4 * 2.5x10^8(2.5x10^8個整數)

一個長度為2.5億的bitmap,只需要記憶體3.125Mb,具體計算為:

250 000 000 bit = 250 000 000/8 byte = 3.125 x 10^4 Kb = 31.25 Mb.

可以看出,bitmap所需要的記憶體空間是很少很少的。

所以,PCY思路:
1. 當k = 1的時候,也就是每一個項中只有一個物品,有10億個項,表示為C1。統計出10億個項的個數,排除掉非頻繁的項。
2. 使用巢狀的兩個for迴圈,遍歷第1步所得到的頻繁項,兩兩組合,得到C2。分別把C2用過雜湊函式得到bitmap的下標,把該下標下的bitmap的值置為1,同時計數。這裡把每一個下標表示為一個桶。
3. 過濾掉個數小於閾值的桶,得到的便是k = 2的時候的頻繁項。
4. 重複進行下去,直到上一步結果中的頻繁項為0.
這裡寫圖片描述
從上面的演算法可以看出,需要儲存的只是bitmap和統計的變數。所以,PCY方法就不存在記憶體撐爆的問題。

這裡寫圖片描述
上面是Apriori演算法和PCY演算法的運算對比圖。注意到紅色圈出來的地方,在C2 的時候,使用PCY演算法不需要儲存那麼多個項。所以,在計算時間上面就比Apriori演算法有優勢。

再次改進

我們發現,PCY演算法中的雜湊函式的結果是存在有衝突問題的。也就是說如果三個不同的項通過雜湊函式都對映到了相同的bitmap下標上。他們總的統計數不小於閾值,那麼PCY演算法就把他們都當成了頻繁項進入下一輪的計算環節。而事實上,他們三者中可能不全都是頻繁項。我們希望能夠改進這一點。

在PCY演算法中我們引進了一個bitmap和相應的統計變數,同時雜湊函式表示為hash1. 相同的,我們也可以使用兩個bitmap以及他們對應的統計變數,但是,雜湊函式不相同。

遍歷所有的項,分別把他們雜湊到不同的bitmap上面。最後的頻繁項便是在兩個bitmap中的統計數都大於閾值的項。

這裡的關鍵點是兩個雜湊函式是不一樣的,那麼相同的輸入通過不同的雜湊函式所得到的結果基本上是不一樣的,也就是bitmap的下標不一樣。

完結! 轉載請標明出處,感謝!

參考論文:

R. Agrawal, R. Srikant: “Fast Algorithms for Mining Association Rules”, Proc. of the 20th Int’l Conference on Very Large Databases , 1994.

Rakesh Agrawal, Tomasz Imielinski, Arun N. Swami:Mining Association Rules between Sets of Items in Large Databases. SIGMOD Conference 1993: 207-216

參考書籍:
http://www.mmds.org/