python机器学习(1:K_means聚类算法)
一、算法介绍
K-means算法是最简单的也是最著名的划分聚类算法,由于简洁和效率使得他成为所有聚类算法中最广泛使用的。算法的目的是使各个样本与所在均值的误差平方和达到最小(这也是评价K-means算法最后聚类效果的评价标准)
这里小编给大家推荐两个不错的学习链接:
二、算法步骤解析
我们以二维点集为例:
- 从平面中随机选取K个点作为初始聚类中心,我们的目的是将给定的所有点集分成K类:
- 计算每个点到初始聚类中心的距离,并选择距离最近的归为其类;
- 所有点归完类后,计算每一类的质心,并求出每一类的质心与上一级的聚类中心的偏移量是否都小于给定的阈值(临界值);
- 若不满足则将求出的质心取代上一级聚类中心并迭代(循环)二三步骤,直到满足条件,迭代终止;
*总结就是这十四个字:求距归类,质心替聚心,最后看偏移。
三、代码实践
- 代码中我们用正太分布来随机取点,最后一图的形式直观显示给大家看,所以我们需先引入一些模块;
import numpy
import random
import pylab as pl
- 为求两点间距,定义一个函数;
def cal_distance(a, b): #计算两点的距离
return (a[0]- b[0]) ** 2 + (a[1] - b[1]) **2
- 我们给出三个正太分布的数据,各200个,并用列表a,b来存放;
x1 = numpy.round(numpy.random.normal(115, 10, 200),2)
y1 = numpy.round(numpy.random.normal(95, 4,200),2)
x2 = numpy.round(numpy.random.normal(100, 10, 200),2)
y2 = numpy.round(numpy.random.normal(70, 3, 200),2)
x3 = numpy.round(numpy.random.normal(150,9 , 200),2)
y3 = numpy.round(numpy.random.normal(72, 4, 200),2)
a = []
b = []
for i in range(200):
a.append(x1[i])
b.append(y1[i])
for i in range(200):
a.append(x2[i])
b.append(y2[i])
for i in range(200):
a.append(x3[i])
b.append(y3[i])
- 我们先来直观看看未分类的图像是什么样的;
pl.figure(1) pl.plot(a,b,'o') pl.title('the initial map')
- 按照我们说的十四字步骤,我们的代码如下;
k1 = [ random.randint(85,145) for _ in range(2)] #任选三个为聚类中心
k2 = [ random.randint(70,150) for _ in range(2)]
k3 = [ random.randint(50,150) for _ in range(2)]
clu_k1 = [] #划分三个聚类
clu_k2 = []
clu_k3 = []
while True:
clu_k1 = []
clu_k2 = []
clu_k3 = []
for i in range(600):
ab_distance1 = cal_distance(k1, [a[i], b[i]])
ab_distance2 = cal_distance(k2, [a[i], b[i]]) #计算每个样本到聚类中心的距离
ab_distance3 = cal_distance(k3, [a[i], b[i]])
if (ab_distance1 <= ab_distance2 and ab_distance1 <= ab_distance3):
clu_k1.append(i)
elif (ab_distance2 <= ab_distance1 and ab_distance2 <= ab_distance3):
clu_k2.append(i) #每个样本归于更近的聚类中心
elif (ab_distance3 <= ab_distance1 and ab_distance3 <= ab_distance2):
clu_k3.append(i)
k1_x = sum([a[i] for i in clu_k1]) / len(clu_k1) #每类样本计算质心并使之成为新的聚类中心
k1_y = sum([b[i] for i in clu_k1]) / len(clu_k1)
k2_x = sum([a[i] for i in clu_k2]) / len(clu_k2)
k2_y = sum([b[i] for i in clu_k2]) / len(clu_k2)
k3_x = sum([a[i] for i in clu_k3]) / len(clu_k3)
k3_y = sum([b[i] for i in clu_k3]) / len(clu_k3)
k1_dis=cal_distance(k1, [k1_x, k1_y])
k2_dis=cal_distance(k2, [k2_x, k2_y])
k3_dis=cal_distance(k3, [k3_x, k3_y])
if k1_dis>0.1 or k2_dis>0.1 or k3_dis>0.1: #判断新聚类中心与上一个聚类中心的偏移量是否为0
k1 = [k1_x, k1_y]
k2 = [k2_x, k2_y]
k3 = [k3_x, k3_y] #偏移量大于0.1,则求出的质心取代原来的聚类中心
else:
break #偏移量小于等于0.1,迭代终止
kv1_x = [a[i] for i in clu_k1] #迭代终止后将同一类的点x,y分别以列表形式存放
kv1_y = [b[i] for i in clu_k1]
kv2_x = [a[i] for i in clu_k2]
kv2_y = [b[i] for i in clu_k2]
kv3_x = [a[i] for i in clu_k3]
kv3_y = [b[i] for i in clu_k3]
- 完成迭代后,我们再以图的形式直观显示出来
pl.figure(2)
pl.title('the altered map')
pl.plot(kv1_x,kv1_y,'*')
pl.plot(kv2_x,kv2_y,'s')
pl.plot(kv3_x,kv3_y,'o')
pl.xlabel('X')
pl.ylabel('Y')
pl.show()
四、算法局限性
- 上面所举的代码中,k=3,如果我们设k=4,5,6….,结果会有挺大的不同;
- 对k个初始质心的选择是随机的,容易陷入局部最小值;
- 数据库比较大的时候,收敛会比较慢;
- 不是所有的数据分布都能搞定;