機器學習筆記之Kmeans演算法

birdlove1987發表於2017-06-13

Kmeans演算法:

優點:容易實現

缺點:可能收斂到區域性最小值,在大規模資料集上的收斂速度較慢。

適用資料型別:數值型資料


演算法原理:

k-means 演算法接受引數 k ;然後將事先輸入的n個資料物件劃分為 k個聚類以便使得所獲得的聚類滿足:同一聚類中的物件相似度較高;而不同聚類中的物件相似度較小。聚類相似度是利用各聚類中物件的均值所獲得一個“中心物件”(引力中心)來進行計算的。


演算法流程:

收集資料:需要聚類的資料集。
準備資料:需要數值新資料來計算距離,也可以將標稱型資料對映為二值型資料再用於距離計算。
分析資料:散點圖,視覺化圖。
訓練演算法:無監督演算法無此過程。
測試演算法:使用Kmeans演算法進行聚類,觀測結果。
使用演算法:可適用於任何聚類應用場景。

首先我們先給出一個演示演算法的資料集,為了能直觀的觀測資料,我們使用散點圖進行顯示



PS:感覺未經過訓練的人類也能將這個資料集分成四類。。是不是有點太明顯了。。

然後我們來使用我們的Kmean演算法

首先我們要建立一個計算的程式

def distEclud(vecA, vecB):
    return sqrt(sum(power(vecA - vecB, 2))) #計算兩點間距離

然後我們要在建立一個構建質心的程式

def randCent(dataSet, k):
    n = shape(dataSet)[1]          #一條資料的維度
    centroids = mat(zeros((k,n)))  #初始化質心矩陣
    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

下面就可以編寫Kmeans程式了

def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):    #計算距離和建立質心都以函式引用傳入
    m = shape(dataSet)[0]
    clusterAssment = mat(zeros((m,2)))     #資料分類分析矩陣
                                           
    centroids = createCent(dataSet, k)     #儲存資料聚類的結果
    clusterChanged = True                  #通過while迴圈更新質心,當資料分類不在變動就退出迴圈
    while clusterChanged:
        clusterChanged = False
        for i in range(m):                    #資料開始進行歸類
            minDist = inf; minIndex = -1
            for j in range(k):
                distJI = distMeas(centroids[j,:],dataSet[i,:])
                if distJI < minDist:
                    minDist = distJI; minIndex = j                      #資料歸類
            if clusterAssment[i,0] != minIndex: clusterChanged = True   #判斷資料分類是否不在變動
            clusterAssment[i,:] = minIndex,minDist**2
        print centroids
        for cent in range(k):                                           #根據新的資料變動更新質心
            ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]
            centroids[cent,:] = mean(ptsInClust, axis=0) 
    return centroids, clusterAssment

下面我們就可以使用Kmean來對我們的資料進行分類啦:

# -*- coding: utf-8 -*-
import kMeans as km
from numpy import *
import matplotlib.pyplot as plt
datMat = mat(km.loadDataSet('DataSet.txt'))      #匯入資料
myCentroids , clustAssing = km.kMeans(datMat,4)  #進行kmeans計算
plt.scatter(myCentroids[:, 0], myCentroids[:, 1], s=300, marker='o', color='r') #顯示質心
plt.scatter(datMat[:,0],datMat[:,1]) #顯示資料點
plt.show()

結果如下圖,為了方便大家理解Kmeans的計算過程,我標記了三次質心迭代的過程,並使用箭頭指示了質心移動的方向。



是不是很有趣,其實Kmeans還能做更有趣的事情呢,例如,影象內容分割。。。

先讓大家看看我們使用的兩張原圖:





這兩張圖中第一張藍胖子色彩比較簡單,第二張多色彩圖色彩比較富於變化。下面我們使用Kmeans來試試看

-*- coding: utf-8 -*-
import numpy as np
import PIL.Image as image
from sklearn.cluster import KMeans

#將圖示轉行成一個三通道矩陣,並進行歸一化
def convImage(file_path):
    f = open(file_path, 'rb')      
    data = []
    img = image.open(f)  
    m, n = img.size  
    for i in range(m):
        for j in range(n): 
            x, y, z = img.getpixel((i,j))
            data.append([x/256.0, y/256.0, z/256.0])
    f.close()
    return np.mat(data), m, n  
	
	
#處理圖片
imageData, row, col = convImage('rawImage.png')

k = 5  #聚類個數

result = KMeans(clusters=k).fit_predict(imageData)      

result = result.reshape([row, col])     

#重新繪製結果圖片
resultImage = image.new("L",(row, col))
  
for i in range(row):    
    for j in range(col):
        resultImage.putpixel((i, j), int(256/(result[i][j]+1)))

#儲存圖片
resultImage.save('resultImage.png')

對於第一張藍胖子的圖片,我分別 領k =2 和k=7


看出什麼來了嗎?再看看第二張,我分別領k=2到12。。。


看出來了麼,其實聚類的k的數量越接近原圖色彩的種類和原圖越像,不過。。好像沒什麼卵用。。沒有原圖好看。。。(作為一個頂級帥哥,我是外貌協會啦!!)

相關文章