kmeans聚类的实现

来源:互联网 发布:汉邦网络摄像机默认ip 编辑:程序博客网 时间:2024/06/14 00:19

Kmeans算法流程

从数据中随机抽取k个点作为初始聚类的中心,由这个中心代表各个聚类
计算数据中所有的点到这k个点的距离,将点归到离其最近的聚类里
调整聚类中心,即将聚类的中心移动到聚类的几何中心(即平均值)处,也就是k-means中的mean的含义
重复第2步直到聚类的中心不再移动,此时算法收敛
最后kmeans算法时间、空间复杂度是:
时间复杂度:上限为O(tKmn),下限为Ω(Kmn)其中,t为迭代次数,K为簇的数目,m为记录数,n为维数 
空间复杂度:O((m+K)n),其中,K为簇的数目,m为记录数,n为维数


影响算法准确度的因素

数据的采集和抽象

初始的中心选择

k值的选定

最大迭代次数

收敛值

度量距离的手段


在进一步阐述初始中心点选择之前,我们应该先确定度量kmeans的算法精确度的方法。一种度量聚类效果的标准是:SSE(Sum of Square Error,误差平方和)
SSE越小表示数据点越接近于它们的质心,聚类效果也就越好。因为对误差取了平方所以更重视那些远离中心的点。
一种可以肯定降低SSE的方法是增加簇的个数。但这违背了聚类的目标。因为聚类是在保持目标簇不变的情况下提高聚类的质量。
现在思路明了了我们首先以缩小SSE为目标改进算法。


二分K均值

为了克服k均值算法收敛于局部的问题,提出了二分k均值算法。该算法首先将所有的点作为一个簇,然后将该簇一分为二。之后选择其中一个簇继续划分,选择哪个簇进行划分取决于对其划分是否可以最大程度降低SSE值。
伪代码如下:
将所有的点看成一个簇
当簇数目小于k时
对于每一个簇
计算总误差
在给定的簇上面进行K均值聚类(K=2)
计算将该簇一分为二后的总误差
选择使得误差最小的那个簇进行划分操作



// spectral-cluster.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include <iostream> #include<vector>#include<time.h>#include<cstdlib>#include<set>using namespace std;//template <typename T>class kmeans{private:vector<vector<int>>dataset;unsigned int k;unsigned int dim;typedef vector<double> Centroid;vector<Centroid> center;vector<set<int>>cluster_ID;vector<Centroid>new_center;vector<set<int>>new_cluster_ID;double threshold;int iter;private:void init();void assign();double distance(Centroid cen, int k2);void split(vector<set<int>>&clusters, int kk);void update_centers();bool isfinish();void show_result();void generate_data(){dim = 2;threshold = 0.001;k = 4;for (int i = 0; i < 300; i++){vector<int>data;data.resize(dim);for (int j = 0; j < dim; j++)data[j] = double(rand()) / double(RAND_MAX + 1.0) * 500;dataset.push_back(data);}}public:kmeans(){time_t t;srand(time(&t));}void apply();};//template <typename T>void kmeans::init(){center.resize(k);set<int>bb;for (int i = 0; i < k; i++){int id = double(rand()) / double(RAND_MAX + 1.0)*dataset.size();while (bb.find(id) != bb.end()){id = double(rand()) / double(RAND_MAX + 1.0)*dataset.size();}bb.insert(id);center[i].resize(dim);for (int j = 0; j < dim; j++)center[i][j] = dataset[id][j];}}bool kmeans::isfinish(){double error = 0;for (int i = 0; i < k; i++){for (int j = 0; j < dim; j++)error += pow(center[i][j] - new_center[i][j], 2);}return error < threshold ? true : false;}void kmeans::assign(){for (int j = 0; j < dataset.size(); j++){double mindis = 10000000;int belongto = -1;for (int i = 0; i < k; i++){double dis = distance(center[i], j);if (dis < mindis){mindis = dis;belongto = i;}}new_cluster_ID[belongto].insert(j);}for (int i = 0; i < k; i++){if (new_cluster_ID[i].empty()){split(new_cluster_ID, i);}}}double kmeans::distance(Centroid cen, int k2){double dis = 0;for (int i = 0; i < dim; i++)dis += pow(cen[i] - dataset[k2][i], 2);return sqrt(dis);}void kmeans::split(vector<set<int>>&clusters, int kk){int maxsize = 0;int th = -1;for (int i = 0; i < k; i++){if (clusters[i].size() > maxsize){maxsize = clusters[i].size();th = i;}}#define DELTA 1vector<double>tpc1, tpc2;tpc1.resize(dim);tpc2.resize(dim);for (int i = 0; i < dim; i++){tpc2[i] = center[th][i] - DELTA;tpc1[i] = center[th][i] + DELTA;}for (set<int>::iterator it = clusters[th].begin(); it != clusters[th].end(); it++){double d1 = distance(tpc1, *it);double d2 = distance(tpc2, *it);if (d2 < d1){clusters[kk].insert(*it);}}_ASSERTE(!clusters[kk].empty());for (set<int>::iterator it = clusters[kk].begin(); it != clusters[kk].end(); it++)clusters[th].erase(*it);}void kmeans::update_centers(){for (int i = 0; i < k; i++){Centroid temp;temp.resize(dim);for (set<int>::iterator j = new_cluster_ID[i].begin(); j != new_cluster_ID[i].end(); j++){for (int m = 0; m < dim; m++)temp[m] += dataset[*j][m];}for (int m = 0; m < dim; m++)temp[m] /= new_cluster_ID[i].size();new_center[i] = temp;}}void kmeans::apply(){generate_data();init();new_center.resize(k);new_cluster_ID.resize(k);assign();update_centers();iter = 0;while (!isfinish()){center = new_center;cluster_ID = new_cluster_ID;new_center.clear();new_center.resize(k);new_cluster_ID.clear();new_cluster_ID.resize(k);assign();update_centers();iter++;}show_result();}void kmeans::show_result(){int num = 0;for (int i = 0; i < k; i++){char string[100];sprintf(string, "第个%d簇:", i);cout << string << endl;cout << "中心为 (" << center[i][0] << "," << center[i][1] << ")" << endl;for (set<int>::iterator it = cluster_ID[i].begin(); it != cluster_ID[i].end(); it++){sprintf(string, "编号%d   ", *it);cout << string << "(" << dataset[*(it)][0] << "," << dataset[*(it)][1] << ")" << endl;num++;}cout << endl << endl;}_ASSERTE(num == dataset.size());}int _tmain(int argc, _TCHAR* argv[]){kmeans km;km.apply();system("pause");return 0;}


0 0