機器學習之聚類算法應用篇

1 概述

本專題重點在於使用sklearn進行聚類分析,不涉及聚類算法的原理介紹。

1.1 無監督學習與聚類算法

在機器學習當中,有相當一部分算法屬於“無監督學習”,無監督算法在訓練的時候只需要特徵矩陣X,不需要標籤。聚類算法就是無監督學習的代表算法。

聚類算法又叫“無監督分類”,其目的是將數據劃分爲有意義或者有用的組(簇)。這種劃分可以基於業務的需求或者建模的需求來完成,也可以單純的幫助我們探索數據的自然結構和分佈。比如在商業中,如果我們的手頭有大量的客戶信息,我們可以使用聚類將客戶劃分爲若干組,以便進一步分析和開展營銷活動,最有名的客戶價值判斷模型RFM就常和聚類分析共同使用。再比如,聚類可以用於降維和矢量量化(vector quantization),可以將高位特徵壓縮到一列當中,常常用於圖像,聲音,視頻等非結構化數據,可以大幅壓縮數據量。

聚類 分類
核心 將數據分成多組
探索每個組的數據是否有聯繫
從已經分組的數據中去學習
把新數據放到已經分好的組中去
學習類型 無監督,不需要標籤進行訓練 有監督,需要標籤
典型算法 K-Means, DBSCAN, 層次聚類, 譜聚類 決策樹, 貝葉斯, 邏輯迴歸, SVM
算法輸出 聚類的結果是不確定的
不一定總是能夠反映數據的真實分類
分類結果是確定的
分類的優劣是客觀的

1.2 sklearn中常用的聚類算法

sklearn中的聚類算法有兩種表現形式,一種是類,需要實例化,訓練並使用接口和屬性來調用。另一種是函數,只需要輸入特徵矩陣和超參數即可返回聚類的結果和各種指標。

含義 輸入
cluster.AffinityPropagation 執行親和傳播數據聚類 [damping, …]
cluster.AgglomerativeClustering 凝聚聚類 […]
cluster.Birch Birch聚類算法 [threshold, branching_factor, …]
cluster.DBSCAN DBSCAN聚類算法 [eps, min_samples, metrix, …]
cluster.FeatureAgglomeration 凝聚特徵 [n_clusters, …]
cluster.KMeans K均值聚類 [n_clusters, init, n_init, …]
cluster.MiniBatchKMeans 小批量K均值聚類 [n_clusters, init, …]
cluster.MeanShift 使用平坦核函數的平均移位聚類 [bandwidth, seeds, …]
cluster.SpectralClustering 譜聚類 [n_clusters, …]

1.3 sklearn中聚類使用示例

1.3.1 聚類示例

以KMeans爲例,在sklearn中進行聚類步驟如下:

from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans

x = make_blobs(n_samples=1000, n_features=2, centers = 4, cluster_std=2.0)[0]
## 方式1:使用fit函數訓練,然後調用cluster1.labels_得到每個數據對應的簇編號(0,1,2,...)
cluster1 = KMeans(n_clusters = 4, random_state=10).fit(x)
y_pred = cluster1.labels_

## 方式2:使用fit_predict直接獲得每個數據對應簇編號
y_pred = KMeans(n_clusters = 4, random_state=10).fit_predit(x)

## 以上兩個方法得到的結果完全一致。

1.3.2 重要屬性

  • culster_centers_ - 簇心:返回每個簇心的座標
  • inertia - 總距離平方和:越小越好

2 sklearn中生成測試數據函數介紹

生成數據的函數主要有:sklearn.datasets.make_classification, sklearn.datasets.make_circles, sklearn.datasets.make_moons以及sklearn.datasets.make_blobs。這裏簡單介紹每一個函數,具體參數見官方文檔。

2.1 make_classification

主要用來生成分類數據,用法示例:

x = make_classification(n_samples=1000, n_features=2, n_informative=2, n_redundant=0, n_repeated=0, n_clusters_per_class=1)

示例:

from sklearn.datasets import make_classification, make_circles, make_moons
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure(figsize=(15,5))
fig.add_subplot(1,3,1)
x = make_classification(n_samples=1000, n_features=2, n_informative=2, n_redundant=0, n_repeated=0, n_clusters_per_class=1)
plt.plot(x[0][:,0], x[0][:,1], 'b.')
plt.title('make_classification')

fig.add_subplot(1,3,2)
x = make_classification(n_samples=1000, n_features=2, n_informative=2, n_redundant=0, n_repeated=0, n_clusters_per_class=2)
plt.plot(x[0][:,0], x[0][:,1], 'b.')
plt.title('make_classification')

fig.add_subplot(1,3,3)
x = make_classification(n_samples=1000, n_features=2, n_informative=2, n_redundant=0, n_repeated=0, n_clusters_per_class=2)
plt.plot(x[0][:,0], x[0][:,1], 'b.')
plt.title('make_classification')

在這裏插入圖片描述

2.2 make_moons

用來生成月牙形數據,用法示例:

x = make_moons(n_samples=1000, noise=0.1)

示例:

from sklearn.datasets import make_moons
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure(figsize=(15,5))
fig.add_subplot(1,3,1)
x = make_moons(n_samples=1000, noise=0)
plt.plot(x[0][:,0], x[0][:,1], 'b.')
plt.title('Make_moons(noise=0)')

fig.add_subplot(1,3,2)
x = make_moons(n_samples=1000, noise=0.1)
plt.plot(x[0][:,0], x[0][:,1], 'b.')
plt.title('Make_moons(noise=0.1)')

fig.add_subplot(1,3,3)
x = make_moons(n_samples=1000, noise=0.5)
plt.plot(x[0][:,0], x[0][:,1], 'b.')
plt.title('Make_moons(noise=0.5)')

在這裏插入圖片描述

2.3 make_moons

用來生成環形數據,用法示例:

x = make_circles(n_samples=1000, noise=0.05, factor=0.6)

示例:

from sklearn.datasets import make_circles
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure(figsize=(15,5))
fig.add_subplot(1,3,1)
x = make_circles(n_samples=1000, noise=0)
plt.plot(x[0][:,0], x[0][:,1], 'b.')
plt.title('make_circles(noise=0)')

fig.add_subplot(1,3,2)
x = make_circles(n_samples=1000, noise=0.05, factor=0.6)
plt.plot(x[0][:,0], x[0][:,1], 'b.')
plt.title('make_circles(noise=0.1)')

fig.add_subplot(1,3,3)
x = make_circles(n_samples=1000, noise=0.5)
plt.plot(x[0][:,0], x[0][:,1], 'b.')
plt.title('make_circles(noise=0.5)')

在這裏插入圖片描述

2.4 make_blobs

用來生成簇數據,用法示例:

x = make_blobs(n_samples=1000, n_features=2, centers = 3)

示例:

from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure(figsize=(15,5))
fig.add_subplot(1,3,1)
x = make_blobs(n_samples=1000, n_features=2, centers = 2)
plt.plot(x[0][:,0], x[0][:,1], 'b.')
plt.title('make_blobs')

fig.add_subplot(1,3,2)
x = make_blobs(n_samples=1000, n_features=2, centers = 3)
plt.plot(x[0][:,0], x[0][:,1], 'b.')
plt.title('make_blobs')

fig.add_subplot(1,3,3)
x = make_blobs(n_samples=1000, n_features=2, centers = 4)
plt.plot(x[0][:,0], x[0][:,1], 'b.')
plt.title('make_blobs')

在這裏插入圖片描述

3 使用sklearn聚類示例

使用常用的六個聚類算法,並將聚類結果使用PCA降維後可視化。

3.1 簇數據聚類

from sklearn.datasets import make_circles
import matplotlib.pyplot as plt
import numpy as np
from sklearn.cluster import KMeans, DBSCAN, SpectralClustering, Birch, MeanShift, AgglomerativeClustering
from sklearn.decomposition import PCA
import pandas as pd

x = make_blobs(n_samples=1000, n_features=2, centers = 4, cluster_std=2.0)[0]
pca = PCA(n_components=2)
pca_result = pca.fit_transform(x)
fig = plt.figure(figsize=(15,10))

model = AgglomerativeClustering(n_clusters = 4)
y_pred = model.fit_predict(x)
fig.add_subplot(2,3,1)
plt.scatter(pca_result[:,0], pca_result[:,1], marker='.', c=y_pred)
plt.title('AgglomerativeClustering')

model = KMeans(n_clusters = 4, random_state=10)
y_pred = model.fit_predict(x)
fig.add_subplot(2,3,2)
plt.scatter(pca_result[:,0], pca_result[:,1], marker='.', c=y_pred)
plt.title('KMeans')

model = DBSCAN(eps=0.1)
model.fit(x)
y_pred = model.labels_
fig.add_subplot(2,3,3)
plt.scatter(pca_result[:,0], pca_result[:,1], marker='.', c=y_pred)
plt.title('DBSCAN')

model = SpectralClustering(n_clusters = 4, random_state=10)
y_pred = model.fit_predict(x)
fig.add_subplot(2,3,4)
plt.scatter(pca_result[:,0], pca_result[:,1], marker='.', c=y_pred)
plt.title('SpectralClustering')

model = Birch(n_clusters=4)
model.fit(x)
y_pred = model.labels_
fig.add_subplot(2,3,5)
plt.scatter(pca_result[:,0], pca_result[:,1], marker='.', c=y_pred)
plt.title('Birch')

model = MeanShift()
model.fit(x)
y_pred = model.labels_
fig.add_subplot(2,3,6)
plt.scatter(pca_result[:,0], pca_result[:,1], marker='.', c=y_pred)
plt.title('MeanShift')

在這裏插入圖片描述

3.2 月牙形數據聚類

代碼基本類似,直接放結果了

在這裏插入圖片描述

3.3 環形數據聚類

在這裏插入圖片描述

4 各聚類算法在各種簇分佈下的聚類效果對比

這裏使用sklearn官方的示例代碼演示一下效果。

print(__doc__)

import time

import numpy as np
import matplotlib.pyplot as plt

from sklearn import cluster, datasets
from sklearn.neighbors import kneighbors_graph
from sklearn.preprocessing import StandardScaler

np.random.seed(0)

# Generate datasets. We choose the size big enough to see the scalability
# of the algorithms, but not too big to avoid too long running times
n_samples = 1500
noisy_circles = datasets.make_circles(n_samples=n_samples, factor=.5,
                                      noise=.05)
noisy_moons = datasets.make_moons(n_samples=n_samples, noise=.05)
blobs = datasets.make_blobs(n_samples=n_samples, random_state=8)
no_structure = np.random.rand(n_samples, 2), None

colors = np.array([x for x in 'bgrcmykbgrcmykbgrcmykbgrcmyk'])
colors = np.hstack([colors] * 20)

clustering_names = [
    'MiniBatchKMeans', 'AffinityPropagation', 'MeanShift',
    'SpectralClustering', 'Ward', 'AgglomerativeClustering',
    'DBSCAN', 'Birch']

plt.figure(figsize=(len(clustering_names) * 2 + 3, 9.5))
plt.subplots_adjust(left=.02, right=.98, bottom=.001, top=.96, wspace=.05,
                    hspace=.01)

plot_num = 1

datasets = [noisy_circles, noisy_moons, blobs, no_structure]
for i_dataset, dataset in enumerate(datasets):
    X, y = dataset
    # normalize dataset for easier parameter selection
    X = StandardScaler().fit_transform(X)

    # estimate bandwidth for mean shift
    bandwidth = cluster.estimate_bandwidth(X, quantile=0.3)

    # connectivity matrix for structured Ward
    connectivity = kneighbors_graph(X, n_neighbors=10, include_self=False)
    # make connectivity symmetric
    connectivity = 0.5 * (connectivity + connectivity.T)

    # create clustering estimators
    ms = cluster.MeanShift(bandwidth=bandwidth, bin_seeding=True)
    two_means = cluster.MiniBatchKMeans(n_clusters=2)
    ward = cluster.AgglomerativeClustering(n_clusters=2, linkage='ward',
                                           connectivity=connectivity)
    spectral = cluster.SpectralClustering(n_clusters=2,
                                          eigen_solver='arpack',
                                          affinity="nearest_neighbors")
    dbscan = cluster.DBSCAN(eps=.2)
    affinity_propagation = cluster.AffinityPropagation(damping=.9,
                                                       preference=-200)

    average_linkage = cluster.AgglomerativeClustering(
        linkage="average", affinity="cityblock", n_clusters=2,
        connectivity=connectivity)

    birch = cluster.Birch(n_clusters=2)
    clustering_algorithms = [
        two_means, affinity_propagation, ms, spectral, ward, average_linkage,
        dbscan, birch]

    for name, algorithm in zip(clustering_names, clustering_algorithms):
        # predict cluster memberships
        t0 = time.time()
        algorithm.fit(X)
        t1 = time.time()
        if hasattr(algorithm, 'labels_'):
            y_pred = algorithm.labels_.astype(np.int)
        else:
            y_pred = algorithm.predict(X)

        # plot
        plt.subplot(4, len(clustering_algorithms), plot_num)
        if i_dataset == 0:
            plt.title(name, size=18)
        plt.scatter(X[:, 0], X[:, 1], color=colors[y_pred].tolist(), s=10)

        if hasattr(algorithm, 'cluster_centers_'):
            centers = algorithm.cluster_centers_
            center_colors = colors[:len(centers)]
            plt.scatter(centers[:, 0], centers[:, 1], s=100, c=center_colors)
        plt.xlim(-2, 2)
        plt.ylim(-2, 2)
        plt.xticks(())
        plt.yticks(())
        plt.text(.99, .01, ('%.2fs' % (t1 - t0)).lstrip('0'),
                 transform=plt.gca().transAxes, size=15,
                 horizontalalignment='right')
        plot_num += 1

plt.show()

在這裏插入圖片描述

5 聚類算法的模型評估指標

本節參考自:【機器學習】菜菜的sklearn課堂06 - 聚類算法與KMeans

不同於分類模型和迴歸,聚類算法的模型評估不是一件簡單的事情。在分類問題中,有直接結果作爲輸出,而且分類問題的結果有正誤之分,所以可以使用預測準確度,混淆矩陣,ROC曲線等指標來進行評估。在迴歸問題中有均方誤差,RSE,MSE等損失函數衡量模型的擬合程度。那麼如何衡量聚類算法的效果呢?

面試高危問題:如何衡量聚類算法的效果?
像KMeans這種聚類算法,其目標是確保“簇內差異小,簇間差異大“,因此可以通過衡量簇內差異來衡量聚類的效果,sklearn自帶的inertia就是用距離來衡量簇內差異的指標,因此能否使用inertia來作爲聚類的衡量指標呢?inertia越小模型就越好嗎?

inertia可以作爲指標,但是這個指標的缺點和極限太大。

  • 它不是有界的,我們只知道inertia越小越好,但是我們並不知道一個較小的inertia是否已經達到了模型的極限
  • 它的計算太容易收到特徵數目的影響,數據維度很大的時候,inertia的計算量會爆炸,不適合評估模型
  • inertia對數據的分佈有假設,假設數據滿足凸分佈,並且假設數據是各向同性的(isotropic),即數據的屬性在不同方向上代表着相同的含義。但是現實中的數據往往不是這樣的。所以使用inertia作爲評估指標,會讓聚類算法在一些細長簇,環形簇或者不規則性狀的流行時表現不佳。

那麼可以使用什麼指標呢?

5.1 當真實標籤已知的時候

雖然聚類的過程中不輸入真實標籤,但這不代表數據集中沒有真實標籤。如果擁有真實的標籤,我們更傾向於使用分類算法,但仍然可以使用聚類算法對其進行分類。常用的衡量帶標籤數據聚類效果的方法有以下三種:

模型評估指標 說明
互信息分

普通互信息分
metrics.adjusted_mutual_info_score(y_pred, y_true)
調整的互信息分
metrics.mutual_info_score(y_pred, y_true)
標準化互信息分
metrics.normalized_mutual_info_score(y_pred, y_true)
取值範圍在(0,1),越接近1聚類效果越好
V-measure:基於條件上分析的一系列直觀度量

同質性、完整性、同質性和完整性的調和平均(V-measure)
metrics.homogeneity_score(y_true, y_pred)
metrics.completeness_score(y_true, y_pred)
metrics.v_measure(y_true, y_pred)
取值範圍在(0,1),越接近1聚類效果越好
調整蘭德係數

metrics.adjusted_rand_score(y_true, y_pred)
取值在(-1,1)之間,負值象徵着簇內的點差異巨大,越接近1表示聚類效果越好。

5.2 當真實標籤未知的時候:輪廓係數

5.2.1 輪廓係數介紹

在99%的情況下,我們是對沒有真實標籤的數據進行探索,並不知道數據的真實類別。這樣的聚類,是完全依賴於評價簇內的稠密程度(簇內差異小)和簇間的離散程度(簇間差異大)來評估聚類的效果,其中輪廓係數是最常用的聚類算法的評價指標。它是對每個樣本來定義的,能夠同時衡量:

  • 樣本與其自身所在的簇中的其他樣本的相似度a,等於樣本與統一簇中所有其他點之間的平均距離
  • 樣本與其他簇中樣本的相似度b,等於樣本與下一個最近的簇中的所有點之間的平均距離

我們希望b永遠大於a,並且越大越好,因此單個樣本的輪廓係數計算爲:
s=bamax(a,b) s=\frac{b-a}{max(a,b)}
這個公式可以被解析爲:如果a<b,s=1a/ba<b,s=1-a/b,如果a=b,s=0a=b,s=0,如果a>b,s=b/a1a>b,s=b/a-1。因此輪廓係數的範圍是(-1,1),其值越接近1表示樣本與自己所在的簇中的樣本很相似,並且與其他簇中的樣本不相似,當樣本點與簇外的樣本更相似的時候,輪廓係數爲負數。當輪廓係數爲0的時候,表示兩個簇中的樣本相似度一致,兩個簇本應該是一個簇。因此輪廓係數越接近1越好,越大越好

在sklearn中可以使用metrics中的silhouette_score來計算輪廓係數,它返回的是一個數據集中所有樣本的輪廓係數的均值。在metrics中的sihouette_samples的參數餘輪廓係數一致,能夠返回數據集中每個樣本自己的輪廓係數。用法如下:

from sklearn.metrics import silhouette_score
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans

x = make_blobs(n_samples=1000, n_features=2, centers = 4, cluster_std=2.0)[0]
y_pred = KMeans(n_clusters = 4, random_state=10).fit_predict(x)
silhouette_score(x, y_pred)

# >>> 0.4359497951008228

輪廓係數有很多優點,它在有限空間中取值,是的我們對模型的聚類效果有一個參考。並且輪廓係數對數據的分佈沒有假設,因此在很多的數據集上都表現良好,它在每一個簇的分割比較清晰時表現最好。

但是輪廓係數也有缺陷,它在凸型的數據上表面會虛高,比如基於密度進行聚類,或通過DBSCAN獲得的聚類結果,如果使用輪廓係數來衡量,會表現出比真是聚類效果更高的分數。

5.2.2 使用輪廓係數可視化圖像輔助選擇簇的個數

這裏參考【機器學習】菜菜的sklearn課堂06 - 聚類算法與KMeans提供的可視化代碼,能夠生成很完整且漂亮的可視化圖片,如下圖所示。關於生該圖的完整代碼見我的下一篇博客:
在這裏插入圖片描述

5.3 當真實標籤未知時的其他評價指標

除了輪廓係數是最常用的,還有卡林斯基-哈拉巴斯指數(Calinski-Harabaz index,簡稱CHI,也被稱爲方差比標準),戴維斯-布爾丁指數(Davies-Bouldin)以及權變矩陣(Contingency Matrix)可以使用。

標籤未知時的評估指標
卡林斯基-哈拉巴斯指數:sklearn.metrics.calinski_harabaz_score(x, y_pred)
戴維斯-布爾丁指數:sklearn.metrics.davies_bouldin_score(x, y_pred)
權變矩陣:sklearn.metrics.cluster.contingency_matrix(x, y_pred)

(完)

發佈了129 篇原創文章 · 獲贊 118 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章