K均值聚类算法是一种简单、快速、对大样本集高效的、经典的无监督学习多类分类算法。
K均值聚类算法是最简单的一种基于距离的聚类算法。算法采用样本的特征向量的距离作为相似性的评价标准,两个样本的特征向量的距离越接近,两者的相似度越高。K均值聚类算法训练完成的标准,是使各个样本的特征向量与所在类特征向量均值的误差平方和达到最小,此时所有样本与各自所在类特征向量均值距离最小,相似度最高。距离的计算公式有欧氏距离、曼哈顿距离等等,常用的为欧氏距离公式。
如图 2‑13所示,图中有三类样本点,经过聚类之后,产生了三个聚类中心,各类样本点距离相应的聚类中心最近。各个聚类中心准确的代表了所在类的典型值。
K均值聚类算法的总体上可分为四步,具体如下:
(1)初始化。输入所有样本数据,存入适当的容器中,输入指定要聚类的类别数N,并在容器中随机选取N个对象作为初始聚类中心。设定迭代终止条件,常用的迭代终止条件有最大循环次数和聚类中心收敛误差容限等;
(2)进行迭代。根据算法相似度评价标准将所有数据对象分配到各自最接近的聚类中心所在的类,从而形成N类数据,每一个类用一个容器存储;
(3)更新聚类中心。以每一类数据的几何平均值作为新的聚类中心,然后根据相似性评价标准重新分配数据对象;
(4)循环执行第二步和第三步直至满足迭代终止条件,保存训练得到的聚类中心。
1. Matlab代码
%初始化m_pattern
for i=1:num
m_pattern(i).distance=inf;
m_pattern(i).category=-1;
m_pattern(i).feature=m_pattern1(i).feature;
end
%初始化聚类中心
randPattern=randperm(num);
for i=1:center_num
m_pattern(randPattern(i)).category=i;
m_pattern(randPattern(i)).distance=0;
m_center(i).feature=m_pattern(randPattern(i)).feature;
m_center(i).index=i;
m_center(i).num=1;
m_center(i).distance=0;
end
counter=0;%记录迭代次数
change=1;
while(counter<iter_num&&change~=0)
counter=counter+1;
change=0;
%对每个pattern重归类,并计算重归类后的原类和新类的中心
for i=1:num
index=-1;
distance=inf;
%归类
forj=1:center_num
tempDis=GetDistance(m_pattern(i),m_center(j))-m_center(j).distance;
if(tempDis<distance)
distance=tempDis;
index=j;
end
end
if(m_pattern(i).category==index)
m_pattern(i).distance=distance;
else %计算重归类后的原类和新类的中心
oldIndex=m_pattern(i).category;
m_pattern(i).category=index;
m_pattern(i).distance=distance;
if(oldIndex~=-1)
m_center(oldIndex)=CalCenter(m_center(oldIndex),m_pattern,num);
end
m_center(index)=CalCenter(m_center(index),m_pattern,num);
change=1;
end
end
end
2. C实现
void KMeansCalculate(List l, XInfo* pMeans)
{
List * pl;
double oldVar = -1;
double newVar = 0;
int i;
int count = ListGetCount(l); //样本数量
float threshold = THRESHOLD;
int idx;
int label;
Node * p; //样本数据的链表结构
XInfo x; //样本数据
int time = 0;
//初始化,随即生成聚类中心
pl = (List *)malloc(NK * sizeof(List)); //NK为类别数
for (i = 0; i < NK; i++)
{
idx = rand()%count;
printf("%d \n",idx);
pMeans[i] = ListAt(l, idx)->x;
ListInit(&pl[i]);
}
//根据聚类中心对每个特征向量进行归类
p = l->next;
while (p != NULL)
{
x = p->x;
label = GetLabel(pMeans, x);
ListAdd(&pl[label], x);
p = p->next;
}
//迭代,求出稳定的聚类中心
newVar = GetVar(pl, pMeans);
while (fabs(newVar - oldVar) >= threshold)
{
printf("第%d次迭代开始\n",++time);
for (i = 0; i < NK; i++)
{
pMeans[i] = GetMean(pl[i]);
}
oldVar = newVar;
newVar = GetVar(pl, pMeans);
printf("oldVar = %f newVar = %f\n", oldVar, newVar);
for (i = 0; i < NK; i++)
{
ListClear(&pl[i]);
}
p = l->next;
while (p != NULL)
{
x = p->x;
label = GetLabel(pMeans, x);
ListAdd(&pl[label], x);
p = p->next;
}
}
3. 初值问题改进
Kmens算法由于聚类中心更新的局限性和初始化中心的随机性容易陷入局部最优。如下图所示,当聚类中心到达A位置时
Kmeans 用于图像分割