题记:又是凌晨3点的夜*
elkan kmeans算法
在我的上一篇博客《kmeans算法性能改进_kmeans++算法+kmeans++优化算法+距离计算优化》中,在一般kmeans算法的基础上,添加了对于初始条件和距离计算更上的一些优化技巧。这篇博客聊一聊elkan算法,其实也是对于kmeans运行速度的优化算法。主要作用与kmeans的距离计算过程。
原理
假如我们已经知道任意两个样本之间的距离,再假设我们已经知道了样本1与聚类中心1之间的距离,根据三角性两边之和大于第三边,c+d>a,所以如果c<a/2即2c<a,那么不用计算我们也能知道d一定大于a,所以样本1的标签肯定不会是2了,就减少了一次计算距离的过程。
elkan kmean算法实现
在我前面两篇文章的基础上,添加一个计算两条样本间欧氏距离的函数:
def distTwoSample(x,y):
tol=x-y
return np.sqrt(np.einsum("i,i->",tol,tol))
elkan kmeas完整代码如下(其实就是在我前两章博客的基础上添加了12行代码,性能我就不展示了),总体感觉哪里还可以优化,不是我认为的最好代码(就是有这种直觉,以后灵光乍现再来完善):
import pandas as pd
import numpy as np
import copy
from core.myviews.views import viewResult
from core.cluster.caculationDistance import euclideanDistance,\
squaredNornal,relativeDist,distTwoSample
#自己写kmeans
if __name__ == '__main__':
"""
在kMeansRow.py的基础上增加初始位置选取策略
kmeans算法的实施过程
-----------------
1、确定初始位置
1.1用最简单的随机生成的方法
1.2用kMeans++改进初始位置的产生(本文的重点)
http://ilpubs.stanford.edu:8090/778/1/2006-13.pdf
算法过程:
1.2.1随机的产生一个点
1.2.2首先计算每个样本与已有聚类中心的最短距离(即离得最近的那个样本的距离);接着计算每个样本被选为
下一个聚类中心的概率(D(X)^2/SUM(D(X)^2))。
分析:可以思考一下为什么要选用欧式距离的平方?其实可以看一下我们计算欧式距离的过程
其实取欧式距离的平方是可以起到降低计算量的目的的,少了开方的过程
1.2.3对1.2.2中的点根据概率采用轮盘赌法进行选择直到采样完成
2、进行若干次的循环:
2.1将剩下的样本根据距离划入相应的簇中
2.2计算新的簇中心
2.3如果新簇与老簇达到了结束条件,就提前退出
3、输出结果
"""
n_cluster=3
maxItem=300
tol=1e-8#停止条件,即center的改变的误差平方和(f范数)
matrix_data=np.asarray(pd.read_csv("../../state/rawData/CompleteOrder.csv",header=None))
n_samples,n_feature=matrix_data.shape
# seed=np.random.permutation(n_samples)[:n_cluster]
# center=matrix_data[seed]
center=np.zeros((n_cluster,n_feature))
center[0]=matrix_data[np.random.randint(n_samples)]
#初始化
for i in range(1,n_cluster):
#计算每个样本点到已有中心点的距离
distance_to_centers=euclideanDistance(matrix_data,center[[i for i in range(i)]],square=True)
#选取据距离最小值
closed_distance=np.min(distance_to_centers,axis=1)
#轮盘赌
denominator=closed_distance.sum()
point=np.random.rand()*denominator#轮盘赌的指针
be_choosed=np.searchsorted(np.cumsum(closed_distance),point)
be_choosed=min(be_choosed,n_samples-1)#避免选的是最后一个而造成下标越界
center[i]=matrix_data[be_choosed]
for i in range(maxItem):
#可以发现kmeans算法的后续操作都是基于labels的,ralative_distance也是只为
#labels服务的,所以如果说距离计算上能够优化就更完美了
# relative_distance=relativeDist(matrix_data,center)
# labels=np.argmin(relative_distance,axis=1)
# elkan过程
# 计算各个cluster center 之间的距离
dist_center_half = euclideanDistance(center, center) / 2
labels=np.zeros(n_samples,dtype=int)
for i in range(n_samples):
min_dist = distTwoSample(matrix_data[i],center[0])
minLabel=0
for j in range(1,n_cluster):
if min_dist>dist_center_half[minLabel,j]:
thisDist=distTwoSample(matrix_data[i],center[j])
if thisDist<min_dist:
min_dist=thisDist
minLabel=j
labels[i]=minLabel
#计算聚类中心
new_center=np.zeros((n_cluster,n_feature))
num_center=np.zeros(n_cluster)
for r,v in enumerate(labels):
new_center[v]+=matrix_data[r]
num_center[v]+=1
#out不开辟新的临时变量
np.maximum(num_center,1,out=num_center)#防止除0
np.reciprocal(num_center,dtype=float,out=num_center)
np.einsum("ij,i->ij",new_center,num_center.T,out=new_center)
# 用f范数评价
center_shift_total=squaredNornal(center-new_center)
if squaredNornal(center_shift_total<tol):
break
center=copy.deepcopy(new_center)
print("迭代次数%s,聚类中心最小偏移平方和%s"%(i,center_shift_total))
print("-----下面打印统计指标------")
viewResult(matrix_data,labels,center,"elkanKmeans")
后记:其实我还是喜欢飞翔的