=====================================================================
《机器学习实战》系列博客是博主阅读《机器学习实战》这本书的笔记也包含一些其他python实现的机器学习算法
算法实现均采用python
github 源码同步:https://github.com/Thinkgamer/Machine-Learning-With-Python
=====================================================================
Scikit-learn 实现的K-Means 算法请参考 :点击阅读
K-Means 均值算法请参考:点击阅读
首先二分-K均值是为了解决k-均值的用户自定义输入簇值k所延伸出来的自己判断k数目,其基本思路是:
为了得到k个簇,将所有点的集合分裂成两个簇,从这些簇中选取一个继续分裂,如此下去,直到产生k个簇。
伪代码:
初始化簇表,使之包含由所有的点组成的簇。
repeat
从簇表中取出一个簇。
{对选定的簇进行多次二分试验}
for i=1 to 试验次数 do
使用基本k均值,二分选定的簇。
endfor
从二分试验中选择具有最小误差的两个簇。
将这两个簇添加到簇表中。
until 簇表中包含k个簇
比如要分成5个组,第一次分裂产生2个组,然后从这2个组中选一个目标函数产生的误差比较大的,分裂这个组产生2个,这样加上开始那1个就有3个组了,然后再从这3个组里选一个分裂,产生4个组,重复此过程,产生5个组。这算是一中基本求精的思想。二分k均值不太受初始化的困扰,因为它执行了多次二分试验并选取具有最小误差的试验结果,还因为每步只有两个质心。
优点与缺点
k均值简单并且可以用于各种数据类型,它相当有效,尽管常常多次运行。然后k均值并不适合所有的数据类型。它不能处理非球形簇,不同尺寸和不同密度的簇。对包含离群点(噪声点)的数据进行聚类时,k均值也有问题。
其实现的Python代码如下:
#encoding:utf-8 from numpy import * def loadDataSet(filename): dataMat = [] #创建元祖 fr = open(filename) for line in fr.readlines(): curLine = line.strip().split("\t") fltLine = map(float,curLine) #使用map函数将curLine里的数全部转换为float型 dataMat.append(fltLine) return dataMat def distEclud(vecA,vecB): #计算两个向量的欧式距离 return sqrt(sum(power(vecA-vecB,2))) def randCent(dataSet,k): #位给定数据集构建一个包含k个随机质心的集合 n = shape(dataSet)[1] #shape函数此时返回的是dataSet元祖的列数 centroids = mat(zeros((k,n))) #mat函数创建k行n列的矩阵,centroids存放簇中心 for j in range(n): minJ = min(dataSet[:,j]) #第j列的最小值 rangeJ = float(max(dataSet[:,j]) - minJ) centroids[:,j] = minJ + rangeJ * random.rand(k,1) #random.rand(k,1)产生shape(k,1)的矩阵 return centroids def kMeans(dataSet,k,disMeas = distEclud,createCent = randCent): m = shape(dataSet)[0] #shape函数此时返回的是dataSet元祖的行数 clusterAssment = mat(zeros((m,2))) #创建一个m行2列的矩阵,第一列存放索引值,第二列存放误差,误差用来评价聚类效果 centroids = createCent(dataSet,k) #创建k个质心,调用createCent()函数 clusterChanged =True #标志变量,若为true则继续迭代 print "质心位置更新过程变化:" while clusterChanged: clusterChanged = False for i in range(m): minDist = inf #inf为正无穷大 minIndex = -1 #创建索引 for j in range(k): #寻找最近的质心 disJI = disMeas(centroids[j,:],dataSet[i,:]) #计算每个点到质心的欧氏距离 if disJI< mindist="disJI" minindex="j" if="" clusterassment="" i="" minindex:="" clusterchanged="True" :="" print="" centroids="" for="" cent="" in="" range="" k="" ptsinclust="dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]#通过数组过滤来获得给定簇的所有点" nonzero="" a="" eg="" b2="array([[True," false="" true="">(array([0, 0, 1]), array([0, 2, 0])) #print array(nonzero(b2)) #=>[[0, 0, 1],[0, 2, 0]] centroids[cent,:] = mean(ptsInClust,axis=0) #计算所有点的均值,选项axis=0表示沿矩阵的列方向进行均值计算 return centroids,clusterAssment #返回所有的类质心与点分配结果 def bikMeans(dataSet,k,disMeas = distEclud): m = shape(dataSet)[0] #shape函数此时返回的是dataSet元祖的行数 clusterAssment = mat(zeros((m,2))) #创建一个m行2列的矩阵,第一列存放索引值,第二列存放误差,误差用来评价聚类效果 #创建一个初始簇 centroid0 = mean(dataSet,axis=0).tolist()[0] centList = [centroid0] print centList print len(centList) for j in range(m): clusterAssment[j,1] = disMeas(mat(centroid0),dataSet[j,:])**2 #计算所有点的均值,选项axis=0表示沿矩阵的列方向进行均值计算 while (len(centList)<k): lowestSSE = inf #inf正无穷大 for i in range(len(centList)): #尝试划分每一簇 ptsInCurrCluster = dataSet[nonzero(clusterAssment[:,0].A==i)[0],:] centroidMat,splitClustAss = kMeans(ptsInCurrCluster,2,disMeas) sseSplit = sum(splitClustAss[:,1]) sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1]) print "sseSplit and notSplit:",sseSplit,sseNotSplit if (sseSplit + sseNotSplit)<lowestSSE: bestCentToSplit = i bestNewCents = centroidMat bestClustAss = splitClustAss.copy() lowestSSE = sseSplit + sseNotSplit #更新簇的分配结果 bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList) bestClustAss[nonzero(bestClustAss[:,0].A == 0)[0],0] = bestCentToSplit print "the bestCentToSplit is :",bestCentToSplit print "the len of bestClustAss is:",len(bestClustAss) centList[bestCentToSplit] = bestNewCents[0,:] centList.append(bestNewCents[1,:]) clusterAssment[nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:] =bestClustAss return centList,clusterAssment #return mat(centList),clusterAssment datMat = mat(loadDataSet('data.txt')) myCentList,myNewAssment = bikMeans(datMat,2) print "最终质心:\n",myCentList print "索引值和均值:\n",myNewAssment >