要求:
隨機生成x,y均在[0,10]範圍內的10個點,k=2,訓練一個簡單的k-means模型。
K均值算法步驟如下:
1.在訓練樣本點中隨機初始化[0,10]範圍內的k個樣本點作爲k個簇各自的中心;
2.遍歷一遍所有樣本點,將每一個樣本點分配到最近的簇中心,得到clusterDict。clusterDict的鍵爲centroidList的下標,鍵值爲屬於該類的所有樣本點。
3.計算第一次聚類迭代得到的結果的代價函數,即每一個樣本點到其簇中心的距離的平方和newVar。令oldVar = -1
4.開始迭代,直到代價函數收斂(newVar-oldVar<=0.00001):
*對於計算每一類樣本的均值,作爲新的簇中心
*遍歷一遍所有樣本點,將每一個樣本點分配到最近的簇中心。
import numpy as np
import matplotlib.pyplot as plt
def getCentroids(clusterDict):
# 得到k個質心
centroidList = []
for key in clusterDict.keys():
centroid = np.mean(clusterDict[key], axis=0) # 計算每列的均值,即找到質心
centroidList.append(centroid)
return np.array(centroidList).tolist()
def getVar(clusterDict,centroidList):
# 計算簇集合間的均方誤差
# 將簇類中各個向量與質心的距離進行累加求和
sum = 0.0
for key in clusterDict.keys():
vec1 = centroidList[key]
distance = 0.0
for item in clusterDict[key]:
vec2 = item
distance += calcuDistance(vec1, vec2)
sum += distance
return sum
def calcuDistance(vec1,vec2):
return np.sqrt(np.sum(np.square(vec1-vec2)))
def MinDistance(dataSet,centroidList):
clusterDict = {}
for i in range(len(dataSet)):
flag = 0
min_dis = float("inf")
for j in range(len(centroidList)):
distance = calcuDistance(dataSet[i],centroidList[j])
if min_dis>distance:
flag = j
min_dis = distance
if flag not in clusterDict.keys():
clusterDict[flag] = list()
clusterDict[flag].append(dataSet[i])
return clusterDict
def minDistance(dataSet, centroidList):
# 對每個屬於dataSet的item,計算item與centroidList中k個質心的歐式距離,找出距離最小的,
# 並將item加入相應的簇類中
clusterDict = dict() # 用dict來保存簇類結果
for item in dataSet:
vec1 = np.array(item) # 轉換成array形式
flag = 0 # 簇分類標記,記錄與相應簇距離最近的那個簇
minDis = float("inf") # 初始化爲最大值
for i in range(len(centroidList)):
vec2 = np.array(centroidList[i])
distance = calcuDistance(vec1, vec2) # 計算相應的歐式距離
if distance < minDis:
minDis = distance
flag = i # 循環結束時,flag保存的是與當前item距離最近的那個簇標記
if flag not in clusterDict.keys(): # 簇標記不存在,進行初始化
clusterDict[flag] = list()
clusterDict[flag].append(item) # 加入相應的類別中
return clusterDict # 返回新的聚類結果
def showCluster(centroidList, clusterDict):
# 展示聚類結果
colorMark = ['or', 'ob', 'og', 'ok', 'oy', 'ow'] # 不同簇類的標記 'or' --> 'o'代表圓,'r'代表red,'b':blue
centroidMark = ['dr', 'db', 'dg', 'dk', 'dy', 'dw'] # 質心標記 同上'd'代表棱形
for key in clusterDict.keys():
plt.plot(centroidList[key][0], centroidList[key][1], centroidMark[key], markersize=10) # 畫質心點
for item in clusterDict[key]:
plt.plot(item[0], item[1], colorMark[key]) # 畫簇類下的點
plt.show()
if __name__ == '__main__':
k = 2 #聚類種數
dataSet = 10*np.random.random((10,2))
centroidList = 10*np.random.random((k,2))
clusterDict = MinDistance(dataSet,centroidList)
newVar = getVar(clusterDict, centroidList)
oldVar = -1 # 舊均方誤差值初始化爲-1
# showCluster(centroidList, clusterDict) # 展示聚類結果
k = 2
while abs(newVar - oldVar) >= 0.000001: # 當連續兩次聚類結果小於0.0001時,迭代結束
centroidList = getCentroids(clusterDict) # 獲得新的質心
clusterDict = MinDistance(dataSet, centroidList) # 新的聚類結果
oldVar = newVar
newVar = getVar(clusterDict, centroidList)
print('***** 第%d次迭代 *****' % k)
print('簇類')
for key in clusterDict.keys():
print(key, ' --> ', clusterDict[key])
print('k個均值向量: ', centroidList)
print('平均均方誤差: ', newVar)
k += 1
showCluster(centroidList,clusterDict) # 展示聚類結果