K-Means算法介绍
什么是K-Means?
K-Means方法是一种非监督学习的算法,它解决的是聚类问题。
K 是聚类算法中簇的个数
Means 是均值算法(聚类中对象的均值)
如上图,当k=2时,可以看出利用K-Means算法可以分成蓝色跟红色两簇。
K-Means算法详解(一)
Step1.给定初始质心:首先选取初始质心集合centerpoints
A.质心数量由用户给出,记为k,k-means最终得到的簇数量也是k
B.随机选取k个聚类质心点
K-Means算法详解(二)
Step2.样本聚类:计算每个样本和每个质心的距离,将样本聚类到距离最近的质心的簇里面
A.样本和质心的距离可以使用欧氏距离(欧几里得距离)用python表示就是np.sqrt(sum((p1-p2) **2))
B.经过step2,得到k个新的簇,每个样本都被分到k个簇中的某一个簇D.得到k个新的簇后,当前的质心就会失效,需要计算每个新簇的自己的新质心。
K-Means算法详解(三)
Step3.重新计算质心:每个簇的新质心的属性值等于此簇中所有样本的属性值得平均值
A.比如一个新簇有3个样本:[[1,4], [2,5], [3,6]],得到此簇的新质心=[(1+2+3)/3, (4+5+6)/3]
B.经过step3,会得到k个新的质心,作为step2中使用的质心
K-Means算法详解(四)
Step4.是否停止K-means:
A.样本点归属簇没有变化
B.当loop次数超过loopLimit时,停止k-means
C.当所有质心变化的距离组成的序列中的最大值,小于maxDistance时,停止k-means
K-Means算法详解(五)
Step5打印输出结果
如果step4没有结束k-means,就再执行step2-step3-step4-step5
如果step4结束了k-means,则就打印(或绘制)簇以及质心
k-means样本数据集实例
样本数据集(1, 1), (2, 2), (3, 1), (6, 4), (7, 5), (8, 4) k = 2, 初始质心 = [[1,1], [2,2]]
第一次k-means聚类
样本 | 质心(1,1)的距离 | 质心(2,2)的距离 | 目标簇号 |
[1 1] | 0.0 | 1.41 | 0 |
[2 2] | 1.41 | 0.0 | 1 |
[3 1] | 2.0 | 1.41 | 1 |
[6 4] | 5.83 | 4.47 | 1 |
[7 5] | 7.21 | 5.83 | 1 |
[8 4] | 7.62 | 6.32 | 1
|
centerpoint_1 = [1, 1] = [1/1, 1/1]
centerpoint_2 = [5.20, 3.20] = [(2 + 3 + 6 + 7 + 8)/5, (2 + 1 + 4 + 5 + 4)/5]
centerpoints = [[1,1], [5.20, 3.20]]
第二次k-means聚类
centerpoint_1 = [2.0, 1.33] = [(1+2+3)/3, (1+2+1)/3]
centerpoint_2 = [7.0, 4.33] = [(6 + 7 + 8)/3, (4 + 5 + 4)/3]
centerpoints = [[2.0, 1.33], [7.0, 4.33]]
算法的时间复杂度
O(tkmn),其中,t为迭代次数,k为簇的数目,m为记录数,n为维数
算法的空间复杂度:
O((m+k)n),其中,k为簇的数目,m为记录数,n为维数
k-means的优点和缺点
以上K-Means python代码:
import numpy as np
import random
import matplotlib.pyplot as plt
# 加载数据
def loadDataSet(fileName):
dataSet = np.loadtxt(fileName,delimiter='\t')
return dataSet
# 欧氏距离计算
def distEclud(x,y):
return np.sqrt(np.sum((x-y)**2)) # 计算欧氏距离
# 为给定数据集构建一个包含K个随机质心的集合
def randCent(dataSet,k):
m,n = dataSet.shape
centerpoints = np.zeros((k,n))
for i in range(k):
index = int(np.random.uniform(0,m)) #
centerpoints[i,:] = dataSet[index,:]
return centerpoints
def pointToCenterMinDist(point, centerpoints, center_num):
min_dist = distEclud(point, centerpoints[0,:])
for i in range(center_num):
distance = distEclud(point, centerpoints[i,:])
if distance < min_dist:
min_dist = distance
return min_dist
#kmeans++方法与kmeans方法的区别就是初始化中心点的不同
def selectCenter(dataSet,k):
m,n = dataSet.shape
centerpoints = np.zeros((k,n))
# 1、随机选择一个样本点为第一个聚类中心
index = np.random.randint(0, m)
centerpoints[0,:] = dataSet[index,:]
for i in range(1,k):
weights=[pointToCenterMinDist(dataSet[x,:], centerpoints, i)
for x in range(m)]
total=sum(weights)
#归一化0到1之间
weights=[x/total for x in weights]
num=random.random()
total=0
x=-1
while total<num:
x+=1
total+=weights[x]
centerpoints[i,:] = dataSet[x,:]
return centerpoints
# k均值聚类
def kMeans(dataSet,k):
m = np.shape(dataSet)[0] #行的数目
# 第一列存样本属于哪一簇
# 第二列存样本的到簇的中心点的误差
clusterAssment = np.mat(np.zeros((m,2)))
clusterChange = True
# 初始化centerpoints
centerpoints = selectCenter(dataSet,k)
print(centerpoints)
while clusterChange:
clusterChange = False
# 遍历所有的样本(行数)
for i in range(m):
minDist = 100000.0
minIndex = -1
# 遍历所有的质心
# 找出最近的质心
for j in range(k):
# 计算该样本到质心的欧式距离
distance = distEclud(centerpoints[j,:],dataSet[i,:])
if distance < minDist:
minDist = distance
minIndex = j
# 第 3 步:更新每一行样本所属的簇
if clusterAssment[i,0] != minIndex:
clusterChange = True
clusterAssment[i,:] = minIndex,minDist**2
#更新质心
for j in range(k):
pointsInCluster = dataSet[np.nonzero(clusterAssment[:,0].A == j)[0]] # 获取簇类所有的点
centerpoints[j,:] = np.mean(pointsInCluster,axis=0) # 对矩阵的行求均值
print("Congratulations,cluster complete!")
return centerpoints,clusterAssment
def showCluster(dataSet,k,centerpoints,clusterAssment):
m,n = dataSet.shape
if n != 2:
print("数据不是二维的")
return 1
mark = ['or', 'ob', 'og', 'ok', 'oy', 'sm', 'sc', 'sr', 'sg', 'sb']
if k > len(mark):
print("k值太大了")
return 1
# 绘制所有的样本
for i in range(m):
markIndex = int(clusterAssment[i,0])
plt.plot(dataSet[i,0],dataSet[i,1],mark[markIndex])
#mark = ['Dr', 'Db', 'Dg', 'Dk', '^b', '+r', 'sb', 'db', '<b', 'pb']
mark = ['+r', '+b', '+g', '+k', '+y', '+m', '*c', '*r', '*g', '*b']
# 绘制质心
for i in range(k):
plt.plot(centerpoints[i,0],centerpoints[i,1],mark[i])
plt.show()
if __name__ == '__main__':
dataSet = loadDataSet("test_data2.txt")
k = 2
centerpoints,clusterAssment = kMeans(dataSet,k)
showCluster(dataSet,k,centerpoints,clusterAssment)
样本数据自己可以造
1 1
2 2
3 1
6 4
7 5
8 4
用py3跑
k-means非球状的数据分布
K-Means++初始化优化
k个初始化的质心的位置选择对最后的聚类结果和运行时间都有很大的影响,因此需要选择合适的k个质心。 求初始质心算法:
a)从输入的数据点集合中随机选择一个点作为第一个聚类中心
b)对于数据集中的每一个点,计算它与已选择的聚类中心中最近聚类中心的距离
c)选择一个新的数据点作为新的聚类中心,选择的原则是:距离较大的点,被选取作为聚类中心的概率较大
d)重复b和c直到选择出k个聚类质心 e)利用这k个质心来作为初始化质心去运行标准的K-Means算法
0 0.5 1
p1 p2 p3 p4
elkan K-Means距离计算优化
elkan K-Means利用两边之和大于等于第三边,以及两边之差小于第三边的三角形性质,来减少距离计算。
A.第一种规律是对于一个样本点X和两个质心C1,C2。如果我们预先计算出了这两个质心之间的距离D(C1,C2),则如果计算发现2D(X,C1)≤D(C1,C2),我们立即就可以知道D(X,C1)≤D(X,C2)。此时我们不需要再计算D(X,C2),也就是说省了一步距离计算。
B.第二种规律是对于一个样本点x和两个质心C1,C2。我们可以得到D(X,C2)≥max{0,D(x,C1)−D(C1,C2)}。这个从三角形的性质也很容易得到。
elkan K-Means(两边和)
1.如果 2a ≤ c则a ≤ b.
变换公式 c ≤ a + b ⇒ c− a ≤ b
2a ≤ c
elkan K-Means(两边差)
2.两边之差小于第三边
b≥ max{0, c - a}
c ≤ a + b ⇒ b≥ c − a
Also, b ≥ 0
K-means确定K值-手肘法(一)
核心指标:SSE(sum of the squared errors,误差平方和)
Ci是第i个簇,p是Ci中的样本点,mi是Ci的质心(Ci中所有样本的均值),SSE是所有样本的聚类误差,代表了聚类效果的好坏。
K-means确定K值-手肘法(二)
Mini Batch K-Means大样本优化
在统的K-Means算法中,要计算所有的样本点到所有的质心的距离。如果样本量非常大,比如达到10万以上,特征有100以上,此时用传统的K-Means算法非常的耗时,就算加上elkan K-Means优化也依旧。Mini Batch,也就是用样本集中的一部分的样本来做传统的K-Means,这样可以避免样本量太大时的计算难题,算法收敛速度大大加快。当然此时的代价就是我们的聚类的精确度也会有一些降低。一般来说这个降低的幅度在可以接受的范围之内。 batch size个样本一般是通过无放回的随机采样得到的。
该算法的迭代步骤有两步:
1:从数据集中随机抽取一些数据形成小批量,把他们分配给最近的质心
2:更新质心
K-Means算法的十大用例(一)
1.文档分类器
2.股票预测研究
3.识别犯罪地点 分析犯罪类别、犯罪地点以及两者之间的关联,加强对该类型犯罪的防范。
4.客户分类 根据客户的购买历史、兴趣或活动监控来对客户类别做进一步细分,有助于公司针对特定客户群制定特定的广告。 5.图像分割
6.保险欺诈检测
7.乘车数据分析 提供了大量关于交通、运输时间、高峰乘车地点等有价值的数据集。
8.分析网络诈骗
9.呼叫记录详细分析 将通话详细记录与客户个人资料结合在一起,这能够帮助电信公司对客户需求做更多的预测。
10.IT警报的自动化聚类 对警报消息进行聚类可以对警报类别和平均修复时间做深入了解,有助于对未来故障进行预测。
K-Means图像分割
其他相关学习资料
1.利用余弦计算文本相似度
https://blog.csdn.net/qq_15111861/article/details/80138784
2. scikit-learn中文文档
http://sklearn.apachecn.org/#/docs/22