聚類分析:創建,可視化以及可解釋性

作者:Maarten Grootendorst

編譯:ronghuaiyang

導讀

本文探索了聚類分析,聚類的可視化以及通過探索特徵進行聚類的可解釋性。

儘管我們已經看到大量的監督機器學習技術被應用,但這些方法通常存在一個大問題,需要標記的數據。幸運的是,有許多非監督方法用於將數據聚類到以前不可見的組中,從而可以從你的客戶中提取新的見解。

本文將指導你瞭解客戶聚類的來龍去脈。注意,我不僅將向你展示使用哪個sklearn包,而且更重要的是,如何使用它們,以及應該注意什麼。

與往常一樣,數據相對簡單,它包含來自電信公司的客戶信息,通常用於預測客戶流失:

聚類分析:創建,可視化以及可解釋性

聚類算法

目前有許多無監督聚類算法,儘管它們在某些情況下都有顯著的優勢,但我將討論兩種常用的算法。

k-Means聚類

根據我的經驗,這是迄今為止最常用的數據聚類算法。k-意味著從選擇k個隨機中心開始,你可以自己設置。然後,根據數據點的歐氏距離,將所有數據點分配到最近的中心。接下來,計算新的中心並更新數據點(參見下面的gif)。這個過程是連續的,直到聚類在迭代之間沒有變化為止。

聚類分析:創建,可視化以及可解釋性

在上面的例子中,三個聚類中心的起點非常接近。這通常不能很好地工作,因為它很難找到聚類。相反,你可以使用k-means++來改進中心的初始化。它從一個初始中心開始,並確保所有後續中心都足夠遠。這優化了中心的選擇和創建。

然後,你可以使用elbow方法確定最優聚類的個數k。在選擇聚類個數的範圍時,你希望找到收益遞減點。你可以通過繪製x軸上的聚類數量和y軸上的慣量(簇內平方和)來實現這一點。然後通過拐點找到k:

import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
scores = [KMeans(n_clusters=i+2).fit(df).inertia_ for i in range(10)]
sns.lineplot(np.arange(2, 12), scores)
plt.xlabel('Number of clusters')
plt.ylabel("Inertia")
plt.title("Inertia of k-Means versus number of clusters")
聚類分析:創建,可視化以及可解釋性

你可以看到橘色矩形上的轉折。因此,我們選擇使用k-means生成k=4個聚類。

需要注意的一點是,由於k-Means通常使用歐氏距離來計算距離,由於維數詛咒,它不能很好地處理高維數據集。這一詛咒在一定程度上表明,高維歐幾里德距離沒有什麼意義,因為它們通常非常接近。

我們使用的數據是高維的,因為我們有27個特性。

一個解決方案是使用cos距離,它在高維空間中工作得更好。由於餘弦距離和歐幾里德距離對於歸一化向量是線性一致的,我們可以簡單地對數據進行歸一化。

from sklearn import preprocessing
normalized_vectors = preprocessing.normalize(df)
scores = [KMeans(n_clusters=i+2).fit(normalized_vectors).inertia_ for i in range(10)]
sns.lineplot(np.arange(2, 12), scores)
plt.xlabel('Number of clusters')
plt.ylabel("Inertia")
plt.title("Inertia of Cosine k-Means versus number of clusters")
plt.savefig("intertia_cosine_kmeans.jpg", dpi=300)
聚類分析:創建,可視化以及可解釋性

k-Means在計算上非常昂貴。更快的替代方法是MiniBatchKMeans和BIRCH。這兩種方法生成聚類的速度都更快,但是這些聚類的質量通常低於k-Means生成的聚類。

DBSCAN

還可以根據數據點的密度進行聚類。一個例子是基於密度的帶噪聲的空間聚類(DBSCAN),如果數據點的密度足夠大,DBSCAN就會對它們進行聚類。DBSCAN通過掃描鄰域來識別聚類並擴展它們。如果它找不到任何要添加的點,它就會繼續移動到一個新點,希望找到一個新的聚類。任何缺少足夠鄰居聚類的點都被歸為噪聲:

聚類分析:創建,可視化以及可解釋性

與k-means的不同之處在於,DBSCAN不需要指定聚類的數量。DBSCAN的兩個主要參數是組成聚類的最小點數(minPts)和鄰域大小(eps)。

您通常不希望 minPts非常小,因為會生成來自噪聲的聚類。根據經驗,最好將 minPts設置為數據中特徵的數量。eps優化起來有點困難,可能需要k-distance圖才能找到正確的值。使用小值通常是首選。

DBSCAN的另一個替代方案是光學,它具有與DBSCAN類似的性能,但不需要顯式地設置 eps。

評估聚類

下一步是執行實際的聚類,並嘗試解釋聚類的質量及其內容。

輪廓評分

要開始評估聚類,首先需要了解構成一個良好聚類的要素。儘管存在許多用於評估聚類的定義和方法,但最常用的方法之一是計算輪廓評分。

坤坤評分根據簇之間和簇內的距離來衡量簇之間的可分性。它計算平均簇內距離( a ),這是簇內部的平均距離,以及最接近的其他簇的平均距離( b),這是每個樣本和距離它最近的簇之間的距離。則樣本的輪廓係數為 (b-a)/max(a,b)。

讓我們計算一下前面提到的所有方法的輪廓分數:

from sklearn.metrics import silhouette_score
# Prepare models
kmeans = KMeans(n_clusters=4).fit(df)
normalized_vectors = preprocessing.normalize(df)
normalized_kmeans = KMeans(n_clusters=4).fit(normalized_vectors)
min_samples = df.shape[1]+1
dbscan = DBSCAN(eps=3.5, min_samples=min_samples).fit(df)
# Print results
print('kmeans: {}'.format(silhouette_score(df, kmeans.labels_, metric='euclidean')))
print('Cosine kmeans:{}'.format(silhouette_score(normalized_vectors,
normalized_kmeans.labels_,
metric='cosine')))
print('DBSCAN: {}'.format(silhouette_score(df, dbscan.labels_, metric='cosine')))
聚類分析:創建,可視化以及可解釋性

基於餘弦的k-Means優於k-Means,這並不奇怪,因為我們在數據中有大量的特徵(27個)。有趣的是,DBSCAN同樣執行得很好。

然而,儘管客觀的度量方法是首選的,但我認為,當涉及到無監督聚類時,可視化地檢查聚類是評估它們的最佳方法之一。不要盲目地遵循客觀標準。你一定要看到一下到底發生了什麼!

因此,下一步是在2d和3d中可視化聚類的方法。

可視化聚類

為了聚類可視化,可以使用最流行的降維方法之一,即PCA和t-SNE。

PCA

PCA的工作原理是利用正交變換將相關特徵轉換為一組線性無關特徵的值。剩下的是包含最大可能方差的特徵。

然後,我們可以用3d的方式可視化我們的數據:

tsne_3d_df = prepare_tsne(3, df, kmeans.labels_)
tsne_3d_df['normalized_kmeans'] = normalized_kmeans.labels_
tsne_3d_df['dbscan'] = dbscan.labels_
plot_animation(tsne_3d_df, 'kmeans', 'kmeans')
plot_animation(tsne_3d_df, 'normalized_kmeans', 'normalized_kmeans')
plot_animation(tsne_3d_df, 'dbscan', 'dbscan')
聚類分析:創建,可視化以及可解釋性

雖然PCA可能成功地降低了數據的維數,但它似乎沒有非常直觀地可視化聚類。這種情況經常發生在高維數據中,它們通常聚集在同一個點上,PCA提取這些信息。

我們可以使用一種稱為t-SNE的算法,它專門用來創建數據的直觀表示/可視化。

t-SNE

t-SNE是一種可視化高維數據的算法。它使用點之間的局部關係來創建低維映射,從而捕獲非線性結構。

它首先創建一個概率分佈(即,高斯函數),它規定了相鄰點之間的關係。然後,利用學生t分佈構造一個儘可能接近該分佈的低維空間。現在您可能想知道為什麼在這個步驟中使用學生t分佈。高斯分佈有一個短尾巴,它把附近的點擠壓在一起。如果你用的是學生t分佈比尾巴長點更有可能被分開。

讓我們在3d中實現t-SNE,看看我們是否可以更好地可視化聚類:

tsne_3d_df = prepare_tsne(3, df, kmeans.labels_)
tsne_3d_df['normalized_kmeans'] = normalized_kmeans.labels_
tsne_3d_df['dbscan'] = dbscan.labels_
plot_animation(tsne_3d_df, 'kmeans', 'kmeans')
plot_animation(tsne_3d_df, 'normalized_kmeans', 'normalized_kmeans')

plot_animation(tsne_3d_df, 'dbscan', 'dbscan')
聚類分析:創建,可視化以及可解釋性

Euclidean k-Means (LEFT), Cosine k-Means (MIDDLE), DBSCAN, (RIGHT)

t-SNE為數據提供了更直觀的可視化表示。從動畫中可以看出,cos k-Means和DBSCAN似乎都創建了邏輯聚類。

解釋聚類

現在我們已經對客戶進行了細分,如果我們知道每個聚類的獨特之處就好了。這將幫助我們瞭解我們擁有哪些類型的客戶。

一種方法是簡單地繪製所有變量並查看聚類之間的差異。然而,當處理超過10個變量時,這種方法就會失敗,因為它很難可視化和解釋:

聚類分析:創建,可視化以及可解釋性

解決方案是選擇變量的子集,在一定程度上,這些變量在定義聚類時非常重要。這裡我想演示兩種方法,一種是平均組間的方差,另一種是通過預測建模來提取特徵的重要性。

聚類內部和聚類之間的變量的方差

聚類任務中變量重要性的一個假設是,如果按聚類排序的變量的平均值彼此之間存在顯著差異,那麼該變量在創建聚類時可能很重要。

我們首先簡單地根據生成的聚類聚合數據,並檢索每個變量的平均值:

from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
df_scaled = pd.DataFrame(scaler.fit_transform(df))
df_scaled['dbscan'] = dbscan.labels_
df_mean = (df_scaled.loc[df_scaled.dbscan!=-1, :].groupby('dbscan').mean())
聚類分析:創建,可視化以及可解釋性

DBSCAN生成的聚類中每個變量的均值

我忽略了-1集群,因為它被DBSCAN定義為噪聲。為了便於可視化,數據在0和1之間縮放。

接下來,我簡單地計算每個變量聚類間均值的方差,並選取方差最大的前7個變量:

results = pd.DataFrame(columns=['Variable', 'Var'])
for column in df_mean.columns[1:]:
results.loc[len(results), :] = [column, np.var(df_mean[column])]
selected_columns = list(results.sort_values('Var', ascending=False,).head(7).Variable.values) + ['dbscan']
tidy = df_scaled[selected_columns].melt(id_vars='dbscan')
sns.barplot(x='dbscan', y='value', hue='variable', data=tidy)
聚類分析:創建,可視化以及可解釋性

現在你可以更清楚地看到聚類之間的差異。例如,在cluster 0中,你可以看到每個人都沒有Internet服務,而大多數其他聚類都包含具有Internet服務的特徵。此外,我們可以看到聚類2只包含光纖和電話服務的人員,這意味著這兩種服務要麼一起購買,要麼屬於同一個包。

:在比較變量時,我沒有考慮標準差、偏度和峰度等重要因素。上面的方法只是選擇變量的第一步。

隨機森林特徵選取

最後,我們可以使用聚類作為目標變量,然後應用隨機森林來了解哪些特徵在聚類的生成中是重要的。這種方法需要更多的工作,因為你必須檢查模型的準確性,才能準確地提取重要的特徵。

在這個例子中,我將跳過這一步,因為我們處理的是不平衡的目標和多個類:

from sklearn.ensemble import RandomForestClassifier
X, y = df.iloc[:,:-1], df.iloc[:,-1]
clf = RandomForestClassifier(n_estimators=100).fit(X, y)
data = np.array([clf.feature_importances_, X.columns]).T
columns = list(pd.DataFrame(data, columns=['Importance', 'Feature'])
.sort_values("Importance", ascending=False)
.head(7).Feature.values)
tidy = df_scaled[columns+['dbscan']].melt(id_vars='dbscan')
sns.barplot(x='dbscan', y='value', hue='variable', data=tidy)
聚類分析:創建,可視化以及可解釋性

我們可以看到,與我們之前做的方差分析相比,選擇了類似的特徵。由於這種方法需要更多的驗證工作,我建議使用前面描述的方差方法。

總結

希望本文能夠幫助你開始理解聚類算法背後的原則,以及最重要的是如何應用它們。

英文原文:https://towardsdatascience.com/cluster-analysis-create-visualize-and-interpret-customer-segments-474e55d00ebb

聚類分析:創建,可視化以及可解釋性


分享到:


相關文章: