Python機器學習算法實現
Author:louwill
Machine Learning Lab
聚類分析(Cluster Analysis)是一類經典的無監督學習算法。在給定樣本的情況下,聚類分析通過特徵相似性或者距離的度量方法,將其自動劃分到若干個類別中。常用的聚類分析方法包括層次聚類法(Hierarchical Clustering)、k均值聚類(K-means Clustering)、模糊聚類(Fuzzy Clustering)以及密度聚類(Density Clustering)等。本節我們僅對最常用的kmeans算法進行講解。
相似度度量
相似度或距離度量是聚類分析的核心概念。常用的距離度量方式包括閔氏距離和馬氏距離,常用的相似度度量方式包括相關係數和夾角餘弦等。
閔氏距離
閔氏距離即閔可夫斯基距離(Minkowski Distance),定義如下。給定維向量樣本集合,對於,,,樣本與樣本之間的閔氏距離可定義爲:
,
當時,閔氏距離就可以表達爲歐式距離(Euclidean Distance):
當時,閔氏距離也稱爲曼哈頓距離(Manhatan Distance):
當時,閔氏距離也稱爲切比雪夫距離(Chebyshev Distance):馬氏距離
馬氏距離全稱爲馬哈拉諾比斯距離(Mahalanobis Distance),即一種考慮各個特徵之間相關性的聚類度量方式。給定一個樣本集合,其協方差矩陣爲,樣本與樣本之間的馬氏距離可定義爲:
當爲單位矩陣時,即樣本的各特徵之間相互獨立且方差爲1時,馬氏距離就是歐式距離。相關係數
相關係數(Correlation Coefficent)是度量相似度最常用的方式。相關係數越接近於1表示兩個樣本越相似,相關係數越接近於0,表示兩個樣本越不相似。樣本和之間相關係數可定義爲:夾角餘弦
夾角餘弦也是度量兩個樣本相似度的方式之一。夾角餘弦越接近於1表示兩個樣本越相似,夾角餘弦越接近於0,表示兩個樣本越不相似。樣本和之間夾角餘弦可定義爲:
kmeans聚類
kmeans即k均值聚類算法。給定維樣本集合,均值聚類是要將個樣本劃分到個不同的類別區域,通常而言。所以均值聚類可以總結爲對樣本集合的劃分,其學習策略主要是通過損失函數最小化來選取最優的劃分。
我們使用歐式距離作爲樣本間距離的度量方式。則樣本間的距離可定義爲:
定義樣本與其所屬類中心之間的距離總和爲最終損失函數:
其中爲第個類的質心(即中心點),中表示指示函數,取值爲1或0。函數表示相同類中樣本的相似程度。所以均值聚類可以規約爲一個優化問題求解:
該問題是一個NP hard的組合優化問題,實際求解時我們採用迭代的方法進行求解。
根據以上定義,我們可以梳理均值聚類算法的主要流程如下:
初始化質心。即在第0次迭代時隨機選擇個樣本點作爲初始化的聚類質心點。
按照樣本與中心的距離對樣本進行聚類。對固定的類中心,其中爲類的中心點,計算每個樣本到類中心的距離,將每個樣本指派到與其最近的中心點所在的類,構成初步的聚類結果。
計算上一步聚類結果的新的類中心。對聚類結果計算當前各個類中樣本均值,並作爲新的類中心。
如果迭代收斂或者滿足迭代停止條件,則輸出最後聚類結果,否則令,返回第二步重新計算。
kmeans算法實現
下面我們基於numpy按照前述算法流程來實現一個kmeans算法。回顧上述過程,我們可以先思考一下對算法每個流程該如何定義。首先要定義歐式距離計算函數,然後類中心初始化、根據樣本與類中心的歐式距離劃分類別並獲取聚類結果、根據新的聚類結果重新計算類中心點、重新聚類直到滿足停止條件。
下面我們先定義兩個向量之間的歐式距離函數如下:
import numpy as np
# 定義歐式距離
def euclidean_distance(x1, x2):
distance = 0
# 距離的平方項再開根號
for i in range(len(x1)):
distance += pow((x1[i] - x2[i]), 2)
return np.sqrt(distance)
然後爲每個類別隨機選擇樣本進行類中心初始化:
# 定義中心初始化函數
def centroids_init(k, X):
n_samples, n_features = X.shape
centroids = np.zeros((k, n_features))
for i in range(k):
# 每一次循環隨機選擇一個類別中心
centroid = X[np.random.choice(range(n_samples))]
centroids[i] = centroid
return centroids
根據歐式距離計算每個樣本所屬最近類中心點的索引:
# 定義樣本的最近質心點所屬的類別索引
def closest_centroid(sample, centroids):
closest_i = 0
closest_dist = float('inf')
for i, centroid in enumerate(centroids):
# 根據歐式距離判斷,選擇最小距離的中心點所屬類別
distance = euclidean_distance(sample, centroid)
if distance < closest_dist:
closest_i = i
closest_dist = distance
return closest_i
定義構建每個樣本所屬類別過程如下:
# 定義構建類別過程
def create_clusters(centroids, k, X):
n_samples = np.shape(X)[0]
clusters = [[] for _ in range(k)]
for sample_i, sample in enumerate(X):
# 將樣本劃分到最近的類別區域
centroid_i = closest_centroid(sample, centroids)
clusters[centroid_i].append(sample_i)
return clusters
根據上一步聚類結果重新計算每個類別的均值中心點:
# 根據上一步聚類結果計算新的中心點
def calculate_centroids(clusters, k, X):
n_features = np.shape(X)[1]
centroids = np.zeros((k, n_features))
# 以當前每個類樣本的均值爲新的中心點
for i, cluster in enumerate(clusters):
centroid = np.mean(X[cluster], axis=0)
centroids[i] = centroid
return centroids
然後簡單定義一下如何獲取每個樣本所屬的類別標籤:
# 獲取每個樣本所屬的聚類類別
def get_cluster_labels(clusters, X):
y_pred = np.zeros(np.shape(X)[0])
for cluster_i, cluster in enumerate(clusters):
for sample_i in cluster:
y_pred[sample_i] = cluster_i
return y_pred
最後我們將上述過程進行封裝,定義一個完整的kmeans算法流程:
# 根據上述各流程定義kmeans算法流程
def kmeans(X, k, max_iterations):
# 1.初始化中心點
centroids = centroids_init(k, X)
# 遍歷迭代求解
for _ in range(max_iterations):
# 2.根據當前中心點進行聚類
clusters = create_clusters(centroids, k, X)
# 保存當前中心點
prev_centroids = centroids
# 3.根據聚類結果計算新的中心點
centroids = calculate_centroids(clusters, k, X)
# 4.設定收斂條件爲中心點是否發生變化
diff = centroids - prev_centroids
if not diff.any():
break
# 返回最終的聚類標籤
return get_cluster_labels(clusters, X)
我們來簡單測試一下上述實現的kmeans算法:
# 測試數據
X = np.array([[0,2],[0,0],[1,0],[5,0],[5,2]])
# 設定聚類類別爲2個,最大迭代次數爲10次
labels = kmeans(X, 2, 10)
# 打印每個樣本所屬的類別標籤
print(labels)
[0. 0. 0. 1. 1.]
可以看到,kmeans算法將第1~3個樣本聚爲一類,第4~5個樣本聚爲一類。sklearn中也爲我們提供了kmeans算法的接口,嘗試用sklearn的kmeans接口來測試一下該數據:
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=2, random_state=0).fit(X)
print(kmeans.labels_)
[0. 0. 0. 1. 1.]
可以看到sklearn的聚類結果和我們自定義的kmeans算法是一樣的。但是這裏有必要說明的一點是,不同的初始化中心點的選擇對最終結果有較大影響,自定義的kmeans算法和sklearn算法計算出來的結果一致本身也有一定的偶然性。另外聚類類別k的選擇也需要通過一定程度上的實驗才能確定。
參考資料:
李航 統計學習方法 第二版