聚類是指根據資料本身的特徵對資料進行分類,不需要人工標註,是無監督學習的一種。k-means演算法是聚類演算法中最簡單的演算法之一。
k-means 演算法將n個資料物件劃分為 k個聚類以便使得所獲得的聚類滿足:同一聚類中的物件相似度較高;而不同聚類中的物件相似度較小。聚類相似度是利用各聚類中物件的均值所獲得乙個「中心物件」(引力中心)來進行計算的。
基於這樣乙個假設,我們再來匯出k-means所要優化的目標函式:設我們一共有n個資料點需要分為k個cluster,而k-means要做的就是要最小化這個目標函式
過程如下:
1.首先從n個資料物件任意選擇 k 個物件作為初始聚類中心;而對於所剩下其它物件,則根據它們與這些聚類中心的相似度(距離),分別將它們分配給與其最相似的(聚類中心所代表的)聚類;
2.然後再計算每個所獲新聚類的聚類中心(該聚類中所有物件的均值
);不斷重複這一過程直到標準測度函式開始收斂為止。
一般都採用均方差作為標準測度函式,k個聚類具有以下特點:各聚類本身盡可能的緊湊,而各聚類之間盡可能的分開。
每一次更新聚類中心都會使目標函式減小,因此迭代最終j會達到乙個極小值,不能保證是全域性最小值。k-means對於雜訊十分敏感。
c++實現:
class clustermethod
;
類內函式實現:
//param@v 儲存分類結果 v[i][j][k]表示第i類第j個資料第k個特徵(從0開始)
//param@feateres 輸入資料 feateres[i][j]表示第i個資料第j個特徵(i,j從0開始)
//param@clusternum 分類數
//param@samplenum 資料數量
//param@featurenum 資料特徵數
void clustermethod::getclusterd(vector> >&v, double** feateres, int clusternum, int samplenum, int featurenum)
//類內資料初始化
void clustermethod::initialize(double** feateres, int clusternum, int samplenum, int featurenum)
pdistances = new double*[msamplenum];
for (int i = 0; i < msamplenum; ++i)
clusterresult = new int[msamplenum];
}//演算法入口
void clustermethod::k_means(vector> >&v)
//初始化聚類中心
void clustermethod::k_means_initialize()
}}
上面初始化聚類中心是令資料前i(i為聚類中心個數)個點為i個聚類中心。(注意一定不能使用mpcenters[i] = mpsample[i]進行初始化,它們是指標。)
也可以隨機選取i個資料令為聚類中心,這樣同樣的資料多次的執行結果就可能不同。因為k-means結果不一定達到全域性最小點,最簡單的解決方法就是多次執行(這裡指整個函式的重複執行,與聚類時的迭代次數不同)取目標函式最小時候的聚類結果。如果像前面那種每次都選前i個資料初始化聚類中心,多次執行將不能解決區域性最小點問題。
聚類和更新聚類中心的實現如下:
//聚類過程
void clustermethod::k_means_calculate(vector> >&v)
} //計算歐式距離
for (int i = 0; i < msamplenum; ++i)
now_j += pdistances[i][j];}}
if (j - now_j < 0.01)//目標函式不再變化結束迴圈
j = now_j;
//a存放臨時分類結果
vector> > a(mclusternum);
for (int i = 0; i < msamplenum; ++i)
}vectorvec(mfeaturenum);
for (int k = 0; k < mfeaturenum; ++k)
a[clusterresult[i]].push_back(vec);
// v[clusterresult[i]].push_back(vec);這裡不能這樣給v輸入資料,因為v沒有初始化大小
} v = a;
//計算新的聚類中心
for (int j = 0; j < mclusternum; ++j)
}for (int j = 0; j < mclusternum; ++j)
if (v[j].size() != 0)
}} }
//輸出聚類中心
for (int j = 0; j < mclusternum; ++j)
cout << endl;
}}
生成隨機資料函式:
//param@datanum 資料數量
//param@featurenum 每個資料特徵數
double** createdata(int datanum, int featurenum)
cout << "輸入資料:" << endl;
for (int i = 0; i < datanum ; ++i)
cout << endl;
} return data;
}
主函式:
int main()
cout << endl;
} }}
執行結果如下:
k-means更新聚類中心時使用每類的均值,因此對雜訊較為敏感。
k-medoids將前面的均值改為中位數,這樣可以避免個別過大或過小的點對聚類中心的影響。
說到中位數,我就想起了唐僧師徒。。。不對,想起了之前寫的選擇演算法,不需要對所有資料進行排序,執行速度更快。
但這也不是說k-medoids就一定比k-means好,要知道,資料量較大時,演算法的時間也是關鍵。求均值所需時間比求中位數小。因此,孰優孰劣需要看具體需求。
K Means聚類演算法原理
k means演算法是無監督的聚類演算法,它實現起來比較簡單,聚類效果也不錯,因此應用很廣泛。k means演算法有大量的變體,本文就從最傳統的k means演算法講起,在其基礎上講述k means的優化變體方法。包括初始化優化k means 距離計算優化elkan k means演算法和大資料情況...
K Means聚類演算法原理
k means演算法是無監督的聚類演算法,它實現起來比較簡單,聚類效果也不錯,因此應用很廣泛。k means演算法有大量的變體,本文就從最傳統的k means演算法講起,在其基礎上講述k means的優化變體方法。包括初始化優化k means 距離計算優化elkan k means演算法和大資料情況...
K Means聚類演算法的原理及實現
1 如何理解k means演算法?2 如何尋找k值及初始質心?3 如何應用k means演算法處理資料?k means是聚類演算法中的一種,其中k表示類別數,means表示均值。顧名思義k means是一種通過均值對資料點進行聚類的演算法。k means演算法通過預先設定的k值及每個類別的初始質心對...