K-Means(k-均值)聚類算法

k-Means 算法

聚類是一種無監督的學習,它將相似的對像歸到一個簇中,將不相似對象歸到不同類中,相似這一概念取決於所選擇的相似度計算方法。
k-Means 是發現給定數據集的k個簇的聚類算法,之所以稱之爲k-均值,是因爲它可以發現K個不同的簇,且每個簇的中心所含值的均值計算而成。簇的個數k是用戶指定,每一個簇通過質心(centroid),即簇中所有點的中心來描述,聚類與分類的算法的最大區別在於,分類的目標類別是已知的,而聚類的目標類別是未知的。

k-Means 術語
簇:所有數據點的集合,簇中的對象是相似的。
質心:簇中所有的中心(計算所有值的均值而來)
SSE:Sum of Square Error (平方誤差和),SSE 值越小,表示越接近他們的質心,由於對誤差取了平方,因此更加註重那些遠離中心的點。

K -Means 工作流程

  1. 首先,隨機確定k個初始點作爲質心(通常是隨機選擇)
  2. 然後將數據集中的每一個點分配到一個簇中,具體來講,就是爲每個點找到距離其最近的質心,並將其分配該質心所對應的簇中,之後每個簇的質心更新爲該簇中所有點的均值。

從文件加載數據集

def loadDataSet(fileName):#通用函數,用來解析以tab鍵分隔的floats
	dataMat = []
	fr=open(fileName)
	for line in  fr.readlines():
		curLine = line.strip().split('\t')
		fltLine =map(float,curLine)#映射所有元素爲float類型
		dataMat.append(fltLine)
	return dataMat

計算兩個向量的歐式距離

def distEclud(vecA,vecB):
	return sqrt(sum(power(vecA-vecB,2)))

構建一個包含k個隨機質心的集合

爲給定數據集構造一個包含k個隨機質心的集合,隨機質心必須在整個數據集邊界之內,這可以通過找到數據集每一維的最小值和最大值來完成,然後生成0~1.0之間的隨機數通過取值範圍和最小值,以便確保隨機點在數據的邊界之內。

def randCent(dataSet,k):
	n=shape(dataSet)[1]
	centroids = mat(zeros((k,n))) #創建k個質心矩陣
	for j in range(n):  #創建 隨機質心,並且保證在每一維的邊界內
		minJ=min(dataSet[:,j])
		rangeJ = float(max(dataSet[:,j])-minJ)
		centroids[:,j] = mat(minJ+rangeJ*random.rand(k,1))
		return centroids

注:numpy.random.rand(d0, d1, …, dn)的隨機樣本位於[0, 1)中

arr = np.random.rand(2,4)
print(arr)

[[ 0.19947349  0.05282713  0.56704222  0.45479972]
 [ 0.28827103  0.1643551   0.30486786  0.56386943]]

k-Means聚類算法

該算法會創建k個質心,然後將每個點分配到最近的質心,再重新計算質心。這個過程會重複數次,直到數據點的簇分配結果不再改變位置,

def kMeans(dataSet,k,distMeas=distEculd,createCent=randCent):
	m=shape(dataSet)[0]
	clusterAssment=mat(zeros((m,2)))#保存簇的分配結果
	centroids =createCent(dataSet,k)
	clusterChange=True
	while clusterChange:
		clusterChange = False
		for i in range(m): #循環每一個數據點並分配到最近的質心中去
			minDist = inf;minIndex=-1
			for j in range(k):
				dist =distMeas(dataSet[i,:],centroids[j,:])#計算每一個數據點到質心的距離
				if dist<minDist: #更新minDist 和 minIndex
					minDist =dist;minIndex =j
			if clusterAssment[i,:]!=minIndex:
				clusterAssment[i,:]=minIndex,minDist
		print(centroids)
		for cent in range(k):#更新質點
			ptsInClust =dataSet[nonzero(clusterAssment[:,0].A==cent)[0]] #獲取該簇中的所有點
			centroids[cent,:]=mean(ptsInClust,axis=0)#將質心修改爲簇中所有點的平均值
	return  centroids,clusterAssment

k-Means 聚類算法的缺陷

在k-Means的函數測試中可能偶爾會陷入局部最小值(局部最優的結果,但不是全局最優的結果),爲了克服這種情況,提出二分k-均值算法(bisecting K-Means)

二分k -Means 聚類算法

該算法首先將所有點作爲一個簇,然後將該簇一分爲二,之後選擇其中一個簇繼續進行劃分,選擇哪一個簇進行劃分取決於對其劃分時可以
最大程度降低SSE(平方和誤差)的值。劃分過程不斷重複,直到得到用戶指定的簇數目爲止。第二種方法,選擇SSE最大的簇進行劃分。

二分k-Means 聚類算法

def biKMeans(dataSet,k,distMeas=distEclud):
	m=shape(dataSet)[0]
	clusterAssment=mat(zeros((m,2)))#保存每一個數據點的簇分配結果和最小平方誤差和
	centroid0=mean(dataSet,axis =0).tolist()[0]   #質心初始化爲所有數據點的均值
	centList = [centroid0] #初始化只有1個質心的list
	for j in range(m):
		clusterAssment[j,1]=distMeas(mat(centroid0),dataSet[j,:])**2
	while (len(centList)<k):  #當質心數量小於k
		lowestSSE=inf
		for i in rang(len(centList)): #遍歷每一個質心
			ptsInCurrCluster = dataSet[nonzero(clusterAssment[:,0].A==i)[0],:]#獲取當前數據集下屬於簇i的所有數據點
			centroidMat ,splitClustAss =kMeans(ptsInCurrCluster,2,distMeas)  # 將當前簇 i 進行二分 kMeans 處理
			sseSplit = sum(splitClustAss[:,1]) #將二分的kmeans結果中的平方和的距離進行求和
			sseNoSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1])
			if(sseSplit+sseNoSplit)<lowestSSE:  #總的誤差和越小,越相似,效果越優越,劃分的結果越好
				bestNewCents=centroidMat
				bestClustAss=splitClustAss.copy
				lowestSSE=sseSplit+sseNoSplit
		#找出最好的簇分配結果
		bestClustAss[nonzero(bestClustAss[:,0].A==1)[0],0]=len(centList)  # 調用二分 kMeans 的結果,默認簇是 0,1. 
		# 當然也可以改成其它的數字
		bestClustAss[nonzero(bestClustAss[:,0].A == 0)[0], 0] = bestCentToSplit  # 更新爲最佳質心
		#更新質心列表
		centList[bestCentToSplit]=bestNewCents[0,:].tolist()[0]# 更新原質心 list 中的第 i個質心爲使用二分 kMeans 
		# 後 bestNewCents 的第一個質心
		centList.append(bestNewCents[1,:].tolist()[0]) # 添加 bestNewCents 的第二個質心
		clusterAssment[nonzero(clusterAssment[:,0].A==bestCentToSplit)[0],:]=bestClustAss
		return mat(centList),clusterAssment
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章