K均值算法的實現和應用

前言

前面的學習中,已經詳細了解了K均值算法的相關原理。本篇文章,我們將使用python實現K均值算法并將其應用于圖像壓縮處理。這對K均值算法的直觀理解是非常有幫助的。在本次算法實現中,利用K均值算法減少一副圖像中圖像顏色數量出現最多的部分像素,來實現圖像壓縮。

算法實現

K均值算法的主要原理就是給定一組初始數據\{x_1,x_2,……x_m\},并初始化聚類中心,根據初始化的聚類中心,將的數據分配給最近的聚類中心,并重新計算新的聚類中心,一直重復這個過程,直到沒有最新的數據分配給聚類中心或者是聚類中心不再發生新改變。

尋找聚類中心

K均值算法中,需要將每一個訓練樣本x_i分配給最接近該樣本的聚類中心,對于每一個訓練樣本x_i,可以用如下公式求取其聚類中心:

c^{(i)}:=j \ \ \ \ minimizes ||x^{(i)}-u_j ||^2
其中,c^{(i)}表示最接近x^{(i)}的聚類中心索引,而u_j則表示第j個聚類中心的值或者位置,在代碼中,用idx[i]表示。
尋找聚類中心的的算法可以用以下代碼實現:

  • 初始化
    首先,對一些參數進行初始化,如加載訓練樣本,聚類中心的數目和初始值設置,如下代碼所示:
import matplotlib.pyplot as plt
import numpy as np
import scipy.io as scio
from skimage import io
from skimage import img_as_float
#加載訓練樣本

data = scio.loadmat('ex7data2.mat')
X = data['X']

# 選擇初始的聚類中心的數量和位置(value)
k = 3  #聚類中心的數目
initial_centroids = np.array([[3, 3], [6, 2], [8, 5]]) #聚類中心的初始值
  • 尋找聚類中心
    求取聚類中心的過程可以內外兩層循環完成,內循環表示求取每一個聚類中心與樣本x的范數(距離),而外循環表示每一個樣本x減去聚類中心的所得的值。通過以上步驟,可以得到一個300X3的范數矩陣,最后,返回行方向上最小值索引(長度為300的一維數組)。
def find_closest_centroids(X, centroids):
    K = centroids.shape[0]  # K=3
    m = X.shape[0]          # m = 300
    idx = np.zeros(m)
    means = np.zeros((m, K))  
    for i in range(m):
        x = X[i]
        #外循環,每一個x的位置(二維矩陣)減去聚類中心的位置(二維矩陣)
        diff = x - centroids  
        for k in range(K):
            #內循環,x減去每一個聚類中心所得范數
            means[i, k] = np.linalg.norm(diff[k])
    #聚類中心的行方向上最小值所對應的索引
    idx = np.argmin(means, axis=1)
    return idx

注意:通過求取聚類中心,得到了由聚類中心索引所構成的一維數組。求取聚類中心之后,相當于給每個訓練樣本打上聚類中心k的標識,而這一維數組的索引與訓練樣本x的索引相對應,例如,300個訓練樣本,最后求得一個長度為300的一維數組,而其值表示訓練樣本所對應的聚類中心的標識。

計算聚類中心均值

以上,我們求取了行方向上最小聚類中心的索引,對于給定的K個聚類中心,需要求得給第k個聚類中心的所有訓練樣本的均值,可以用如下公式表示:
u_k := \frac{1}{|C_k|}\sum_{i \in C_k} x^{(i)}

假設,有兩個訓練樣本x^{(3)},x^{(5)}被分配給了聚類中心k=2,則u_2=\frac{1}{2}(x^{(3)}+x^{(5)})
計算聚類中心的均值的算法實現,如下代碼所示

def compute_centroids(X, idx, K):   
    (m, n) = X.shape #m=300,n=2
    centroids = np.zeros((K, n))
    for k in range(K):
        #每個聚類中心索引所對應的樣本x,表示分配給聚類中心索引k的訓練樣本x
        x_for_centroid_k = X[np.where(idx == k)] 
        #分配給索引為k的聚類中心的樣本x在列方向上的和除以分配給聚類中心所對應的樣本數量
        centroid_k = np.sum(x_for_centroid_k, axis=0) / x_for_centroid_k.shape[0]
        centroids[k] = centroid_k
    return centroids

K均值算法的可視化實現

通過以上步驟,已經得到了訓練樣本的聚類中心和其均值,通過以下代碼,通過十次迭代,可視化的實現K均值算法在訓練樣本中的運行方式,具體實現,如下代碼所示:

def run_kmeans(X, initial_centroids, max_iters, plot):
    if plot:
        plt.figure()

 
    (m, n) = X.shape
    K = initial_centroids.shape[0]
    centroids = initial_centroids
    previous_centroids = centroids
    idx = np.zeros(m)

    
    for i in range(max_iters):
      
        print('K-Means iteration {}/{}'.format((i + 1), max_iters))

        
        idx = find_closest_centroids(X, centroids)

       
        if plot:
            plot_progress(X, centroids, previous_centroids, idx, K, i)
            previous_centroids = centroids
            input('Press ENTER to continue')

        
        centroids = compute_centroids(X, idx, K)

    return centroids, idx


def plot_progress(X, centroids, previous, idx, K, i):
    plt.scatter(X[:, 0], X[:, 1], c=idx, s=15)

    plt.scatter(centroids[:, 0], centroids[:, 1], marker='x', c='black', s=25)

    for j in range(centroids.shape[0]):
        draw_line(centroids[j], previous[j])

    plt.title('Iteration number {}'.format(i + 1))


def draw_line(p1, p2):
    plt.plot(np.array([p1[0], p2[0]]), np.array([p1[1], p2[1]]), c='black', linewidth=1)

通過10次迭代后,運行圖像,如下圖所示:


利用K均值算法實現圖像壓縮

以上,已經詳細了解并實現了K均值算法,在這部分內容中,將使用K均值算法來實現圖像壓縮,所謂圖像壓縮指的是在圖像像素方面的處理。圖像常用的編碼方式為RGB編碼,即用三基色(RED,GREEN,BLUE)表示圖像顏色。每個像素由三個8位無符號二進制數(范圍從0到255)表示其像素顏色,例如,一個像素的顏色可以用(220,101,25)表示。給定的圖像包含這數千種顏色,通過K均值算法,可以將其顏色的數量降至16種,從而實現圖像壓縮。

像素處理

圖像的每個像素顏色即代表訓練樣本x,通過K均值算法尋找16種顏色代表圖像中的所有像素的顏色,即也就是尋找16個聚類中心,最后,將所有的像素顏色替換為16個聚類中心所對應的顏色。

  • 圖像加載和預處理
    對于每一個像素,可以用一個三維矩陣表示,其中,第一維和第二維表示其所在位置,第三維代表其是藍色,紅色,或者綠色。例如一個矩陣A= martix(53,44,3),表示53行,44列所在的像素其顏色為3.
    在此過程中,需要將圖像轉換為m*3的矩陣,其中,m=像素的行×列。其實現過程可以用如下代碼表示
image = io.imread('bird_small.png')
#將圖像轉換為浮點型數據
image = img_as_float(image)
img_shape = image.shape
X = image.reshape(img_shape[0] * img_shape[1], 3)

通過以上代碼,將圖像轉化為(128×128,3)的二維矩陣

運行K均值算法處理圖像

  • 聚類中心的隨機初始化
    在運行算法之前,還需要對聚類中心,進行隨機初始化的處理,其初始化過程也就是對位置進行初始化,具體實現方式如下代碼所示
def kmeans_init_centroids(X, K):   
    centroids = np.zeros((K, X.shape[1]))   
    indices = np.random.randint(X.shape[0], size=K)
    centroids = X[indices]
    return centroids

根據之前的算法實現,現在,可以直接運行K均值算法了,其實現方式如下所示

K = 16 #設置聚類中心數量
max_iters = 10 #最大迭代次數

initial_centroids = kmeans_init_centroids(X, K)
centroids, idx = run_kmeans(X, initial_centroids, max_iters, False)
  • 實現圖像壓縮
    經過以上處理,圖像壓縮的實現步驟如下代碼所示:
idx = find_closest_centroids(X, centroids)


X_recovered = centroids[idx]
# (128*128*3)
X_recovered = np.reshape(X_recovered, (img_shape[0], img_shape[1], 3))

plt.subplot(2, 1, 1)
plt.imshow(image)
plt.title('Original')

plt.subplot(2, 1, 2)
plt.imshow(X_recovered)
plt.title('Compressed, with {} colors'.format(K))

最后,經過處理過后的圖像對比如下圖所示,可以明顯的看出,第二幅圖像的質量有所降低。


?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容