K-均值聚類算法及其初始點的選取

K-均值聚類(K-Means Clustering)是一種無監督的聚類方法,即最初並不知道同種類數據的特徵,算法會根據數據自身特點進行分類。
算法流程如下:
1 . 選取k個初始中心點,選取方法根據具體數據特點決定,可以是隨機;
2 . 遍歷數據集,找到離每個數據最近的中心點,並將其歸入該點;
3 . 更新中心點位置:求出歸入每個中心點的數據的均值,將其更新爲新的中心點;
4 . 如果中心點更新量小於某個閾值,或者算法執行到一定次數,結束程序;否則,執行步驟2。

在算法中,k是需要提前約定的,它代表期望的種類數。但有時會不確定數據的種類數目,這種情況可以多次嘗試使用不同的k值進行聚類,並選取其中最符合的(參考:http://blog.csdn.net/shennongzhaizhu/article/details/51871891
另外一個是初始點的選取。初始點的選取與數據具體特徵有關,如果數據分佈較密集,且相對於選定的k值比較符合,這時如果使用隨機選點就可能造成空簇,而某兩類本應該被分開的數據聚合到一個類中。這種情況,需要選定其初始互相較遠的k個點作爲初始點。

一個實例如下:

//pts_src爲原數據,data爲聚類得到需要輸出的中心點數據
    void kmean_cluster(int k = 3){
        //初始化中心點(應選取相互距離較遠的三個點)
        vector<Point3d> ks(3,Point3d());
        Point3d pt_evg;
        ks[0] = pts_src[0];
        double max_dis = 0;
        int max_i = 0;
        for (int i = 1; i < pts_src.size(); i++){
            double dis = P3d_distance(ks[0], pts_src[i]);
            if (dis > max_dis){
                max_dis = dis;
                max_i = i;
            }
        }
        ks[1] = pts_src[max_i];
        max_dis = 0;
        for (int i = 1; i < pts_src.size(); i++){
            double dis = P3d_distance(ks[0], pts_src[i]) + P3d_distance(ks[1], pts_src[i]);
            if (dis > max_dis){
                max_dis = dis;
                max_i = i;
            }
        }
        ks[2] = pts_src[max_i];
        int n = 100;
        bool over_flag = false;
        //---------
        while (n--){
            vector<set<int>> sets(k, set<int>());//集合初始化
            for (int i = 0; i < pts_src.size(); i++){
                double min = 1000;
                int min_j = 0;
                for (int j = 0; j < k; j++){
                    double dist = P3d_distance(ks[j], pts_src[i]);
                    if (dist < min){
                        min = dist;
                        min_j = j;
                    }
                }
                sets[min_j].insert(i);
                if (over_flag){
                    save_card(min_j, i, pic_src[i]);
                    //imshow(num2str(min_j), pic_src[i]);
                    //waitKey(0);
                }
            }
            if (over_flag){
                cout << "輸入角色對應集合的順序:0 1 2" << endl;
                for (int i = 0; i < k; i++){
                    int temp;
                    cin >> temp;
                    pos.push_back(temp);
                }
                data = ks;
                save_data();
                cout << "數據已存儲" << endl;
                break;
            }
            //重新分配中心點
            int i = 0;
            int sim_count = 0;
            for (auto&s : sets){
                Point3d pt_evg;
                for (auto&i : s){
                    Point3d pt = pts_src[i];
                    pt_evg.x += pt.x;
                    pt_evg.y += pt.y;
                    pt_evg.z += pt.z;
                }
                if (s.size()){
                    pt_evg.x /= s.size();
                    pt_evg.y /= s.size();
                    pt_evg.z /= s.size();
                }
                if (P3d_distance(pt_evg, ks[i]) < 0.01)sim_count++;
                ks[i++] = pt_evg;
            }
            if (sim_count == k)over_flag = true;
            //
        }
    }

這是從最近正在做的一個程序中截取的部分代碼,作用是將得到的15張圖片根據rgb值分類。因爲其類型數目是已知的,k= 3。且其各個特徵較爲明顯,只是存在噪聲干擾。
最初使用隨機選取點,聚類結果如下:
這裏寫圖片描述
文件第一個數字爲集合標識。可以看出,雖然準確將地分成了兩類(分類沒有出錯),但是沒有達到想要的目標:分成三類,因爲存在一個空簇。
後來使用找出互相較遠的點的方法,其流程如下:
1 . 隨機選取一個數據點,作爲第一個中心
2 . 找出離這個數據點最遠的一個點,作爲第二個中心
3 . 找出離這兩個數據點之和最遠的一個點,作爲第三個中心
再往更高的k推廣的話,這個算法的初始點選擇又會影響,但是對於這裏的三個數據點是比較好用的。
其聚類結果如下:
這裏寫圖片描述

參考資料:
http://blog.csdn.net/rex_huang61/article/details/51051839
《集體智慧編程》42~44

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章