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 1
vector<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;
}