北京中科聚网信息技术有限公司
大数据服务
AI人工智能服务
标准大数据服务
汽车大数据服务
融媒体大数据服务

聚类分析:创建,可视化以及可解释性

2021-01-18

来源:

作者: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)
聚类分析:创建,可视化以及可解释性


我们可以看到,与我们之前做的方差分析相比,选择了类似的特征。由于这种方法需要更多的验证工作,我建议使用前面描述的方差方法。


北京总部
  • 北京市海淀区大钟寺13号华杰大厦B座3层306-310室
  • 010-56181910
郑州子公司
  • 郑州市金水区紫荆山路5号
  • 0371-60921991
石家庄办事处
  • 石家庄市新华路294号盛安大厦5楼
  • 18633018987
Copyright 2011-2021 All Rights Reserved | 京ICP备13013562号

微信号

抖音号