層次聚類


1. 層次聚類

層次聚類算法與之前所講的順序聚類有很大不同,它不再產生單一聚類,而是產生一個聚類層次。說白了就是一棵層次樹。介紹層次聚類之前,要先介紹一個概念——嵌套聚類。講的簡單點,聚類的嵌套與程序的嵌套一樣,一個聚類中R1包含了另一個R2,那這就是R2嵌套在R1中,或者說是R1嵌套了R2。具體說怎么算嵌套呢?聚類R1={{x1,x2},{x3},{x4,x5}嵌套在聚類R2={{x1,x2,x3},{x4,x5}}中,但並不嵌套在聚類R3={{x1,x4},{x3},{x2,x5}}中。

層次聚類算法產生一個嵌套聚類的層次,算法最多包含N步,在第t步,執行的操作就是在前t-1步的聚類基礎上生成新聚類。主要有合並和分裂兩種實現。我這里只講合並,因為前一階段正好課題用到,另外就是合並更容易理解和實現。當然分裂其實就是合並的相反過程。

g(Ci,Cj)為所有可能的X聚類對的函數,此函數用於測量兩個聚類之間的近鄰性,用t表示當前聚類的層次級別。通用合並算法的偽碼描述如下:

1. 初始化:

a) 選擇Â0={{x1},…,{xN}}

b) 令t=0

2. 重復執行以下步驟:

a) t=t+1

b) 在Ât-1中選擇一組(Ci,Cj),滿足

c) 定義Cq=CiÈCj,並且產生新聚類Ât=(Ât-1-{Ci,Cj})È{Cq}

直到所有向量全被加入到單一聚類中。

這一方法在t層時將兩個向量合並,那么這兩個向量在以后的聚類過程中的后繼聚類都是相同的,也就是說一旦它們走到一起,那么以后就不會再分離……(很專一哦O(∩_∩)O~)。這也就引出了這個算法的缺點,當在算法開始階段,若出現聚類錯誤,那么這種錯誤將一直會被延續,無法修改。在層次t上,有N-t個聚類,為了確定t+1層上要合並的聚類對,必須考慮(N-t)(N-t-1)/2個聚類對。這樣,聚類過程總共要考慮的聚類對數量就是(N-1)N(N+1)/6,也就是說整個算法的時間復雜度是O(N3)。

舉例來說,如果令X={x1, x2, x3, x4, x5},其中x1=[1, 1]T, x2=[2, 1]T, x3=[5, 4]T, x4=[6, 5]T, x5=[6.5, 6]T。那么合並算法執行的過程可以用下面的圖來表示。

    P(X)是不相似矩陣

該算法從核心過程上來講,就是先計算出數據集中向量之間的距離,記為距離矩陣(也叫不相似矩陣)。接着通過不斷的對矩陣更新,完成聚類。矩陣更新算法的偽碼描述如下:

1. 初始化:

a) Â0={{x1},…,{xN}}

b) P0=P(X) (距離矩陣)

c) t=0

2. 重復執行以下步驟:

a) t=t+1

b) 合並CiCjCq,這兩個聚類滿足d(Ci,Cj)=minr,s=1,…,N,r≠sd(Cr,Cs)

c) 刪除第i和j行,第i和j列,同時插入新的行和列,新的行列為新合並的類Cq與所有其他聚類之間的距離值

直到將所有向量合並到一個聚類中

大家可以看到,層次聚類算法的輸出結果總是一個聚類,這往往不是我們想要的,我們總希望算法在得到我們期望的結果后就停止。那么我們如何控制呢?常用的做法就是為算法限制一個閾值,矩陣更新過程中,總是將兩個距離最近的聚類合並,那么我們只要加入一個閾值判斷,當這個距離大於閾值時,就說明不需要再合並了,此時算法結束。這樣的閾值引入可以很好的控制算法結束時間,將層次截斷在某一層上。

2. 算法實現

       MATLAB實現了層次聚類算法,基本語句如下:

1X = [1 2;2.5 4.5;2 2;4 1.5;4 2.5] ;
2Y = pdist(X,'euclid'); 
3Z = linkage(Y,'single'); 
4T = cluster(Z,'cutoff',cutoff);

MATLAB還有一個簡化的層次聚類版本,一句話搞定

1T = clusterdata(X,cutoff)

Java實現的版本:

1 package util;
  2
  3 import java.util.*;
  4
  5 public class Clusterer {
  6    private List[] clusterList;
  7    DisjointSets ds;
  8    private static final int MAX = Integer.MAX_VALUE;
  9    private int n;
10    private int cc;
11
12    // private double ori[] = {1,2,5,7,9,10};
13
14    public Clusterer(int num) {
15        ds = new DisjointSets(num);
16        n = num;
17        cc = n;
18        clusterList = new ArrayList[num];
19        for (int i = 0; i < n; i++)
20            clusterList[i] = new ArrayList();
21    }
22
23    public List[] getClusterList() {
24        return clusterList;
25    }
26
27    public void setClusterList(List[] clusterList) {
28        this.clusterList = clusterList;
29    }
30
31    public void output() {
32        int ind = 1;
33        for (int i = 0; i < n; i++) {
34            clusterList[ds.find(i)].add(i);
35        }
36        for (int i = 0; i < n; i++) {
37            if (clusterList[i].size() != 0) {
38                System.out.print("cluster " + ind + " :");
39                for (int j = 0; j < clusterList[i].size(); j++) {
40                    System.out.print(clusterList[i].get(j) + " ");
41                }
42                System.out.println();
43                ind++;
44            }
45        }
46    }
47
48    /** *//**
49     * this method provides a hierachical way for clustering data.
50     *
51     * @param r
52     *            denote the distance matrix
53     * @param n
54     *            denote the sample num(distance matrix's row number)
55     * @param dis
56     *            denote the threshold to stop clustering
57     */
58    public void cluster(double[][] r, int n, double dis) {
59        int mx = 0, my = 0;
60        double vmin = MAX;
61        for (int i = 0; i < n; i++) { // 尋找最小距離所在的行列
62            for (int j = 0; j < n; j++) {
63                if (j > i) {
64                    if (vmin > r[i][j]) {
65                        vmin = r[i][j];
66                        mx = i;
67                        my = j;
68                    }
69                }
70            }
71        }
72        if (vmin > dis) {
73            return;
74        }
75        ds.union(ds.find(mx), ds.find(my)); // 將最小距離所在的行列實例聚類合並
76        double o1[] = r[mx];
77        double o2[] = r[my];
78        double v[] = new double[n];
79        double vv[] = new double[n];
80        for (int i = 0; i < n; i++) {
81            double tm = Math.min(o1[i], o2[i]);
82            if (tm != 0)
83                v[i] = tm;
84            else
85                v[i] = MAX;
86            vv[i] = MAX;
87        }
88        r[mx] = v;
89        r[my] = vv;
90        for (int i = 0; i < n; i++) { // 更新距離矩陣
91            r[i][mx] = v[i];
92            r[i][my] = vv[i];
93        }
94        cluster(r, n, dis); // 繼續聚類,遞歸直至所有簇之間距離小於dis值
95    }
96
97    /** *//**
98     *
99     * @param r
100     * @param cnum
101     *            denote the number of final clusters
102     */
103    public void cluster(double[][] r, int cnum) {
104        /**//*if(cc< cnum)
105            System.err.println("聚類數大於實例數");*/
106        while (cc > cnum) {// 繼續聚類,循環直至聚類個數等於cnum
107            int mx = 0, my = 0;
108            double vmin = MAX;
109            for (int i = 0; i < n; i++) { // 尋找最小距離所在的行列
110                for (int j = 0; j < n; j++) {
111                    if (j > i) {
112                        if (vmin > r[i][j]) {
113                            vmin = r[i][j];
114                            mx = i;
115                            my = j;
116                        }
117                    }
118                }
119            }
120            ds.union(ds.find(mx), ds.find(my)); // 將最小距離所在的行列實例聚類合並
121            double o1[] = r[mx];
122            double o2[] = r[my];
123            double v[] = new double[n];
124            double vv[] = new double[n];
125            for (int i = 0; i < n; i++) {
126                double tm = Math.min(o1[i], o2[i]);
127                if (tm != 0)
128                    v[i] = tm;
129                else
130                    v[i] = MAX;
131                vv[i] = MAX;
132            }
133            r[mx] = v;
134            r[my] = vv;
135            for (int i = 0; i < n; i++) { // 更新距離矩陣
136                r[i][mx] = v[i];
137                r[i][my] = vv[i];
138            }
139            cc--;
140        }
141    }
142
143    public static void main(String args[]) {
144        double[][] r = { { 0, 1, 4, 6, 8, 9 }, { 1, 0, 3, 5, 7, 8 },
145                { 4, 3, 0, 2, 4, 5 }, { 6, 5, 2, 0, 2, 3 },
146                { 8, 7, 4, 2, 0, 1 }, { 9, 8, 5, 3, 1, 0 } };
147        Clusterer cl = new Clusterer(6);
148        //cl.cluster(r, 6, 1);
149        cl.cluster(r, 3);
150        cl.output();
151    }
152
153}
154

3. 小結
       層次聚類算法是非常常用的聚類算法,同時也是被廣泛研究的聚類算法。層次聚類本身分為合並和分裂兩種實現,在合並算法中,又分基於矩陣理論的合並和基於圖論的合並。本文只是初學聚類的一點體會,因此只實現了基於矩陣理論的算法,同時,用於大數據集合的層次算法如CURE,ROCK和Chameleon算法都沒有涉及,這些算法如果以后有時間,會整理發布。還有截斷點的選擇,最佳聚類數的確定都是可以研究的問題。

4. 參考文獻及推薦閱讀
[1]Pattern Recognition Third Edition, Sergios Theodoridis, Konstantinos Koutroumbas

[2]模式識別第三版, Sergios Theodoridis, Konstantinos Koutroumbas著, 李晶皎, 王愛俠, 張廣源等譯

http://www.blogjava.net/changedi/archive/2010/03/19/315963.html

http://ishare.iask.sina.com.cn/f/12149552.html?from=like


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM