机器学习-二分KMeans
由于传统的KMeans算法的聚类结果容易受到初始聚类中心点选择的影响,因此在传统的KMeans算法的基础上进行算法改进,对初始中心点选取比较严格,各中心点的距离较远,这就避免了初始聚类中心会选到一个类上,一定程度上克服了算法限入局部最优状态。
二分KMeans(Bisecting KMeans)算法的主要思想是:首先将所有点作为一个簇,然后将该簇一分为二。之后选择能最大限度降低聚类代价函数(误差平方和)的簇划分为两个簇。以此进行下去,直到簇的数目等于用户给定的数目k为止。以上隐含的一个原则是:因为聚类的误差平方和能够衡量聚类性能,该值越小表示数据点越接近于它们的质心,聚类效果就越好。所以我们需要对误差平方和最大的簇进行再一次划分,因为误差平方和越大,表示该簇聚类效果越不好,越有可能是多个簇被当成了一个簇,所以我们首先需要对这个簇进行划分。
代码实现(其中的kmeans方法参照上一篇)
# 二分kmeans实现 from numpy import * import matplotlib.pyplot as plt # 数据文件转矩阵 from sklearn.cluster import KMeans from com.machineLearning.kmeans.KmeansSelf import kMeans def file2matrix(filePath): dataSet = [] fr = open(filePath, 'r') content = fr.read() for line in content.splitlines(): dataSet.append([line.split(' ')[0], line.split(' ')[1]]) fr.close() return dataSet # 欧氏距离公式 def distEclud(vecA, vecB): return linalg.norm(vecA.astype(float) - vecB.astype(float)) # 根据聚类中心绘制散点图,以及绘制聚类中心 def color_cluster(dataindx, dataSet, plt, k=4): # print(dataindx) plt.scatter(dataSet[:, 0].tolist(), dataSet[:, 1].tolist(), c='blue', marker='o') # index = 0 # datalen = len(dataindx) # for indx in range(datalen): # if int(dataindx[index]) == 0: # plt.scatter(dataSet[index, 0].tolist(),dataSet[index, 1].tolist(), c='blue', marker='o') # elif int(dataindx[index]) == 1: # plt.scatter(dataSet[index, 0].tolist(), dataSet[index, 1].tolist(), c='green', marker='o') # elif int(dataindx[index]) == 2: # plt.scatter(dataSet[index, 0].tolist(), dataSet[index, 1].tolist(), c='red', marker='o') # elif int(dataindx[index]) == 3: # plt.scatter(dataSet[index, 0].tolist(), dataSet[index, 1].tolist(), c='cyan', marker='o') # index += 1 # 绘制散点图 def drawScatter(plt, mydata, size=20, color='blue', mrkr='o'): # print(mydata.T[0][0].tolist()) # print(mydata.T[1][0].tolist()) plt.scatter(mydata.T[0][0].tolist(), mydata.T[1][0].tolist(), s=size, c=color, marker=mrkr) dataMat = file2matrix("/Users/FengZhen/Desktop/accumulate/机器学习/推荐系统/kmeans聚类测试集.txt") # 从文件构建的数据集 dataSet = mat(dataMat).astype(float) # 转换为矩阵形式 k = 4 # 分类数 m = dataSet.shape[0] # 行数 # axis 不设置值,对 m*n 个数求均值,返回一个实数 # axis = 0:压缩行,对各列求均值,返回 1* n 矩阵 # axis =1 :压缩列,对各行求均值,返回 m *1 矩阵 centroid0 = mean(dataSet, axis=0)[0] # 初始化第一个聚类中心:每一列的均值 centList = [centroid0] # 把均值聚类中心加入中心表中 ClustDist = mat(zeros((m, 2))) # 初始化聚类距离表,距离方差,每组数据到中心点的距离 for j in range(m): ClustDist[j, 1] = distEclud(centroid0, dataSet[j, :])**2 while(len(centList) < k): lowestSSE = inf # 初始化最小误差平方和。核心参数,这个值越小说明聚类的效果越好 for i in range(len(centList)): print(i) ptsInCurrCluster = dataSet[nonzero(ClustDist[:, 0].A == i)[0], :] # .A为转换数组 # centroidMat:中心点, splitClustAss:所属分类,距离 centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2) # 计算所有距离和 sseSplit = sum(splitClustAss[:, 1]) # 计算splitClustAss的距离平方和 sseNotSplit = sum(ClustDist[nonzero(ClustDist[:, 0].A != i)[0], 1]) # 计算ClustDist第一列不等于i的距离平方和 if(sseSplit + sseNotSplit) < lowestSSE: bestCentToSplit = i # 确定聚类中心的最优分隔点 bestNewCents = centroidMat # 用新的聚类中心更新最优聚类中心 bestClustAss = splitClustAss.copy() # 用深拷贝聚类距离表为最优聚类距离表 lowestSSE = sseSplit + sseNotSplit # 更新lowestSSE # bestClustAss 赋值为聚类中心的索引 # 第一部分:bestClustAss[bIndx0,0]赋值为聚类中心的索引 bestClustAss[nonzero(bestClustAss[:, 0].A == 1)[0], 0] = len(centList) # 用最优分隔点的指定聚类中心索引 bestClustAss[nonzero(bestClustAss[:, 0].A == 0)[0], 0] = bestCentToSplit # 覆盖:bestNewCents[0, :].tolist()[0]附加到原有聚类中心的bestCentToSplit位置 centList[bestCentToSplit] = bestNewCents[0, :].tolist()[0] # 增加:聚类中心增加一个新的bestNewCents[1, :].tolist()[0]向量 centList.append(bestNewCents[1, :].tolist()[0]) ClustDist[nonzero(ClustDist[:, 0].A == bestCentToSplit)[0], :] = bestClustAss color_cluster(ClustDist[:, 0:1], dataSet, plt) # print("centList:", mat(centList)) drawScatter(plt, mat(centList), size=60, color='red', mrkr='D') plt.show()
效果如下