聚类算法的对比与评估

1、用真实值评估聚类(ARI)

1.1 ARI(调整rand指数)

🌺有一些指标可用于评估聚类算法相对于真实聚类的结果,其中最重要的是调整rand指数归一化互信息

  • 二者都给出了定量的度量,其最佳值为1,0表示不相关的聚类(虽然ARI可以取负值)。

📐下面我们使用ARI来比较k均值,凝聚聚类和DBSCAN算法。

  X, y = make_moons(n_samples=200,noise=0.05,random_state=0)

  #将数据缩放成平均值
  scaler = StandardScaler()
  scaler.fit(X)
  X_scaled = scaler.transform(X)

  #列出要使用的算法
  fig, axes = plt.subplots(1,4,figsize=(15,3),subplot_kw={'xticks':(),'yticks':()})
  algorithms = [KMeans(n_clusters=2),AgglomerativeClustering(n_clusters=2),DBSCAN()]

  #创建一个随机簇分配
  random_state = np.random.RandomState(seed=0)
  random_clusters = random_state.randint(low=0,high=2,size=len(X))

  #绘制随机分配
  axes[0].scatter(X_scaled[:,0],X_scaled[:,1],c=random_clusters,cmap=mglearn.cm3,s=60)
  axes[0].set_title("Random Assignment - ARI:{:.2f}".format(adjusted_rand_score(y,random_clusters)))

  for ax, algorithm, in zip(axes[1:],algorithms):
      clusters = algorithm.fit_predict(X_scaled)
      ax.scatter(X_scaled[:,0],X_scaled[:,1],c=clusters,cmap=mglearn.cm3,s=60)
      ax.set_title("{}-ARI:{:.2f}".format(str(algorithm),adjusted_rand_score(y,clusters)))

📣

调整rand参数给出了符合直觉的结果,随机簇分配的分数约等于0,而DBSCAN(完美找到了期望中的聚类)的分数为1.

👍

用这种方法评估聚类时,一个常见的错误是使用accuracy_score(之前在监督学习中,用于分类器评分),而不是adjusted_rand_score或者其它聚类指标。
使用精度的问题在于:它要求分配的簇标签与真实值完全匹配。

  • 但簇标签本身毫无意义——唯一重要的是哪些点位于同一个簇中。

2、没有真实值的情况下评估聚类(轮廓系数)

🌺在实践中,使用诸如ARI之类的指标有一个很大的问题。

  • 在应用聚类算法时,通常没有真实值来比较结果。
  • 如果我们知道了数据的正确聚类,那么可以使用这一信息构建一个监督模型(比如分类器)。
  • 因此,使用类似ARI和NMI的指标通常仅有助于开发算法,但对评估应用是否成功没有帮助。

🌺有一些聚类的评分指标不需要真实值,比如轮廓系数

  • 轮廓分数计算一个簇的紧致度,其值越大越好,最高分数为1.
  • 虽然紧致的簇很好,但紧致度不允许复杂的形状,它们在实践中的效果并不好。

2.1 轮廓系数

📐下面我们将用轮廓系数在make_moons数据集上比较k均值,凝聚聚类和DBSCAN

  #轮廓系数silhouette_score(样本点,预测的标签)

  from sklearn.metrics.cluster import silhouette_score


  fig, axes = plt.subplots(1,4,figsize=(15,3),subplot_kw={'xticks':(),'yticks':()})

  #绘制随机分配
  axes[0].scatter(X_scaled[:,0],X_scaled[:,1],c=random_clusters,cmap=mglearn.cm3,s=60)
  axes[0].set_title("Random Assignment:{:.2f}".format(silhouette_score(X_scaled,random_clusters)))


  for ax, algorithm, in zip(axes[1:],algorithms):
      clusters = algorithm.fit_predict(X_scaled)
      ax.scatter(X_scaled[:,0],X_scaled[:,1],c=clusters,cmap=mglearn.cm3,s=60)
      ax.set_title("{}-ARI:{:.2f}".format(str(algorithm),silhouette_score(X_scaled,clusters)))

📣
k均值的轮廓分数最高,尽管我们可能更喜欢DBSCAN的结果

👍
(1)
对于评估聚类,稍好的策略是使用基于鲁棒性的聚类指标,

  • 这种指标先向数据中添加一些噪声,或者使用不同的参数设定,然后运用算法,并对结果进行比较。
  • 其思想是,如果许多算法参数和许多数据扰动返回相同的结果,那么它很可能是可信的。

(2)
即使我们得到一个鲁棒性很好的聚类或者非常高的轮廓分数,但仍然不知道聚类中是否有任何语义含义,或者聚类是否反应了数据中我们感兴趣的某个方面。

  • 要想知道聚类是否对应于我们感兴趣的内容,唯一的办法就是对簇进行人工分析。

3、在人脸数据集上比较算法

🌺人脸数据集要先处理一下

  people = fetch_lfw_people(min_faces_per_person=20,resize=0.7)
  image_shape = people.images[0].shape

  # 降低数据偏斜,每个人最多取50张图像

  people = fetch_lfw_people(min_faces_per_person=20,resize=0.7)
  image_shape = people.images[0].shape

  # 降低数据偏斜,每个人最多取50张图像

  mask = np.zeros(people.target.shape,dtype=np.bool)

  for target in np.unique(people.target):
      mask[np.where(people.target==target)[0][:50]]=1 #按条件查找数组元素并返回索引——np.where()

  X_people = people.data[mask] #mask是一个bool列表,将显示True的行选出来做X_people
  y_people = people.target[mask]

  X_people=X_people / 255 #把灰度值缩放到0-1之间

  X_train,X_test,y_train,y_test=train_test_split(X_people,y_people,random_state=0)

  #从lfw中提取特征脸,并对数据进行transform
  pca = PCA(n_components=100,whiten=True,random_state=0)
  X_pca = pca.fit_transform(X_people)

3.1 用DBSCAN分析人脸数据集

  #用DBSCANF分析人脸数据集
  dbscan = DBSCAN()
  labels = dbscan.fit_predict(X_pca)
  print("Unique labels:{}".format(np.unique(labels)))

📣

我们看到,所有返回的标签都是-1,因此所有数据都被DBSCAN标记为“噪声”。我们可以通过增大eps从而扩展每个点的领域,或者减小min_samples,得到更小的点组视为簇。

  #增大eps
  for eps in [1,3,5,7,9,11,13]:
      print("eps={}".format(eps))
      dbscan = DBSCAN(eps=eps)
      labels = dbscan.fit_predict(X_pca)
      print("clusters present:{}".format(np.unique(labels)))
      print("clusters sizes:{}".format(np.bincount(labels+1)))

  #将eps=7中13个较小的簇中的点全部可视化,以详细研究这一聚类结果

  dbscan = DBSCAN(eps=7,min_samples=3)
  labels = dbscan.fit_predict(X_pca)

  for cluster in range(max(labels)+1):
      mask = labels==cluster
      print('mask:{}'.format(mask))
      
      n_images = np.sum(mask)
      
      fig, axes = plt.subplots(1,n_images,figsize=(n_images*1.5,4),
                              subplot_kw={'xticks':(),'yticks':()})
      for image, label, ax in zip(X_people[mask],y_people[mask],axes):
          ax.imshow(image.reshape(image_shape),vmin=0,vmax=1)
          ax.set_title(people.target_names[label].split()[-1])

3.2 用KMeans分析人脸数据集

🌺用DBSCAN无法创建多于一个较大的簇,凝聚聚类和KMeans可以创建大小更均匀的簇

  #用K均值分析人脸数据集

  #KMeans提取簇

  km = KMeans(n_clusters=10,random_state=0)
  labels_km = km.fit_predict(X_pca)
  print("Cluster sizes k-means:{}".format(np.bincount(labels)))

  #将簇中心进行可视化
  #先将聚类中心旋转回原始空间

  fig, axes = plt.subplots(1,10,figsize=(12,4),subplot_kw={'xticks':(),'yticks':()})

  for ax, center in zip(axes.ravel(),km.cluster_centers_):
      ax.imshow(pca.inverse_transform(center).reshape(image_shape),vmin=0,vmax=1)

📣
KMeans找到的簇中心是非常平滑的人脸。

  mglearn.plots.plot_kmeans_faces(km,pca,X_pca,X_people,y_people,people.target_names)

📣
上图为每个簇找到的样本图像

  • 簇中心在最左边,然后是5个距离中心最近的点,然后是5个距离最远的点

👍
利用更多数量的簇,算法可以找到更细微的区别,但会使得人工检查更困难

3.3 用凝聚聚类分析人脸数据集

  #用凝聚聚类分析人脸数据集

  from sklearn.cluster import AgglomerativeClustering


  #输出用agglomerative聚类的每个簇的样本点个数
  agglomerative = AgglomerativeClustering(n_clusters=10)
  labels_agg = agglomerative.fit_predict(X_pca)

  print("Cluster sizes agglomerative clustering:{}".format(np.bincount(labels)))

  from scipy.cluster.hierarchy import ward,dendrogram


  linkage_array = ward(X_pca)
  plt.figure(figsize=(20,5))
  dendrogram(linkage_array,p=7,truncate_mode='level',no_labels=True)
  plt.xlabel("sample index")
  plt.ylabel("Cluster distance")

📣
对于人脸数据集而言,似乎没有非常自然的切割点。

  #将agglomerative的10个簇可视化

  for label in np.unique(labels_agg):
      mask = labels_agg==label #依次取出每一个簇的所有样本
      fig,axes = plt.subplots(1,10,figsize=(15,8),subplot_kw={"xticks":(),'yticks':()})
      axes[0].set_ylabel(np.sum(mask))
      
      for ax, image, ylabel in zip(axes.ravel(),X_people[mask],y_people[mask]):
          ax.imshow(image.reshape(image_shape),vmin=0,vmax=1)
          ax.set_title(people.target_names[ylabel].split()[-1])

  #用agglomerative,这次使用40个簇,并挑选一些有趣的簇进行可视化

  #训练模型并预测
  agglomerative = AgglomerativeClustering(n_clusters=40)
  labels_agg = agglomerative.fit_predict(X_pca)

  n_clusters = 40

  for cluster in [6,13,22,38,39]:
      mask = labels_agg==cluster
      cluster_size = np.sum(mask)
      
      fig,axes = plt.subplots(1,15,figsize=(15,8),subplot_kw={"xticks":(),'yticks':()})
      axes[0].set_ylabel(np.sum(mask))
      
      for ax, image, ylabel in zip(axes.ravel(),X_people[mask],y_people[mask]):
          ax.imshow(image.reshape(image_shape),vmin=0,vmax=1)
          ax.set_title(people.target_names[ylabel].split()[-1])
      for i in range(cluster_size,15):
          axes[i].set_visible(False)

📣
上图为将簇的数量设为40时,从聚类找到的簇中挑选的图像

4、参考文献

《python机器学习基础教程》

posted @ 2022-05-07 12:25  朝南烟  阅读(720)  评论(0编辑  收藏  举报
body { color: #000; background-color: #e6e6e6; font-family: "Helvetica Neue",Helvetica,Verdana,Arial,sans-serif; font-size: 12px; min-height: 101%; background: url(https://images.cnblogs.com/cnblogs_com/caolanying/1841633/o_2009041…ly1geq8oc9owbj21hc0u0th5.jpg) fixed; } #home { margin: 0 auto; opacity: 0.8; width: 65%; min-width: 1080px; background-color: #fff; padding: 30px; margin-top: 50px; margin-bottom: 50px; box-shadow: 0 2px 6px rgba(100, 100, 100, 0.3); }