圖像聚類 K-means算法實現

0、圖像聚類

0.1 什麼是圖像聚類?

聚類是一種運用普遍的探索性數據分析技術,直觀上講,聚類是將對象進行分組的一項任務,使類似的對象歸爲一類,不類似的對象歸爲不一樣類中。html

當聚類對象是圖像的時候,就是所謂的圖像聚類。git

更爲詳細的介紹,可參考文後的參考資料[1]。程序員

圖像聚類就是在給出的圖像集合中,根據圖像的內容,在無先驗知識的狀況下,將圖像按照類似度進行分類。使得分類後的圖像類內類似度高,類間類似度低。中國有句俗話叫作「物以類聚,人以羣分」,大概就是這個意思吧。github

0.2 聚類算法的分類

前面講到聚類是將類似度高的對象聚到一類中,如何來衡量對象之間的類似度是個關鍵問題。算法

按照聚類的尺度,聚類方法能夠被分爲如下三種:數組

  • 基於距離的聚類算法: 使用各類各樣的距離來衡量數據對象之間的類似度
  • 基於密度的聚類算法: 依據合適的密度函數來進行分類
  • 基於互連性的聚類算法: 一般基於圖或超圖模型,將高度連通的對象聚爲一類。

下面的部分主要介紹K-means聚類方法。bash

一、K-means聚類算法

K-means算法是一種基於距離的聚類算法,也叫作K均值K平均,也常常被稱爲勞埃德(Lloyd)算法。是經過迭代的方式將數據集中的各個點劃分到距離它最近的簇內,距離指的是數據點到簇中心的距離。dom

1.1 K-means基本原理

K-means算法的思想很簡單,對於給定的樣本集,按照樣本之間的距離大小,將樣本劃分爲K個簇。將簇內的數據儘可能緊密的連在一塊兒,而讓簇間的距離儘可能的大。ide

Kmeans步驟函數

  1. 隨機初始化k個簇中心座標
  2. 計算數據集內全部對象到k個簇中心的距離,並將數據點劃分到最近的簇
  3. 對每個簇,從新計算該簇的質心,爲當前簇內節點的座標平均值
  4. 重複第2,3步直到收斂

終止條件有:

  • 再也不有從新的分配
  • 達到最大迭代次數
  • 全部類中心移動小於某個值

K-means問題

  1. 貪心算法: 常常陷入局部最優解

    • 類中心個數K的選取
    • 初始點選取
  2. 對噪聲或離羣點比較敏感

    沒法區分出哪些是噪聲或者離羣點,只能給每一個數據點都判斷出一個類別來,這樣會致使樣本質心偏移,致使誤判或者聚類緊密程度下降。

  3. 樣本點形狀對聚類的影響

    K-means算法對於凸性數據具備良好的效果,可以根據聚類來將數據分爲球狀類的簇,但對於非凸形狀的數據點就無能爲力了,好比環形數據等等。以下圖左邊是K-means方法的聚類效果。

1.2 K-means中的參數

1. K值

聚類中心的個數K須要事先給定。K值的選取直接影響最終的聚類效果。

選取方法:

  1. elbow method經過繪製K和損失函數的關係圖,選擇拐點處的K值。即便用多個值進行嘗試,取聚類指標最優或提高的轉折點。
  2. 經驗選取。根據人工經驗先定幾個K,屢次隨機初始化中心選經驗上最合適的。

一般是根據經驗選取,由於實際操做中拐點不明顯,且效率過低,實際中不容許這樣作。

elbow method也叫作「手肘法」,是利用偏差平方和(SSE)的變化趨勢來做爲選取K值的指標。

SSE = \Sigma_{i=1}^{k} \Sigma_{p\in C_i}|p-m_i|^2

其中,C_i是第i個簇,pC_i中的樣本點,m_iC_i的質心,SSE是全部樣本的聚類偏差,表示聚類效果的好壞。

以下圖所示,當K取值爲2~7時,對應的聚類結果,當K=5時的效果最好。

2. 初始聚類中心(質心)

K-means選擇的初始點不一樣得到的最終分類結果也可能不一樣。在實際使用中,咱們並不知道待聚類的數據集中哪些是咱們關注的label,人工事先指定質心是不現實的。

通常初始質心的選擇方法是:

  • 隨機選擇
  • Kmeans++方式
    • 第一個類中心->隨機選取
    • D(x)爲數據點x距最近的聚類中心的距離
    • 選取下一個聚類中心,選取的機率正比於D(x)^2
    • 以此類推,到第K個。

3. 距離度量

距離是K-means中衡量簇內樣本點類似度的指標。

K-means中比較經常使用的距離度量方法是:

  • 歐幾里得距離
  • 餘弦類似度
  • 曼哈頓距離

二、K-means實現之sklearn

Python中的sklearn庫中提供了K-means聚類的實現方法,咱們能夠直接調用。

對於圖像聚類來說,咱們須要提取表示圖像內容的特徵x_ix_id維的特徵向量。具備N個圖像,其特徵向量表示爲X=(x_1, x_2, x_3,....x_n),維度爲(n, d)

示例:

from sklearn.cluster import KMeans
import numpy as np

# X表示一組圖像的特徵向量
X = np.array([[1, 2], [1, 4], [1, 0],
           [4, 2], [4, 4], [4, 0]])
kmeans = KMeans(n_clusters=2, random_state=0).fit(X)
kmeans.labels_
# array([0, 0, 0, 1, 1, 1], dtype=int32)
kmeans.predict([[0, 0], [4, 4]])
# array([0, 1], dtype=int32)
kmeans.cluster_centers_
# array([[ 1., 2.],
# [ 4., 2.]])
複製代碼

2.1 KMeans類

class sklearn.cluster.KMeans(n_clusters=8, init='k-means++', n_init=10, max_iter=300, tol=0.0001, precompute_distances='auto', verbose=0, random_state=None, copy_x=True, n_jobs=1, algorithm='auto')
複製代碼

參數:

參數 含義
n_clsuters int, 可選,默認值爲8。聚類中心的個數,即聚類的類數
init {‘k-means++’, 'random'或一個ndarray},初始化質心的方法,默認是'k-means++'‘random’隨機從訓練數據中選初始質心,若是傳遞一個ndarray,應該行如(n_clusters, n_features)並給出初始質心
n_init int,默認10,用不一樣質心初始化運行算法的次數,最終解是在inertia意義下選出的最優結果
max_iter int,默認300,執行一次K-means算法的最大迭代次數
tol float型,默認0.0001
precompute_distances {auto, True, False}預先計算距離值(更快,但佔用更多內存),對一組數據只運行較少次聚類結果時,不須要預選計算。
verbose int型,默認0,是否打印中間過程,0是不打印
random_state int型,RandomState的實例或None,可選,默認None。若是是intrandom_state是隨機數生成器使用的種子,若是是RandomState實例,random_state是隨機數生成器,若是是None,隨機數生成器是由np.randomRandomState實例
n_jobs int型,使用的計算力的數量,經過計算並行運行的每一個n_init來實現。若是是-1,則全部CPU所有使用,若是指定爲1,則不使用並行代碼,方便調試。該值小於-1,則使用 (n_cpus + 1 + n_jobs) . 對於n_jobs = -2, 使用n_cpus-1.
algorithm 可選值'auto', 'full','elkan'。'full'是傳統的K-means算法,'elkan'是elkan K-means算法,默認值‘auto’會根據數據值是否稀疏,來決定如何選擇'full'和'elkan'。通常,數據稠密選‘elkan’,不然就是'full'。

主要屬性:

屬性 含義
cluster_centers_ 向量[n_clsuters, n_features],每一個簇中心的座標
Labels_ 每一個數據的分類標籤,從0開始
inertia_ float型,每一個數據點到其簇的質心的距離之和,用來評估簇的個數是否合適

2.2 KMeans類方法

1. fit()

對Kmeans肯定類別之後的數據集進行聚類.

定義:

def fit(self, X, y=None)
    random_state = check_random_state(self.random_state)
    X = self._check_fit_data(X)

    self.cluster_centers_, self.labels_, self.inertia_, self.n_iter_ = \
        k_means(
            X, n_clusters=self.n_clusters, init=self.init,
            n_init=self.n_init, max_iter=self.max_iter, verbose=self.verbose,
            precompute_distances=self.precompute_distances,
            tol=self.tol, random_state=random_state, copy_x=self.copy_x,
            n_jobs=self.n_jobs, algorithm=self.algorithm,
            return_n_iter=True)
    return self
複製代碼

內部調用k_means函數進行聚類,返回self

調用k_means()函數,會返回self.cluster_centers_,self.labels_, self.inertia_, self.n_iter_

  • self.cluster_centers_:聚類中心,shape爲 (k, n_features)
  • self.labels_int,聚類索引值,shape爲(n_samples,)
  • self.inertia_:聚類失真值(訓練集中全部觀測到的距離的平方之和)
  • self.n_iter_:最佳結果對應的迭代次數,只有當 return_n_iter 設爲True時返回。

2. predict()

根據聚類結果,肯定所屬類別

def predict(self, X)
複製代碼
  • X:{array, sparse matrix},shape是[n_samples, n_features] 返回值:
  • labelsarray, shape是[n_samples,]。每一個樣例屬於聚類的類別索引。

3. fit_predict

def fit_predict(self, X, y=None)
    return self.fit(X).labels_
複製代碼

返回值:

  • labelsarray, shape是[n_samples,]。每一個樣例屬於聚類的類別索引值。

計算聚類中,並預測每一個sample的聚類索引。

等效於,調用fit(X)方法以後,調用predict(X)函數。

注意:在此函數中,返回的是self.fit(X).labels_屬性。

4. transform

def transform(self, X)
複製代碼

將X轉化爲聚類-距離空間

返回值:

  • X_newarray, shape是[n_samples, k]

5. fit_transform

def fit_transform(self, X, y=None)
複製代碼

進行聚類運算,並將X轉化到距離空間。

等效於,調用fit(X)方法以後,調用transform(X)函數,可是更爲有效。

重要,sklearn中的Kmeans方法沒法指定距離度量方法,默認使用歐式距離

K-means默認使用的是歐式距離,這是算法設計之初的度量基礎。緣由是涉及平均值的計算。

來自: 聚類分析 - sklearn的kmeans使用的是哪一種距離度量? - IT屋-程序員軟件開發技術分享社區

三、K-means實現之scipy

scipy庫中也實現了K-means算法。

中心索引或聚類索引也被稱爲 「code」 ,code到中心的映射表被稱爲 「code book」.

3.1 kmeans函數

使用kmeans函數進行聚類,須要兩步來實現

  1. 使用kmeans函數生成codebook和失真值
  2. 使用vq函數將codebook分配到每一個觀察數據上,並獲得每一個觀測數據到它最近中心點的距離。

示例:

import scipy
from scipy.cluster.vq import kmeans, vq, whiten
import numpy as np

#生成待聚類的數據點,這裏生成了20個點,每一個點4維:
points=scipy.randn(20,4) 

data=whiten(points) # 將原始數據作歸一化處理
#返回聚類中心的映射表和損失
codebook, variance = kmeans(data, 4) 
# 使用vq函數根據聚類中心對全部數據進行分類,vq的輸出全部數據的label和距離
code, distance = vq(data, codebook)

# 結果
>>> codebook # (4,4)
array([[-1.227829  , -0.41256122, -0.1342359 , -0.98257834],
       [ 1.01190005, -0.34999089, -0.13180372,  0.06394479],
       [ 0.01156929, -0.39212056,  1.86893218, -0.34921357],
       [ 0.21946277,  1.36809613,  0.87196001,  0.9213216 ]])
>>>variance
1.221658211170926

>>> code
array([2, 0, 0, 2, 0, 2, 1, 3, 1, 1, 3, 0, 1, 0, 1, 1, 3, 2, 3, 2],
      dtype=int32)
>>>distance
array([1.32927696, 0.99594691, 1.38351644, 1.22323281, 1.12605626,
       2.04444249, 0.55554746, 2.06947197, 1.44928466, 1.09481098,
       1.60957745, 1.07210177, 1.3848659 , 0.6393925 , 0.69392457,
       1.06200234, 1.09091552, 0.87726365, 0.76938663, 1.96214695])
複製代碼

kmeans函數定義:

def kmeans(obs, k_or_guess, iter=20, thresh=1e-5, check_finite=True)
複製代碼

參數:

  • obsndarrayMxN數組,每行表示一個觀測矢量。特徵必須進過whiten函數處理
  • k_or_guessint或者ndarray,產生的中心點的個數,每一箇中心點分配一個code,這也是質心在生成的code_book矩陣中的行索引,經過從觀測矩陣中隨機選擇觀測值來選擇初始k中心。也能夠經過傳入一個kxN的數組來指定初始k中心點。
  • iterint,可選值。運行k均值算法的次數,返回具備最低失真的code book,若是爲k_or_guess參數的數組指定了初始質心,則將忽略此參數,此參數不表明k均值算法的迭代次數
  • threshfloat,可選值。若是自上次k均值迭代以來失真的變化小於或等於閾值,則終止k均值算法。
  • check_finitebool,可選值,默認值:True。是否檢查輸入矩陣僅包含有限數。禁用可能會提升性能,可是若是輸入中確實包含無窮大或NaN,則可能會致使問題(崩潰,終止)。

返回:

  • codebookndarray,由k個質心組成的維度(k,N)的數組
  • distortionfloat型,觀測值與生成的中心點之間的平均歐式距離(非平方)。請注意,在K-means算法中失真的標準定義是平方距離總和。

注意: 1. `kmeans`函數中,`iter`參數用來指定運行K均值算法的次數,而不是迭代次數。算法終止條件只能經過`thresh`參數來指定。 2. 距離度量使用的是平均歐式距離(非平方)

vq函數定義:

def vq(obs, code_book, check_finite=True)
複製代碼

code book中的每個code分配給觀察值。在MXN的數組中的每一個觀察矢量與code book中的質心進行比較,併爲其分配最接近質心的code.

obs中的特徵應該具備單位方差,能夠經過將他們傳遞給whiten函數來實現。code book可使用K-means算法或其餘編碼算法來建立。

參數:

  • obsMxN數組,每一行表明一個觀測值。特徵必須通過whiten函數處理。
  • code_bookndarray。一般使用k-means算法生成,每一行表示一個不一樣的code,列表示code的特徵值。
  • check_finitebool,可選值,默認是True。是否檢查輸入數組中僅包含有限值。禁用可能會提升性能,但若是輸入內容確實包含無窮大或NaN,則可能會致使問題(崩潰,終止)。

返回值:

  • code: ndarray,長度爲M的數組,用於保存每一個觀察數據的code book
  • dist: ndarray``,(M,)每一個觀察數據到它最近code的失真值。

3.2 kmeans2函數

該函數也是用來實現K-means算法。該算法嘗試最小化觀測值和質心之間的歐幾里得距離,包括幾種初始化方法。

scipy.cluster.vq.kmeans2(data, k, iter=10, thresh=1e-05, minit='random', missing='warn', check_finite=True)
複製代碼

參數:

  • data:ndarray(M,N)的數組,包含M個具備N維的觀測數據。
  • k:int or ndarray,聚類個數。若是minit參數是matrix,或者若是給定一個ndarray,它被解釋爲初始聚類,以代替使用。
  • iter:int,可選值,k-means算法運行的迭代次數,注意,與kmeans函數的iter參數的含義不一樣。
  • thresh:float,可選值,沒有使用
  • minit:str,可選值,初始化方法。可選擇random, points,++matrix
    • random:從高斯生成k個質心,均值和方差根據數據估算出
    • points:從數據中隨機選擇k個觀測值(行)做爲初始中心
    • ++:根據kmeans++方法選擇k個觀測值
    • matrix:將k參數解釋爲初始質心的(k,M)數組
  • missing:str,可選值,用於解決空聚類的方法,可用方法有warnraise
    • warn:給出警告並繼續
    • raise:引起ClusterError並終止算法
  • check_finite:bool,可選值,是否檢查輸入矩陣僅包含有限數,默認True

返回值:

  • centroid:ndarray:一個(k,N)的數組,表示k-means方法最後一次迭代的中心點
  • label:ndarray,每一個觀測值的代碼或索引值。label [i]是第i個觀測值最接近的質心的代碼或索引

示例

import scipy
from scipy.cluster.vq import kmeans2, whiten
import numpy as np

#生成待聚類的數據點,這裏生成了20個點,每一個點4維:
points=scipy.randn(20,4) 

data=whiten(points) # 將原始數據作歸一化處理
centroid, label = kmeans2(data, 5)

# 結果
>>> centroid
array([[ 0.52132816,  0.97577703, -0.30863464, -1.30546523],
       [-0.27344139, -0.81129939, -0.59560322,  0.47788319],
       [ 1.99658961, -0.10701021,  1.09921144,  0.51397034],
       [-0.37598454,  1.72180727, -0.18127439,  0.58114466],
       [ 0.25895367, -0.01881385,  1.25681737,  0.03119893]])
>>> label
array([1, 0, 3, 0, 1, 1, 2, 4, 0, 1, 1, 0, 4, 4, 0, 3, 1, 4, 3, 2],
      dtype=int32)
複製代碼

四、參考資料

[1]各種聚類(clustering)算法初探 - 鄭瀚Andrew.Hann - 博客園

[2]特徵提取方法:聚類之Kmeans - Jack_Kuo的博客 - CSDN博客

[3]sklearn.cluster.KMeans — scikit-learn 0.17 文檔

[4]K-means clustering and vector quantization (scipy.cluster.vq) — SciPy v1.3.1 Reference Guide

相關文章
相關標籤/搜索