Kmeans算法的Python實現

Kmeans聚類

kmeans

K-means算法是很典型的基于距離的聚類算法,采用距離作為相似性的評價指標,即認為兩個對象的距離越近,其相似度就越大。該算法認為簇是由距離靠近的對象組成的,因此把得到緊湊且獨立的簇作為最終目標。動圖來源.

k個初始類聚類中心點的選取對聚類結果具有較大的影響,因為在該算法第一步中是隨機的選取任意k個對象作為初始聚類的中心,初始地代表一個簇。該算法在每次迭代中對數據集中剩余的每個對象,根據其與各個簇中心的距離將每個對象重新賦給最近的簇。當考察完所有數據對象后,一次迭代運算完成,新的聚類中心被計算出來。如果在一次迭代前后,J的值沒有發生變化,說明算法已經收斂。

算法步驟:

  • 創建k個點作為起始支點(隨機選擇)
  • 當任意一個簇的分配結果發生改變的時候
  • 對數據集的每個數據點
    • 對每個質心
      • 計算質心與數據點之間的距離
    • 將數據分配到距離其最近的簇
  • 對每一簇,計算簇中所有點的均值并將其均值作為質心

iris

我們用非常著名的iris數據集。

from sklearn import datasets
iris = datasets.load_iris()
X, y = iris.data, iris.target
data = X[:,[1,3]] # 為了便于可視化,只取兩個維度
plt.scatter(data[:,0],data[:,1]);

[圖片上傳失敗...(image-7972e8-1569669245838)]

歐式距離

計算歐式距離,我們需要為每個點找到離其最近的質心,需要用這個輔助函數。

def distance(p1,p2):
    """
    Return Eclud distance between two points.
    p1 = np.array([0,0]), p2 = np.array([1,1]) => 1.414
    """
    tmp = np.sum((p1-p2)**2)
    return np.sqrt(tmp)

distance(np.array([0,0]),np.array([1,1]))
1.4142135623730951

隨機質心

在給定數據范圍內隨機產生k個簇心,作為初始的簇。隨機數都在給定數據的范圍之內dmin + (dmax - dmin) * np.random.rand(k)實現。

def rand_center(data,k):
    """Generate k center within the range of data set."""
    n = data.shape[1] # features
    centroids = np.zeros((k,n)) # init with (0,0)....
    for i in range(n):
        dmin, dmax = np.min(data[:,i]), np.max(data[:,i])
        centroids[:,i] = dmin + (dmax - dmin) * np.random.rand(k)
    return centroids

centroids = rand_center(data,2)
centroids
array([[ 2.15198267,  2.42476808],
       [ 2.77985426,  0.57839675]])

k均值聚類

這個基本的算法只需要明白兩點。

  • 給定一組質心,則簇更新,所有的點被分配到離其最近的質心中。
  • 給定k簇,則質心更新,所有的質心用其簇的均值替換

當簇不在有更新的時候,迭代停止。當然kmeans有個缺點,就是可能陷入局部最小值,有改進的方法,比如二分k均值,當然也可以多計算幾次,去效果好的結果。

def kmeans(data,k=2):
    def _distance(p1,p2):
        """
        Return Eclud distance between two points.
        p1 = np.array([0,0]), p2 = np.array([1,1]) => 1.414
        """
        tmp = np.sum((p1-p2)**2)
        return np.sqrt(tmp)
    def _rand_center(data,k):
        """Generate k center within the range of data set."""
        n = data.shape[1] # features
        centroids = np.zeros((k,n)) # init with (0,0)....
        for i in range(n):
            dmin, dmax = np.min(data[:,i]), np.max(data[:,i])
            centroids[:,i] = dmin + (dmax - dmin) * np.random.rand(k)
        return centroids
    
    def _converged(centroids1, centroids2):
        
        # if centroids not changed, we say 'converged'
         set1 = set([tuple(c) for c in centroids1])
         set2 = set([tuple(c) for c in centroids2])
         return (set1 == set2)
        
    
    n = data.shape[0] # number of entries
    centroids = _rand_center(data,k)
    label = np.zeros(n,dtype=np.int) # track the nearest centroid
    assement = np.zeros(n) # for the assement of our model
    converged = False
    
    while not converged:
        old_centroids = np.copy(centroids)
        for i in range(n):
            # determine the nearest centroid and track it with label
            min_dist, min_index = np.inf, -1
            for j in range(k):
                dist = _distance(data[i],centroids[j])
                if dist < min_dist:
                    min_dist, min_index = dist, j
                    label[i] = j
            assement[i] = _distance(data[i],centroids[label[i]])**2
        
        # update centroid
        for m in range(k):
            centroids[m] = np.mean(data[label==m],axis=0)
        converged = _converged(old_centroids,centroids)    
    return centroids, label, np.sum(assement)

由于算法可能局部收斂的問題,隨機多運行幾次,取最優值

best_assement = np.inf
best_centroids = None
best_label = None

for i in range(10):
    centroids, label, assement = kmeans(data,2)
    if assement < best_assement:
        best_assement = assement
        best_centroids = centroids
        best_label = label

data0 = data[best_label==0]
data1 = data[best_label==1]

如下圖,我們把數據分為兩簇,綠色的點是每個簇的質心,從圖示效果看,聚類效果還不錯。

fig, (ax1,ax2) = plt.subplots(1,2,figsize=(12,5))
ax1.scatter(data[:,0],data[:,1],c='c',s=30,marker='o')
ax2.scatter(data0[:,0],data0[:,1],c='r')
ax2.scatter(data1[:,0],data1[:,1],c='c')
ax2.scatter(centroids[:,0],centroids[:,1],c='b',s=120,marker='o')
plt.show()

[圖片上傳失敗...(image-8a5a3-1569669245838)]

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容