計算機如何感知大資料——聚類演算法
看看下面這張圖片。這是一個不同形狀大小的昆蟲的集合。花點時間按照相似程度將它們分成幾組。
這不是什麼很有技巧性的問題。 我們從把蜘蛛分到一起開始。
圖片來自Google圖片搜尋,標記以便重用
做完了嗎?雖然這裡沒有必要有所謂的正確答案,不過你極有可能將這些蟲子分成四組。蜘蛛分成一組,蝸牛一組,蝴蝶和蛾子一族,黃蜂和蜜蜂總共三個一組。
不算太糟糕,是吧?如果蟲子數量是這個的兩倍你可能還是會這麼分,對吧?如果你有空餘的時間——或者對昆蟲學的熱情——你甚至可能用相同方法對一百年個蟲子這樣分類。
然而對一個機器來說,將十個目標分成多個有意義的群組是個不小的任務,多虧了數學中一個令人費解的分支組合數學
如果對一百個蟲子分類——方案數將是宇宙中粒子的數量的很多倍。多少倍呢?通過我的計算,大概是5*10的35次方倍。事實上,有四千萬億googol多種方案(什麼是googol?)。僅僅是一百個目標。
而這些方案几乎都是沒有意義的——雖然從這些數量異常龐大的可能的選擇中,你可以很快地找到為數不多的對蟲子分類的有用的方式。
我們人類理所當然的可以很快地對大量資料理解並分類。無論是一段文字,或者螢幕上的影象,或是一系列的目標——人類通常可以相當有效地搞清世界給我們的任何資料。
鑑於發展人工智慧和機器學習的一個關鍵方面就是讓機器迅速理解龐大的輸入資料集,那麼有什麼捷徑嗎?在這本文中,你將會看到三個聚類演算法,機器可以這些演算法快速理解大型資料集。這決不是一個詳盡的清單——還有很多其他的演算法——但這三個演算法代表了一個良好的開端!
當你想要使用它們的時候,你會得到對於每一個問題的總結,一個總體的功能介紹以及一個更細緻的,一步一步實現的例子。我相信在現實中實現對於理解是非常有幫助的。如果你真的很喜歡這些,你會發現最好的學習方式是用筆和紙。加油去吧——沒人評判哪種方式更好!
三個整齊的聚類,K=3
K-均值聚類
適用場景:
預先已經知道要分成多少組。
如何工作:
演算法首先將每一個觀測點隨機地分配到K個類別中的一個,然後計算每一個類別的均值。下一步,在重新計算均值之前演算法將每一個點重新分配給均值與該點資料最接近的類別。不斷重複這一步直到不需要再重新分配。
實現例子:
考慮有12個足球運動員的一個足球隊,這些運動員每個人在本賽季都獲得了一定的進球數(3-30之間)。現在我們來將他們分成三個單獨的聚類。
第一步需要我們隨機地將這些運動員分成三組並計算每組的均值。
1234567891011 | Group1PlayerA(5goals),PlayerB(20goals),PlayerC(11goals)Group Mean=(5+20+11)/3=12Group2PlayerD(5goals),PlayerE(3goals),PlayerF(19goals)Group Mean=9Group3PlayerG(30goals),PlayerH(3goals),PlayerI(15goals)Group Mean=16 |
第二步:對每一個運動員,將它們重新分配到均值離他們最近的組。即,運動員A(進球數5)被分配到第2個組(均值=9)。然後重新計算這幾組的均值。
1234567891011 | Group1(Old Mean=12)PlayerC(11goals)NewMean=11Group2(Old Mean=9)PlayerA(5goals),PlayerD(5goals),PlayerE(3goals),PlayerH(3goals)NewMean=4Group3(Old Mean=16)PlayerG(30goals),PlayerI(15goals),PlayerB(20goals),PlayerF(19goals)NewMean=21 |
不斷重複第二步直到所有組的均值不再變化。對於這個有些不太好的例子,下一次重複就會達到終止條件。停止!現在你已經形成三個聚類的資料集!
1234567891011 | Group1(Old Mean=11)PlayerC(11goals),PlayerI(15goals)FinalMean=13Group2(Old Mean=4)PlayerA(5goals),PlayerD(5goals),PlayerE(3goals),PlayerH(3goals)FinalMean=4Group3(Old Mean=21)PlayerG(30goals),PlayerB(20goals),PlayerF(19goals)FinalMean=23 |
在這個例子中,聚類可能對應於球員在場上的位置——如後衛、中場和前鋒。k-均值在這裡使用因為我們合理地預期資料可以分成這三類。
通過這種方式,給出一系列效能統計資料,機器可以合理地評估任何團體運動中運動員的位置——這對運動分析很有用,對任何其他的將資料集按照預定義的組別進行分類也可以提供相關的見解。
細節:
這個演算法有一些變體。對聚類進行“seeding”的初始化方法有好幾種方式。在這裡,我們將每個球員隨機分配到一個小組,然後計算出這些組的均值。這會導致初始生成的組之間的均值很接近,致使後面有更多重複步驟。
另一種方法是初始化每個聚類時只分配一個球員,然後將其它球員分配到最近的聚類。這樣返回的聚類比初始化seeding得到的聚類更有意義,降低了變化很大的資料集中的重複性。但是,這種方法可能會減少完成演算法所需的迭代次數,因為這些組將花費較少的時間完成收斂。
K-均值聚類的一個明顯的缺點是你必須提供關於你期望找到多少個聚類的先驗假設。 有一些方法可以評估一組特定的聚類結果。 例如,聚類內平方和(Within-Cluster Sum-of-Squares)是每個聚類內方差的度量。 聚類效果越好,整體WCSS越低。
層次聚類
什麼時候使用:
…你希望發掘出你的觀測資料之間的潛在關係。
如何工作:
計算距離矩陣,其中矩陣元素(i,j)的值是觀察值i和j之間的距離度量值。 然後,將最接近的兩個觀察值進行配對並計算其平均值。通過將配對的觀測值合併成一個單一的物件,形成一個新的距離矩陣。 從這個距離矩陣中,配對最近的兩個觀察值並計算它們的平均值。 重複這個過程,直到所有的觀察結果合併在一起。
例項:
這是一個關於鯨魚和海豚物種選擇的超簡化資料集。作為一名訓練有素的生物學家,我可以向您保證,我們通常會使用更多更詳細的資料集來重建系統發生樹。 現在我們來看看這六種物種的典型體長。 我們將只使用兩個重複的步驟。
1234567 | Species Initials Length(m)Bottlenose Dolphin BD3.0Risso'sDolphin RD3.6Pilot Whale PW6.5Killer Whale KW7.5Humpback Whale HW15.0Fin Whale FW20.0 |
步驟1:計算每個物種之間的距離矩陣。在這裡,我們將使用歐式距離——資料點之間的距離。你可以計算出距離就像在道路圖上檢視距離圖一樣。可以通過讀取相關行和列的交點處的值來查詢任何物種對之間的長度差異。
123456 | BD RD PW KW HWRD0.6PW3.52.9KW4.53.91.0HW12.011.48.57.5FW17.016.413.512.55.0 |
步驟2:配對兩個最接近的物種。在這裡,兩個最接近的物種是寬吻海豚和灰海豚,平均長度為3.3米。
重複步驟1重新計算距離矩陣,但是這次將寬吻海豚和灰海豚合併成長度為3.3m的單個物件。
12345 | [BD,RD]PW KW HWPW3.2KW4.21.0HW11.78.57.5FW16.713.512.55.0 |
接下來,用這個新距離矩陣重複步驟2。現在,距離最小的是領航鯨與逆戟鯨,所以我們計算他們的平均——7.0米。
然後,我們重複第1步——重新計算距離矩陣,但是現在我們已經將領航鯨與逆戟鯨合併成了一個長度為7.0米的單個物件。
1234 | [BD,RD][PW,KW]HW[PW,KW]3.7HW11.78.0FW16.713.05.0 |
接下來,我們用這個距離矩陣重複步驟2。最小距離是兩個合併物件之間的(3.7米)——現在我們將它們合併成一個更大的物件,並取平均值(即5.2米)。
然後,我們重複步驟1並計算一個新的距離矩陣,合併了寬吻海豚和灰海豚與領航鯨與逆戟鯨。
123 | [[BD,RD],[PW,KW]]HWHW9.8FW14.85.0 |
接下來,我們重複步驟2。最小的距離(5.0米)在座頭鯨與長鬚鯨之間,所以我們將它們合併成一個單一的物件,取平均值(17.5米)。
然後,返回步驟1——計算距離矩陣,座頭鯨與長鬚鯨已經合併。
12 | [[BD,RD],[PW,KW]][HW,FW]12.3 |
最後,我們重複步驟2——在這個矩陣中只有一個距離(12.3米),我們把所有東西都放在一個大物件中,現在我們可以停下來了!我們來看看最後的合併物件:
1 | [[[BD,RD],[PW,KW]],[HW,FW]] |
它有一個巢狀的結構(可以認為是JSON),可以把它繪製成一個樹狀的圖形或者樹形圖。 它讀取的方式與家譜相同。 兩個觀察結果在樹上離得越近,就被認為是更相似或更密切相關的。
樹狀圖的結構使我們能夠深入瞭解資料集的結構。在我們的例子中,我們看到兩個主要分支,一個分支是 HW 和 FW,另一個是 BD、RD、PW、KW。
在進化生物學中,具有更多樣本和測量的更大的資料集以這種方式被用來推斷它們之間的分類關係。 除了生物學之外,層次聚類在資料探勘和機器學習環境中也有應用。
很酷的是,這種方法不需要假設你正在尋找的聚類數量。 您可以通過在給定高度“切割”樹來將返回的樹形圖劃分為聚類。 這個高度可以通過多種方式進行選擇,具體取決於你希望對資料進行聚類的解析度。
例如,從上面的樹狀圖可以看出,如果我們在高度為10的情況下繪製一條水平線,我們就會將兩條主要分支分割,將樹狀圖分成兩個子圖。 如果我們在高度= 2切割,樹狀圖就被分成三個聚類。
細節:
關於層次聚類演算法可以出現的變化,基本上有三個方面。
最根本的是方法——在這裡,我們使用了一個凝聚的過程,我們從單個數據點開始,並將它們迭代地聚集在一起,直到剩下一個大聚類。 另一種(但是計算量更大)的方法是從一個大聚類開始,然後將資料分成越來越小的聚類,直到剩下孤立的資料點為止。
還有一系列的方法可以用來計算距離矩陣。 很多情況下,歐幾里得距離(參考畢達哥拉斯定理)就足夠了,但是在某些情況下還有其他可能更適用的選擇。
最後,連線標準也可以變化。 聚類根據彼此之間的距離而相互連線,但是我們定義“近”的方式是靈活的。 在上面的例子中,我們測量了每個組的平均值(或“質心”)之間的距離,並將最近的組進行配對。 但是, 你可能想要使用其他的定義。
例如,每個聚類由多個離散點組成。 我們可以將兩個聚類之間的距離定義為任意點之間的最小(或最大)距離——如下圖所示。 還有其他方法來定義連線標準,它們可能適用於不同的環境。
紅/藍:形心連線;紅色/綠色:最少連線;綠/藍:最大連線
圖社群發現
什麼時候使用:
…你可以將資料表示為網路或“圖”的時候。
如何工作:
圖中的社群通常被定義為與網路其餘部分相比,彼此連線更多的頂點的子集。 存在各種演算法來基於更具體的定義來識別社群。 演算法包括但不限於:Edge Betweenness,Modularity-Maximsation,Walktrap,Clique Percolation,Leading Eigenvector …
實現例子:
圖論或網路的數學研究是數學的一個有趣的分支,它讓我們將複雜系統建模為由“線”(或邊)連線的“點”(或頂點)的抽象集合。
最直觀的案例研究也許就是社交網路了。在這裡,頂點代表人,邊連線的是朋友/關注者的頂點。 然而,如果你能證明一個方法可以有意義地連線不同的部分,任何系統都可以被建模為一個網路。 圖論中聚類的創新應用包括從影象資料中提取特徵,分析基因調控網路。
作為一個入門級的例子,看看這個快速組合在一起的圖。 它顯示了我最近訪問過的八個網站,根據它們各自的維基百科文章是否彼此連線而連線在一起。 您可以手動組合這些資料,但是對於大型專案,編寫Python指令碼執行相同的操作要快得多。這是我之前寫的一個。
根據其社群成員對頂點進行著色,並根據其中心位置來確定大小。 看看Google和Twitter是最中心的吧?
而且,這些聚類在現實世界中也很有意義(一直一個重要的效能指標)。黃色的頂點通常是參考/搜尋網站;藍色頂點全部用於線上釋出(文章,推文或程式碼);紅色的頂點包括YouTube,因為Youtube是由前PayPal員工建立的。 機器推論的不錯!
除了作為有用的大型系統視覺化的方式之外,網路的真正威力是其數學分析。 讓我們開始把我們的這張網路圖片變成一種更加數學的格式。 以下是網路的鄰接矩陣。
123456789 | GH GlMPQTWYGitHub01000100Google10111111Medium01000100PayPal01000101Quora01000110Twitter11111001Wikipedia01001000YouTube01010100 |
每行和每列交點處的值記錄該對頂點之間是否存在邊。例如,Medium和Twitter之間存在一條邊(驚喜!),所以它們的行/列相交的值是1。同樣,Medium和PayPal之間沒有邊,所以它們行/列交點的值為0。
在鄰接矩陣內進行編碼就會得到這個網路的所有屬性——它給了我們解鎖各種有價值見解的鑰匙。首先,將任意列(或行)相加,就可以得出每個頂點的度數——即連線了多少個頂點。通常用字母k表示。
同樣,將每個頂點的度數相加併除以2得到L,即網路中邊(或“連線”)的數量。行數/列數即網路中頂點(或“節點”)的數量,用N表示。
知道k,L,N和鄰接矩陣A中每個單元的值,就可以計算任意給定的網路聚類的模組性。
假設我們已經將網路聚類成了一些社群。我們可以使用模組性評分來評估這個聚類的“質量”。更高的分數將表明我們已經把網路分成了“準確”的社群,而低分表明我們的聚類更隨機。下面的圖片說明了這一點。
模組性可以使用下面的公式計算:
這個公示包含相當數量的數學知識,但我們可以一點一點地分解它,使它更好理解。
M當然是我們正在計算的——模組性。
1/2L告訴我們把後面所有的東西除以2L,即網路中邊的數量的兩倍。到現在為止,一切都很好理解。
Σ符號告訴我們對右邊的所有東西求和,並迭代鄰接矩陣A中的每一行和列。對於那些不熟悉求和符號的人來說,i,j = 1和N的作用非常像程式設計中的巢狀for迴圈。在Python中,你可以這樣寫:
123456 | sum=0foriinrange(1,N):forjinrange(1,N):ans=#stuff with i and j as indicessum+=ans |