《Python機器學習及實踐:從零開始通往Kaggle競賽之路》第2章 基礎篇 學習筆記(十二)2.2.1.1K均值算法總結

目錄

2.2.1.1K均值算法

0、引言

(1)無監督學習

(2)數據聚類

1、模型介紹

2、數據描述

(1)數據下載

(2)數據描述

3、編程實踐

4、性能測評

(1)ARI指標

(2)輪廓係數

5、特點分析


2.2.1.1K均值算法

0、引言

(1)無監督學習

無監督學習着重於發現數據本身的分佈特點。與監督學習不同,無監督學習不需要對數據進行標記。這樣,在節省大量人工的同時,也讓可以利用的數據規模變得不可限量。

從功能角度講,無監督學習模型可以發現數據的“羣落”,同時也可以尋找“離羣”的樣本;另外對於特徵維度非常高的數據樣本,同樣可以通過無監督的學習對數據進行降維,保留最具有區分性的低緯度特徵。這些都是在海量數據處理中是非常實用的技術。

(2)數據聚類

數據聚類是無監督學習的主流應用之一。最爲經典並且易用的聚類模型,當屬K均值(K-means)算法該算法要求預先設定聚類的個數,然後不斷更新聚類中心;經過幾輪這樣的迭代,最後的目標就是要讓所有數據點到其所屬聚類中心的平方和趨於穩定。

1、模型介紹

這是在數據聚類中是最經典的,也是相對容易理解的模型。算法執行的過程分爲4個階段,如圖2-10所示:

①首先,隨機佈設K個特徵空間內的點作爲初始的聚類中心。

②然後,對於根據每個數據的特徵向量,從K個聚類中心中尋找距離最近的一個,並且把該數據標記爲從屬於這個聚類中心。

③接着,在所有的數據上都被標記過聚類中心後,根據這些數據新分配的類簇,重新對K個聚類中心做計算。

④如果一輪下來,所有的數據點從屬的聚類中心與上一次分配的類簇沒有變化,那麼迭代可以停止;否則回到步驟②繼續循環。

2、數據描述

使用手寫體數字圖像數據的完整版本。

(1)數據下載

數據集的下載地址爲:https://archive.ics.uci.edu/ml/machine-learning-databases/optdigits/

備註:下載文件爲:optdigits.names。

(2)數據描述

1. Title of Database: Optical Recognition of Handwritten Digits

2. Source:
	E. Alpaydin, C. Kaynak
	Department of Computer Engineering
	Bogazici University, 80815 Istanbul Turkey
	[email protected]
	July 1998

3. Past Usage:
	C. Kaynak (1995) Methods of Combining Multiple Classifiers and Their
	Applications to Handwritten Digit Recognition, 
	MSc Thesis, Institute of Graduate Studies in Science and 
	Engineering, Bogazici University.

	E. Alpaydin, C. Kaynak (1998) Cascading Classifiers, Kybernetika,
	to appear. ftp://ftp.icsi.berkeley.edu/pub/ai/ethem/kyb.ps.Z

4. Relevant Information:
	We used preprocessing programs made available by NIST to extract
	normalized bitmaps of handwritten digits from a preprinted form. From
	a total of 43 people, 30 contributed to the training set and different
	13 to the test set. 32x32 bitmaps are divided into nonoverlapping 
	blocks of 4x4 and the number of on pixels are counted in each block.
	This generates an input matrix of 8x8 where each element is an 
	integer in the range 0..16. This reduces dimensionality and gives 
	invariance to small distortions.

	For info on NIST preprocessing routines, see 
	M. D. Garris, J. L. Blue, G. T. Candela, D. L. Dimmick, J. Geist, 
	P. J. Grother, S. A. Janet, and C. L. Wilson, NIST Form-Based 
	Handprint Recognition System, NISTIR 5469, 1994.

5. Number of Instances
	optdigits.tra	Training	3823
	optdigits.tes	Testing		1797
	
	The way we used the dataset was to use half of training for 
	actual training, one-fourth for validation and one-fourth
	for writer-dependent testing. The test set was used for 
	writer-independent testing and is the actual quality measure.

6. Number of Attributes
	64 input+1 class attribute

7. For Each Attribute:
	All input attributes are integers in the range 0..16.
	The last attribute is the class code 0..9

8. Missing Attribute Values
	None

9. Class Distribution
	Class:	No of examples in training set
	0:  376
	1:  389
	2:  380
	3:  389
	4:  387
	5:  376
	6:  377
	7:  387
	8:  380
	9:  382

	Class: No of examples in testing set
	0:  178
	1:  182
	2:  177
	3:  183
	4:  181
	5:  182
	6:  181
	7:  179
	8:  174
	9:  180

Accuracy on the testing set with k-nn 
using Euclidean distance as the metric

 k =  1   : 98.00
 k =  2   : 97.38
 k =  3   : 97.83
 k =  4   : 97.61
 k =  5   : 97.89
 k =  6   : 97.77
 k =  7   : 97.66
 k =  8   : 97.66
 k =  9   : 97.72
 k = 10   : 97.55
 k = 11   : 97.89

完整的手寫體數字圖像分爲兩個數據集合。其中,訓練樣本3823條,測試樣本1797條;圖像數據通過8 X 8的像素矩陣表示,共有64個像素維度;1個目標維度用來標記每個圖像樣本代表的數字類別。該數據沒有缺失的特徵值,並且不論是訓練還是測試樣本,在數字類別方面都採樣得非常平均,是一份非常規整的數據集。

3、編程實踐

對這份數據的圖像特徵進行K-means聚類。

# 代碼47:K-means算法在手寫體數字圖像數據上的使用示例
# 分別導入numpy、matplotlib以及pandas,用於數學運算、作圖以及數據分析。
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

# 使用pandas分別讀取訓練數據與測試數據集。
digits_train = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/optdigits/optdigits.tra', header=None)
digits_test = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/optdigits/optdigits.tes', header=None)

# 從訓練與測試數據集上都分離出64維度的像素特徵與1維度的數字目標。
X_train = digits_train[np.arange(64)]
y_train = digits_train[64]

X_test = digits_test[np.arange(64)]
y_test = digits_test[64]

# 從sklearn.cluster中導入KMeans模型。
from sklearn.cluster import KMeans

# 初始化KMeans模型,並設置聚類中心數量爲10。
kmeans = KMeans(n_clusters=10)
kmeans.fit(X_train)

# 逐條判斷每個測試圖像所屬的聚類中心。
y_pred = kmeans.predict(X_test)

4、性能測評

也許有些讀者會困惑於如何評估聚類算法的性能,特別是應用在沒有標註類別的數據集上的時候。針對不同的數據特點,提供兩種方式。

(1)ARI指標

如果被用來評估的數據本身帶有正確的類別信息,那麼就使用Adjusted Rand Index(ARI)。ARI指標與分類問題中計算準確性的方法類似,同時也兼顧到了類簇無法和分類標記一一對應的問題。

# 代碼48:使用ARI進行K-means聚類性能評估
# 從sklearn導入度量函數庫metrics。
from sklearn import metrics

# 使用ARI進行KMeans聚類性能評估。
print(metrics.adjusted_rand_score(y_test, y_pred))

本地輸出:

0.6630577949326525

(2)輪廓係數

如果被用於評估的數據沒有所屬類別,那麼習慣使用輪廓係數(Silhouette Coefficient)來度量聚類結果的質量。輪廓係數同時兼顧了聚類的凝聚度(Cohesion)和分離度(Separation),用於評估聚類的效果並且取值範圍爲[-1,1]。輪廓係數值越大,表示聚類效果越好。

具體的計算步驟如下:

①對於已聚類數據中第i個樣本x^{i},計算x^{i}與其同一個類簇內的所有其他樣本距離的平均值,記作a^{i},用於量化簇內的凝聚度。

②選取x^{i}外的一個簇b,計算x^{i}與簇b中所有樣本的平均距離,遍歷所有其他簇,找到最近的這個平均距離,記作b^{i},用於量化簇之間分離度。

③對於樣本x^{i},輪廓係數爲sc^{i}=\frac{b^{i}-a^{i}}{max(b^{i},a^{i})}

④最後對所有樣本X求出平均值即爲當前聚類結果的整體輪廓係數。

由輪廓係數的計算公式,不難發現:如果sc^{i}小於0,說明x^{i}與其簇內元素的平均距離大於最近的其他簇,表示聚類效果不好;如果a^{i}趨於0,或者b^{i}足夠大,那麼sc^{i}趨近於1,說明聚類效果比較好。

# 代碼49:利用輪廓係數評價不同類簇數量的K-means聚類實例
# 導入numpy。
import numpy as np
# 從sklearn.cluster中導入KMeans算法包。
from sklearn.cluster import KMeans
# 從sklearn.metrics導入silhouette_score用於計算輪廓係數。
from sklearn.metrics import silhouette_score
import matplotlib.pyplot as plt

# 分割出3*2=6個子圖,並在1號子圖作圖。
plt.subplot(3, 2, 1)

# 初始化原始數據點。
x1 = np.array([1, 2, 3, 1, 5, 6, 5, 5, 6, 7, 8, 9, 7, 9])
x2 = np.array([1, 3, 2, 2, 8, 6, 7, 6, 7, 1, 2, 1, 1, 3])
X = np.array(list(zip(x1, x2))).reshape(len(x1), 2)

# 在1號子圖做出原始數據點陣的分佈。
plt.xlim([0, 10])
plt.ylim([0, 10])
plt.title('Instances')
plt.scatter(x1, x2)

colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k', 'b']
markers = ['o', 's', 'D', 'v', '^', 'p', '*', '+']

clusters = [2, 3, 4, 5, 8]
subplot_counter = 1
sc_scores = []
for t in clusters:
    subplot_counter += 1
    plt.subplot(3, 2, subplot_counter)
    kmeans_model = KMeans(n_clusters=t).fit(X)
    for i, l in enumerate(kmeans_model.labels_):
        plt.plot(x1[i], x2[i], color=colors[l], marker=markers[l], ls='None')
    plt.xlim([0, 10])
    plt.ylim([0, 10])
    sc_score = silhouette_score(X, kmeans_model.labels_, metric='euclidean')
    sc_scores.append(sc_score)

# 繪製輪廓係數與不同類簇數量的直觀顯示圖。
plt.title('K = %s, silhouette coefficient= %0.03f' % (t, sc_score))

# 繪製輪廓係數與不同類簇數量的關係曲線。
plt.figure()
plt.plot(clusters, sc_scores, '*-')
plt.xlabel('Number of Clusters')
plt.ylabel('Silhouette Coefficient Score')

plt.show()

備註:原來的會報錯,錯誤提示爲:

    X = np.array(zip(x1, x2)).reshape(len(x1), 2)
ValueError: cannot reshape array of size 1 into shape (14,2)

報錯原因:

zip()方法在Python2和Python3中的不同:在Python3.x中爲了減少內存,zip()返回的是一個對象。如需展示列表,需手動list()轉換。

故採用以下方法。

X = np.array(list(zip(x1, x2))).reshape(len(x1), 2)

本地輸出:

結論:當聚類中心數量爲3的時候,輪廓係數最大;此時,也觀察到聚類中心數量爲3也符合數據的分佈特點,的確是相對較爲合理的類簇數量。

5、特點分析

K-means聚類模型所採用的迭代式算法,直觀易懂並且非常實用。只是有兩大缺陷:

①容易收斂到局部最優解。

②需要預先設定簇的數量。

首先解釋什麼叫做局部最優解。假設圖2-13左側爲實際數據以及正確的所屬類簇。如果聚類算法可以收斂至全局最優解,那麼三個類簇的聚類中心應如右側Global Optimum所示,聚類結果同正確結果一致。但是,K-means算法無法保證能夠使得三個類簇的中心迭代至上述的全局最優解。相反很有可能受到隨機初始類簇中心點位置的影響,最終迭代到如右側Local Optimum所示的兩種情況而收斂。這樣便導致無法繼續更新聚類中心,使得聚類結果與正確結果有很大出入。

這是算法自身的理論缺陷所造成的,無法輕易地從模型設計上彌補;卻可以通過執行多次K-means算法來挑選性能表現更好的初始中心點,這樣的工程方法代替。

然後,介紹一種“肘部”觀察法用於粗略地預估相對合理的類簇個數。因爲K-means模型最終期望所有數據點到其所屬的類簇距離的平方和趨於穩定,所以可以通過觀察這個數值隨着K的走勢來找出最佳的類簇數量。理想條件下,這個折線在不斷下降並且趨於平穩的過程中會有斜率的拐點,同時意味着從這個拐點對應的K值開始,類簇中心的增加不會過於破壞數據聚類的結構。

# 代碼50:“肘部”觀察法示例
# 導入必要的工具包。
import numpy as np
from sklearn.cluster import KMeans
from scipy.spatial.distance import cdist
import matplotlib.pyplot as plt

# 使用均勻分佈函數隨機三個簇,每個簇周圍10個數據樣本。
cluster1 = np.random.uniform(0.5, 1.5, (2, 10))
cluster2 = np.random.uniform(5.5, 6.5, (2, 10))
cluster3 = np.random.uniform(3.0, 4.0, (2, 10))

# 繪製30個數據樣本的分佈圖像。
X = np.hstack((cluster1, cluster2, cluster3)).T
plt.scatter(X[:, 0], X[:, 1])
plt.xlabel('x1')
plt.ylabel('x2')
plt.show()

# 測試9種不同聚類中心數量下,每種情況的聚類質量,並作圖。
K = range(1, 10)
meandistortions = []

for k in K:
    kmeans = KMeans(n_clusters=k)
    kmeans.fit(X)
    meandistortions.append(sum(np.min(cdist(X, kmeans.cluster_centers_, 'euclidean'), axis=1)) / X.shape[0])

plt.plot(K, meandistortions, 'bx-')
plt.xlabel('k')
plt.ylabel('Average Dispersion')
plt.title('Selecting k with the Elbow Method')
plt.show()

本地輸出:

結論:類簇數量爲1或2的時候,樣本距所屬類簇的平均距離的下降速度很快,這說明更改K值會讓整體聚類結構有很大改變,也意味着新的聚類數量讓算法有更大的收斂空間,這樣的K值不能反映真實的類簇數量。而當K=3時,平均距離的下降速度有了顯著放緩,這意味着進一步增加K值不再會有利於算法的收斂,也同時暗示着K=3是相對最佳的類簇數量。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章