人工智障學習筆記——機器學習(8)K均值聚類

一.概念

K均值聚類(K-means)是硬聚類算法,是典型的基於原型的目標函數聚類方法的代表,它是數據點到原型的某種距離作爲優化的目標函數,利用函數求極值的方法得到迭代運算的調整規則。K-means算法以歐式距離作爲相似度測度,它是求對應某一初始聚類中心向量V最優分類,使得評價指標J最小。算法採用誤差平方和準則函數作爲聚類準則函數。


二.算法
1)從數據中隨機選取K組數據作爲質心centroids
2)對剩餘的數據依次計算其到每個質心的距離,並把它歸到最近的質心的類
3)重新計算已經得到的各個類的質心
4)迭代2~3步直至新的質心與原質心相等或小於指定閾值,算法結束


三.實現

# -*- coding: utf-8 -*-  
from numpy import *
import time
import matplotlib.pyplot as plt


#隨機200個座標點 (-10~10)
def getdataSet():
    dataSet=mat(zeros((100, 2)))
    numSamples = dataSet.shape[0]  
    offset=[[-10,-10],[-10,10],[10,-10],[10,10]]#分類
    for i in range(numSamples):
        dataSet[i] =random.uniform(-5,5)+offset[i%4][0],random.uniform(-5,5)+offset[i%4][1]#偏移
       
    return dataSet


# 歐式距離
def euclDistance(vector1, vector2):
	return sqrt(sum(power(vector2 - vector1, 2)))

# 總誤差
def getcost(clusterAssment):  
    len = clusterAssment.shape[0]  
    Sum = 0.0  
    for i in range(len):  
        Sum = Sum + clusterAssment[i, 1]  
    return Sum 

# 用隨機樣本初始化centroids  
def initCentroids(dataSet, k):  
    numSamples, dim = dataSet.shape  
    centroids = zeros((k + 1, dim))    
    s = set()  
    for i in range(1, k + 1):  
        while True:  
            index = int(random.uniform(0, numSamples))  
            #隨機數去重
            if index not in s:  
                s.add(index)  
                break        
        centroids[i, :] = dataSet[index, :]  
    return centroids

# k-means主算法
def kmeans(dataSet, k):
    numSamples = dataSet.shape[0]

    # 第一列存這個樣本點屬於哪個簇
    # 第二列存這個樣本點和樣本中心的誤差
    clusterAssment = mat(zeros((numSamples, 2)))
    for i in range(numSamples):
        clusterAssment[i, 0] = -1
    clusterChanged = True

    # step 1: 初始化centroids
    centroids = initCentroids(dataSet, k)

    # 如果收斂完畢,則clusterChanged爲False
    while clusterChanged:
        clusterChanged = False
        # 對於每個樣本點
        for i in range(numSamples):
            minDist = 0xfffff
            minIndex = 0
            # 對於每個樣本中心
            # step 2: 找到最近的樣本中心
            for j in range(1, k + 1):
                distance = euclDistance(centroids[j, :], dataSet[i, :])
                if distance < minDist:
                    minDist = distance
                    minIndex = j

            # step 3: 更新樣本點與中心點的分配關係
            if clusterAssment[i, 0] != minIndex:
                clusterChanged = True
                clusterAssment[i, :] = minIndex, minDist
            else:
                clusterAssment[i, 1] = minDist

        # step 4: 更新樣本中心     
        for j in range(1, k + 1):
            pointsInCluster = dataSet[nonzero(clusterAssment[:, 0].A == j)[0]]
            centroids[j, :] = mean(pointsInCluster, axis=0)
    return centroids, clusterAssment

x=[1, 2, 3, 4, 5, 6, 7, 8]  
y=[0, 0, 0, 0, 0, 0, 0, 0]

dataSet =getdataSet()
print(dataSet)

plt.figure('k-means',figsize=(12, 6)) 
for index in range(8):      
    k = x[index] 
    centroids, clusterAssment = kmeans(dataSet, k)       
    #print(clusterAssment)
    y[index]=getcost(clusterAssment)
    print("x: ",k," y: ",y[index])
    plt.subplot(2, 4, index+1,facecolor=(0.5,0.5,0.5))   
    numSamples, dim = dataSet.shape
    mark = ['or', 'og', 'ob', 'oc', 'om', 'oy', 'ok', 'ow']
    for i in range(numSamples):
        markIndex = int(clusterAssment[i, 0])
        plt.plot(dataSet[i, 0], dataSet[i, 1], mark[markIndex-1])
    mark = ['*r', '*g', '*b', '*c', '*m', '*y', '*k', '*w']
    for i in range(1,k+1):
        plt.plot(centroids[i, 0], centroids[i, 1], mark[i-1], markersize = 12)
    plt.subplots_adjust(wspace=0.4, hspace=0.4) 
    plt.xticks(fontsize=10, color="darkorange")  
    plt.yticks(fontsize=10, color="darkorange") 
    plt.title(index+1) 
plt.figure('K enum',figsize=(6, 3)) 
plt.plot(x, y, "b--", linewidth=2)  
plt.show()
  



這裏在getdataSet隨機數據點中加入了偏移量影響,可以看出數據分佈大概在四角方向,顯然K=4效果應最好。

通過程序輸出的不同K值誤差(clusterAssment)總和也可以看出,當K>4後分類的增多確實已經不會再對誤差產生太大的優化了,反而看起來亂亂的。

故合理的確定K值對於聚類效果的好壞有很大的影響。


四.K-Means ++ 算法

除了要確定K值外,Kmeans需要人爲地確定初始聚類中心,不同的初始聚類中心可能導致完全不同的聚類結果。故而衍生了 k-means++算法。

 k-means++算法選擇初始聚類中心的基本思想就是:初始的聚類中心之間的相互距離要儘可能的遠。

1.從輸入的數據點集合中隨機選擇一個點作爲第一個聚類中心
2.對於數據集中的每一個點x,計算它與最近聚類中心(指已選擇的聚類中心)的距離D(x)
3.選擇一個新的數據點作爲新的聚類中心,選擇的原則是:D(x)較大的點,被選取作爲聚類中心的概率較大
4.重複2和3直到k個聚類中心被選出來
利用這k個初始的聚類中心來運行標準的k-means算法

從上面的算法描述上可以看到,算法的關鍵是第3步,如何將D(x)反映到點被選擇的概率上,一種算法如下:
先從我們的數據庫隨機挑個隨機點當“種子點”
對於每個點,我們都計算其和最近的一個“種子點”的距離D(x)並保存在一個數組裏,然後把這些距離加起來得到Sum(D(x))。
然後,再取一個隨機值,用權重的方式來取計算下一個“種子點”。這個算法的實現是,先取一個能落在Sum(D(x))中的隨機值Random,然後用Random -= D(x),直到其<=0,此時的點就是下一個“種子點”。
重複2和3直到k個聚類中心被選出來
利用這k個初始的聚類中心來運行標準的k-means算法
可以看到算法的第三步選取新中心的方法,這樣就能保證距離D(x)較大的點,會被選出來作爲聚類中心了。

代碼可以參考http://rosettacode.org/wiki/K-means%2B%2B_clustering


五.總結

 k-means算法比較簡單,和之前說過的K近鄰算法(KNN)http://blog.csdn.net/sm9sun/article/details/78521927有些類似,本質上都是給定一個點,然後在數據集中找鄰近的點。但又有些區別:

首先KNN是分類算法,而K-means是聚類算法,KNN是監督學習,而K-means是非監督學習。也就是說,KNN拿到的數據集是已經是有分類的數據了,而K-means拿到的數據是無分類的,經過聚類後纔有了類的標誌。且K的定義也完全不一樣,K-means的K值是需要提前指定的,通過上述代碼也可以看出,K的選取至關重要。但在實際中這個 K 值的選定是非常難以估計的,很多時候,事先並不知道給定的數據集應該分成多少個類別才最合適。且K-means應用侷限性非常大,聚類的算法公式還需實際情況而變。


六.相關學習資源

http://blog.csdn.net/loadstar_kun/article/details/39450615

http://blog.csdn.net/eventqueue/article/details/73133617

http://blog.csdn.net/zouxy09/article/details/17589329




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章