免費Python機器學習課程九:K均值聚類
K聚類是什么意思?
K均值聚類是最流行和廣泛使用的無監督學習模型。它也稱為群集,因為它通過群集數據來工作。與監督學習模型不同,非監督模型不使用標記數據。
該算法的目的不是預測任何標簽。而是更好地了解數據集并對其進行標記。
在k均值聚類中,我們將數據集聚類為不同的組。
這是k均值聚類算法的工作原理
(1) 第一步是隨機初始化一些點。這些點稱為簇質心。

在上圖中,紅色和藍色點是群集質心。
您可以選擇任意數量的群集質心。但是簇質心的數量必須少于數據點的總數。
(2) 第二步是群集分配步驟。在此步驟中,我們需要遍歷每個綠點。根據點是否更靠近紅色或藍色點,我們需要將其分配給其中一個點。
換句話說,根據綠色點是紅色還是藍色來著色,具體取決于它是靠近藍色簇質心還是紅色簇質心。

(3) 下一步是移動群集質心。現在,我們必須對分配給紅色聚類質心的所有紅點取平均值,然后將紅色聚類質心移至該平均值。我們需要對藍色簇質心執行相同的操作。
現在,我們有了新的簇質心。我們必須回到編號2(集群分配步驟)。我們需要將點重新排列到新的群集質心。在那之后重復第三。
數字2和3需要重復幾次,直到兩個聚類質心都位于合適的位置,如下圖所示。

看,我們只是按照分配給它們的簇質心對所有綠色點進行了著色。藍色簇質心位于藍色簇的中心,紅色簇質心位于紅色簇的中心。
當我們開發該算法時,將會稍微清楚一點。我們將對此進行更詳細的討論。
開發算法
我將用于此算法的數據集是從安德魯·伍(Andrew Ng)在Coursera的機器學習課程中獲得的。這是開發k均值算法的分步指南:
(1) 導入必要的包和數據集
- import pandas as pd
- import numpy as np
- df1 = pd.read_excel('dataset.xlsx', sheet_name='ex7data2_X', header=None)
- df1.head()

數據集只有兩列。我采用了兩個特色數據集,因為它很容易可視化。當您看到視覺效果時,該算法將對您更容易理解。但是,相同的算法也將適用于多維數據集。
我將DataFrame df1轉換為Numpy數組,因為我們將在此過程中處理其他數組:
- X = np.array(df1)
現在,我將按照上面討論的三個步驟進行操作。
(2) 第一步是隨機初始化質心。
我將從數據集中隨機初始化三個點。首先,我將在0和數據集長度之間選擇三個數字。
- import randomrandominit_centroids = random.sample(range(0, len(df1)), 3)
- init_centroids
輸出:
- [95, 30, 17]
使用這三個數字作為索引,并獲取這些索引的數據點。
- centroids = []
- for i in init_centroids:
- centroids.append(df1.loc[i])
- centroids
輸出:
- [0 3.907793
- 1 5.094647
- Name: 95, dtype: float64,
- 0 2.660466
- 1 5.196238
- Name: 30, dtype: float64,
- 0 3.007089
- 1 4.678978
- Name: 17, dtype: float64]
這三點是我們最初的質心。
我將它們轉換為二維數組。因為這是我比較熟悉的格式。
- centroids = np.array(centroids)
輸出:
- array([[3.90779317, 5.09464676],
- [2.66046572, 5.19623848],
- [3.00708934, 4.67897758]])
(3) 實施群集分配步驟。
在這一步中,我們將遍歷數據集中的所有數據點。
一個數據點表示一行數據
讓我們看一行數據,了解如何將這些數據分配給集群。
我們將計算所有三個質心的數據距離。然后將該數據點分配給距離最短的質心。
如我們所見,我們必須計算兩個點之間的許多距離。讓我們開發一個計算距離的函數。
- def calc_distance(X1, X2):
- return(sum((X1 - X2)**2))**0.5
開發一個函數,將每個數據點分配給一個質心。我們的"質心"數組只有三個值。因此,我們有三個索引:0、1、2。我們將為每個數據點分配這些索引之一。
- def findClosestCentroids(ic, X):
- assigned_centroid = []
- for i in X:
- distance=[]
- for j in ic:
- distance.append(calc_distance(i, j))
- assigned_centroid.append(np.argmin(distance))
- return assigned_centroid
此功能是將數據點分配給群集的功能。讓我們使用此函數來計算每個數據點的質心:
- get_centroids = findClosestCentroids(centroids, X)
- get_centroids
部分輸出:
- [2,
- 0,
- 0,
- 2,
- 1,
- 2,
- 2,
- 2,
- 1,
- 1,
- 2,
- 2,
- 2,
- 2,
- 2,
- 2,
- 0,
總輸出很長。因此,我在這里顯示部分輸出。輸出中的第一個質心為2,這意味著將其分配給質心列表的索引2。
(4) 最后一步是根據數據點的平均值移動質心
在這一步中,我們將取每個質心的所有數據點的平均值,然后將質心移動到該平均值。
例如,我們將在索引2處找到分配給質心的所有點的平均值,然后將質心2移至平均值。對索引0和1的質心也執行相同的操作。
讓我們定義一個函數來做到這一點:
- def calc_centroids(clusters, X):
- new_centroids = []
- new_df = pd.concat([pd.DataFrame(X), pd.DataFrame(clusters, columns=['cluster'])],
- axis=1)
- for c in set(new_df['cluster']):
- current_cluster = new_df[new_df['cluster'] == c][new_df.columns[:-1]]
- cluster_mean = current_cluster.mean(axis=0)
- new_centroids.append(cluster_mean)
- return new_centroids
這些都是我們需要開發的所有功能。
正如我之前所討論的,我們需要重復此群集分配過程,并多次移動質心,直到質心處于合適的位置。
對于此問題,我選擇重復此過程10次。我將在每次迭代后繼續繪制質心和數據,以直觀地向您展示其工作方式。
- for i in range(10):
- get_centroids = findClosestCentroids(centroids, X)
- centroids = calc_centroids(get_centroids, X)
- #print(centroids)
- plt.figure()
- plt.scatter(np.array(centroids)[:, 0], np.array(centroids)[:, 1], color='black')
- plt.scatter(X[:, 0], X[:, 1], alpha=0.1)
- plt.show()

經過五次迭代,將質心設置為其最佳位置。因此,他們此后不再更改職位。
我建議,在嘗試降維之前,請運行上面的所有代碼以使其學習好。
否則,您可能會感到不知所措!另外,由于我們已經詳細解釋了該算法,因此我現在將加快執行速度。
降維
我想解釋一下這種算法的至少一個用例。一種非常有用的用例是降維。
想一想圖像。圖像中可能有太多不同的像素。在任何計算機視覺問題中,如果我們可以縮小圖片的尺寸,則設備讀取該圖片的速度將大大提高!是不是
我們可以使用剛剛開發的算法來縮小圖片的尺寸。
我將使用青蛙的圖片來說明這一點:
> Image By Author
我將這張照片上傳到了與筆記本相同的文件夾中。讓我們導入這個:
- import cv2
- im = cv2.imread('frog.png')
- im
輸出:
- array([[[ 2, 57, 20],
- [ 2, 57, 20],
- [ 2, 57, 21],
- ...,
- [ 0, 5, 3],
- [ 8, 12, 11],
- [ 91, 94, 93]], [[ 2, 56, 20],
- [ 1, 54, 20],
- [ 1, 56, 19],
- ...,
- [ 0, 2, 1],
- [ 7, 9, 8],
- [ 91, 92, 91]], [[ 2, 55, 20],
- [ 2, 53, 19],
- [ 1, 54, 18],
- ...,
- [ 2, 4, 2],
- [ 8, 11, 9],
- [ 91, 93, 91]], ..., [[ 6, 76, 27],
- [ 6, 77, 26],
- [ 6, 78, 28],
- ...,
- [ 6, 55, 18],
- [ 13, 61, 25],
- [ 94, 125, 102]], [[ 9, 79, 31],
- [ 11, 81, 33],
- [ 12, 82, 32],
- ...,
- [ 6, 56, 19],
- [ 14, 61, 27],
- [ 96, 126, 103]], [[ 43, 103, 63],
- [ 44, 107, 66],
- [ 46, 106, 66],
- ...,
- [ 37, 81, 50],
- [ 47, 88, 59],
- [118, 145, 126]]], dtype=uint8)
檢查數組的形狀,
- im.sgape
輸出:
- (155, 201, 3)
我將整個數組除以255,以使所有值從0到1。
然后將其重塑為155 * 201 x 3,使其成為二維數組。因為我們之前開發了二維數組的所有函數。
- im = (im/255).reshape(155*201, 3)
如您在上方所見,有許多不同的像素值。我們要減少它并僅保留10像素值。
讓我們初始化10個隨機索引,
- randomrandom_index = random.sample(range(0, len(im)), 10)
現在,像上一個示例一樣找到質心:
- centroids = []
- for i in random_index:
- centroids.append(im[i])
- centroids = np.array(centroids)
輸出:
- array([[0.00392157, 0.21176471, 0.06666667],
- [0.03529412, 0.2627451 , 0.09803922],
- [0.29411765, 0.3254902 , 0.26666667],
- [0.00784314, 0.18431373, 0.05882353],
- [0.29019608, 0.49411765, 0.28235294],
- [0.5254902 , 0.61176471, 0.48627451],
- [0.04313725, 0.23921569, 0.09803922],
- [0.00392157, 0.23529412, 0.0745098 ],
- [0.00392157, 0.20392157, 0.04705882],
- [0.22352941, 0.48235294, 0.40784314]])
現在,我也將" im"轉換為數組,
- im = np.array(im)
數據準備就緒。現在,我們可以繼續進行集群過程。但是這次,我將不進行可視化。因為數據不再是二維的。因此,可視化并不容易。
- for i in range(20):
- get_centroids = findClosestCentroids(centroids, im)
- centroids = calc_centroids(get_centroids, im)
我們現在得到了更新的質心。
- centroids
輸出:
- [0 0.017726
- 1 0.227360
- 2 0.084389
- dtype: float64,
- 0 0.119791
- 1 0.385882
- 2 0.247633
- dtype: float64,
- 0 0.155117
- 1 0.492051
- 2 0.331497
- dtype: float64,
- 0 0.006217
- 1 0.048596
- 2 0.019410
- dtype: float64,
- 0 0.258289
- 1 0.553290
- 2 0.406759
- dtype: float64,
- 0 0.728167
- 1 0.764610
- 2 0.689944
- dtype: float64,
- 0 0.073519
- 1 0.318513
- 2 0.170943
- dtype: float64,
- 0 0.035116
- 1 0.273665
- 2 0.114766
- dtype: float64,
- 0 0.010810
- 1 0.144621
- 2 0.053192
- dtype: float64,
- 0 0.444197
- 1 0.617780
- 2 0.513234
- dtype: float64]
這是最后一步。我們只會保留這10點。
如果還打印get_centroids,您將看到集群分配。
現在,我們要遍歷整個數組" im",并將數據更改為其相應的簇質心值。這樣,我們將僅具有這些質心值。
我不想更改原始數組,而是要制作一個副本并在那里進行更改。
- imim_recovered = im.copy()
- for i in range(len(im)):
- im_recovered[i] = centroids[get_centroids[i]]
您還記得,我們在一開始就更改了圖像的尺寸,使其成為二維數組。我們現在需要將其更改為原始形狀。
- im_recoveredim_recovered = im_recovered.reshape(155, 201, 3)
在這里,我將并排繪制原始圖像和縮小后的圖像,以顯示差異:
- im1 = cv2.imread('frog.png')
- import matplotlib.image as mpimg
- fig,ax = plt.subplots(1,2)
- ax[0].imshow(im1)
- ax[1].imshow(im_recovered)
> Image by Author
看,我們如此大地減小了圖像的尺寸。不過,它看起來像只青蛙!但是計算機閱讀起來會快得多!
結論
在本文中,我解釋了k均值聚類的工作原理以及如何從頭開始開發k均值聚類算法。我還解釋了如何使用此算法來縮小圖像尺寸。請嘗試使用其他圖像。
這是我在本文中使用的數據集的鏈接。
https://github.com/rashida048/Machine-Learning-With-Python/blob/master/kmean.xlsx
這個是代碼:
https://github.com/rashida048/Machine-Learning-With-Python/blob/master/k_mean_clustering_final.ipynb