概述
聚类(K-mean)是一种典型的无监督学习。
采用距离作为相似性的评价指标,即认为两个对象的距离越近,其相似度就越大。
该算法认为类簇是由距离靠近的对象组成的,因此把得到紧凑且独立的簇作为最终目标。
核心思想
通过迭代寻找k个类簇的一种划分方案,使得用这k个类簇的均值来代表相应各类样本时所得的总体误差最小。
k个聚类具有以下特点:各聚类本身尽可能的紧凑,而各聚类之间尽可能的分开。
k-means算法的基础是最小误差平方和准则,
其代价函数是:
式中,μc(i)表示第i个聚类的均值。
各类簇内的样本越相似,其与该类均值间的误差平方越小,对所有类所得到的误差平方求和,即可验证分为k类时,各聚类是否是最优的。
上式的代价函数无法用解析的方法最小化,只能有迭代的方法。
实践
第一步,为了测试,使用脚本生成1000个数据的数据集。
import numpy as np
#x = np.random.uniform(-6,6,2)
#print(str(x[0])+'\t'+str(x[1]))
with open('data.txt', 'w') as f: # 以写的方式打开文件
for i in range(250):
x1 = 2*np.random.randn(2)
x2 = 2 * np.random.randn(2)
x3 = 2 * np.random.randn(2)
x4 = 2 * np.random.randn(2)
strr1 = str(x1[0]+6)+'\t'+str(x1[1]+6)+'\n'
strr2 = str(x2[0] + 6) + '\t' + str(x2[1] - 6) + '\n'
strr3 = str(x3[0] - 6) + '\t' + str(x3[1] + 6) + '\n'
strr4 = str(x4[0] - 6) + '\t' + str(x4[1] - 6) + '\n'
strr = strr1+strr2+strr3+strr4
f.write(strr)
查看data.txt里面的数据:
第二步K-means算法:(解释都在注释中)
from numpy import *
import numpy as np
import matplotlib.pyplot as plt
import time
from threading import Thread
#加载数据
plt.ion() #开启interactive mode
def loadDataSet(fileName):#解析文件,按tab分割字符,得到一个浮点数字类型的矩阵
dataMat = []#文件的最后一个字段是类别标签
fr = open(fileName)
for line in fr.readlines():
curLine = line.strip().split('\t')
fltLine = list(map(float,curLine))#将每个元素转成float类型
dataMat.append(fltLine)
dataMat = np.array(dataMat)
return dataMat
def distEclud(vecA,vecB):
return sqrt(sum(power(vecA - vecB,2)))#求两个向量之间的距离
#构建聚簇中心,取k个(此例中为4)随机质心
def randCent(dataSet,k):
n = shape(dataSet)[1]
centroids = mat(zeros((k,n)))#每个质心有n个坐标值,总共k个质心
for j in range(n):
minJ = min(dataSet[:,j])
maxJ = max(dataSet[:,j])
rangeJ = float(maxJ-minJ)
centroids[:,j] = minJ + rangeJ * random.rand(k,1)
return centroids
#k-means聚类算法
def kMeans(dataSet,k,distMeans = distEclud,createCent = randCent):
m = shape(dataSet)[0]#获取总数据量
clusterAssment = mat(zeros((m,2)))#用于存放该样本属于哪类及质心距离
#clusterAssment第一列存放该数据所属的中心点,第二列是该数据到中心点的距离
centroids = createCent(dataSet,k)#创建k个中心点
clusterChanged = True#用来判断聚类是否收敛
while clusterChanged:
clusterChanged = False
for i in range(m):#把每个数据点划分到离它最近的中心点
minDist = inf;minIndex = -1;#inf为无穷,minIndex为质心的代号
for j in range(k):#分别计算各个点离k个质心的距离
distJI = distMeans(centroids[j,:],dataSet[i,:])
if distJI < minDist:#找到离这个点最近的质心
minDist = distJI;minIndex = j
if clusterAssment[i,0] != minIndex: #只要有一个数据点发生变化,就说明分类还没收敛,还要继续
clusterChanged = True
clusterAssment[i,:] = minIndex,minDist**2 #并将第i个数据点的分配情况存入字典
print(centroids)
for cent in range(k):
ptsInClust = dataSet[nonzero(clusterAssment[:, 0].A == cent)[0]] # 取第一列等于cent的点
centroids[cent, :] = mean(ptsInClust, axis=0) # 算出这些数据的中心点,及当前更新后的质点
ii = 0
for cent in clusterAssment:
#print(cent[0,0])
if cent[0,0] == 0:
x,y = dataSet[ii,0],dataSet[ii,1]
plt.scatter(x, y, c='y')
elif cent[0, 0] == 1:
x, y = dataSet[ii, 0], dataSet[ii, 1]
plt.scatter(x, y, c='r')
elif cent[0, 0] == 2:
x, y = dataSet[ii, 0], dataSet[ii, 1]
plt.scatter(x, y, c='b')
elif cent[0, 0] == 3:
x, y = dataSet[ii, 0], dataSet[ii, 1]
plt.scatter(x, y, c='g')
ii = ii + 1
plt.pause(1)
plt.close()
return centroids, clusterAssment
datMat = mat(loadDataSet('data.txt'))
myCentroids,clustAssing = kMeans(datMat,4)
print(myCentroids)
#print(clustAssing)
数据集比较简单,数据很快就收敛。(数据集越复杂,越大,收敛会越慢)
可以看到最终得到的四个聚类的中心都在(+-6,+-6)附近,符合题设。