Change Dir

          先知cd——熱愛生活是一切藝術的開始

          統計

          留言簿(18)

          積分與排名

          “牛”們的博客

          各個公司技術

          我的鏈接

          淘寶技術

          閱讀排行榜

          評論排行榜

          聚類算法學習筆記(四)——層次聚類

           

          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,rsd(Cr,Cs)

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

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

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

          2. 算法實現

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

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


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

          1= clusterdata(X,cutoff)

           

          Java實現的版本:

           

            1package util;
            2
            3import java.util.*;
            4
            5public 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 = 014689 }103578 },
          145                430245 }652023 },
          146                874201 }985310 } }
          ;
          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. 小結

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

          4. 參考文獻及推薦閱讀

          [1]Pattern Recognition Third Edition, Sergios Theodoridis, Konstantinos Koutroumbas

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



          PS,第四第五節的內容的代碼地址:

          http://www.kuaipan.cn/index.php?ac=fileview&dirid=50160446308614332


          posted on 2010-03-19 20:08 changedi 閱讀(13756) 評論(23)  編輯  收藏 所屬分類: 聚類分析

          評論

          # re: 聚類算法學習筆記(四)——層次聚類 2010-03-20 10:40 路人甲

          哈哈,我這幾天也在學聚類,樓主的博客寫的不錯!  回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類 2010-03-22 15:53 changedi

          @路人甲
          大家可以共同探討~~  回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類 2010-04-22 00:00 劉賀

          你好,能給我發一份源碼嗎?拜托!hebeigeng@126.com  回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類 2010-04-22 12:50 changedi

          @劉賀
          已發送,附帶并查集的實現。  回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類[未登錄] 2010-05-02 10:35 緣來如此

          我現在正在學習聚類算法,能發一份到我的郵箱嗎?謝謝。dowell_2008@live.cn  回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類 2010-06-15 16:25 Meccas

          麻煩您可以發一份源碼給我嗎?我的郵箱是zhaozeyu1@yahoo.cn,最近在寫聚類算法的論文,但是苦于手中沒有源碼,看到您的文章,寫的太好了。不知可以嗎?  回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類 2010-06-21 18:50 王虎虎

          @changedi
          您好,請問您可以給我發個完整的源碼么,您給出的這個代碼里面沒有DisjointSets類的實現,我自己想寫出來,可惜不行,呵呵,我擅長c/c++,對java不很了解,哎,最近一直在研究層次聚類算法,謝謝您!  回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類 2010-06-21 18:54 王虎虎

          您好!
          能否給我發一份您完整的源代碼,您上面給出的那個代碼缺少DisjointSets類的實現,我自己補寫了好幾天還是沒搞定,擺脫您了,我的郵箱是tjnuwanghu@163.com,
          tel:13752311612。
          謝謝您!  回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類 2010-06-22 14:22 王虎虎

          @劉賀
          您好!
          您那有樓主給的層次聚類的java實現代碼么?我也最近在做這方面的研究,生物信息方向的,我對java不是很了解,想看看,完了后轉成c++的。謝謝您啊!

          請您給我也發一份吧:
          Email:tjnuwanghu@163.com
          TEL:137+5231+1612
            回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類 2010-06-25 23:08 王虎虎

          @王虎虎
          package util;
          /**
          * 并查集簡單實現
          * @author Jia Yu
          *
          */
          public class DisjointSets
          {
          public DisjointSets( int numElements )
          {
          s = new int [ numElements ];
          for( int i = 0; i < s.length; i++ )
          s[ i ] = -1;
          }

          public void union( int root1, int root2 )
          {
          assertIsRoot( root1 );
          assertIsRoot( root2 );
          if( root1 == root2 )
          throw new IllegalArgumentException( "Union: root1 == root2 " + root1 );

          if( s[ root2 ] < s[ root1 ] )
          s[ root1 ] = root2;
          else
          {
          if( s[ root1 ] == s[ root2 ] )
          s[ root1 ]--;
          s[ root2 ] = root1;
          }
          }

          public int find( int x )
          {
          assertIsItem( x );
          if( s[ x ] < 0 )
          return x;
          else
          return s[ x ] = find( s[ x ] );
          }

          private int [ ] s;


          private void assertIsRoot( int root )
          {
          assertIsItem( root );
          if( s[ root ] >= 0 )
          throw new IllegalArgumentException( "Union: " + root + " not a root" );
          }

          private void assertIsItem( int x )
          {
          if( x < 0 || x >= s.length )
          throw new IllegalArgumentException( "Disjoint sets: " + x + " not an item" );
          }

          }
            回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類 2010-07-06 07:46 馬雷

          劉賀,你好!
          您那有層次聚類的java實現代碼嗎?我也最近在做這方面的學習,請你給我發送一份吧,郵箱793125322@qq.com  回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類[未登錄] 2011-08-09 13:32 jiajia

          樓主,您好。
          我也最近在做這方面的研究,對java不是很了解,方便發一份完整代碼么,謝謝。
          278164822@qq.com  回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類 2011-10-03 15:31 www

          你好,最近剛學習聚類,能發送一份java源碼嗎?謝謝。wwwsd@live.cn  回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類 2012-08-06 17:34 smallmood

          樓主,你好!層次聚類總結的真好,最近在研究chameleon,想問一下,有沒有關于chameleon的資料,如果有的話,能給我發一份嗎?算法、matlab實現等等都可以,謝啦!sjiezz@163.com  回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類 2012-10-24 17:16 changedi

          我把代碼發鏈接出來,需要的下載就好http://www.kuaipan.cn/index.php?ac=fileview&dirid=50160446308614332  回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類 2012-12-10 21:09 香草可樂

          樓主,你寫的好棒,你發布的那個連接沒有下載的文件喔,能發給我一份源代碼么,萬分感謝!!420164881@qq.com  回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類[未登錄] 2013-01-14 16:53 白樺林

          樓主你好 能給我發份完整的代碼嗎 感激不盡 1028122036@qq.com  回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類[未登錄] 2013-01-14 16:54 白樺林

          對不起 郵箱寫錯了 是1028142036@qq.com  回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類[未登錄] 2013-02-27 17:26 笑笑

          不知問您一下 您的這個代碼可以進行短文本聚類嗎?或者是詞聚類  回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類[未登錄] 2013-02-27 17:50 笑笑

          你知道是否能對短文本聚類呢?@香草可樂
            回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類[未登錄] 2013-02-28 09:22 changedi

          當然可以,詞匯作為空間節點,詞匯相似度作為相似測度(節點距離)@笑笑
            回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類 2013-07-25 10:40 zack

          看了一下 這貨不是貪心么 跟哈弗曼樹差不多啊  回復  更多評論   

          # re: 聚類算法學習筆記(四)——層次聚類 2013-10-22 17:27 歲月的帆

          貌似進不去啊@changedi
          麻煩發給我郵件吧 872651253@qq.com 謝謝  回復  更多評論   

          主站蜘蛛池模板: 宁津县| 含山县| 景德镇市| 惠州市| 天津市| 富蕴县| 定州市| 西城区| 高阳县| 长泰县| 商河县| 扬州市| 太保市| 甘肃省| 营山县| 闽清县| 吉木乃县| 紫金县| 巴林左旗| 福贡县| 东海县| 射洪县| 苗栗市| 定南县| 开江县| 深州市| 延寿县| 新巴尔虎右旗| 清苑县| 台安县| 成安县| 临澧县| 兰考县| 岱山县| 新绛县| 临城县| 湟中县| 巧家县| 津市市| 石家庄市| 临夏县|