Python机器学习笔记(十五、聚类算法的对比和评估)

用真实世界的数据集对k均值、凝聚聚类和DBSCAN算法进行比较。

1. 用真实值评估聚类

评估聚类算法对真实世界数据集的聚类结果,可以用调整rand指数ARI和归一化互信息NMI。

调整rand指数 (adjusted rand index,ARI)和归一化互信息(normalized mutual information,NMI),二者都给出了定量的度量,最佳值为1,0表示不相关的聚类。

示例,使用 ARI 来比较 k 均值、凝聚聚类和 DBSCAN 算法。

import numpy as np
import matplotlib.pyplot as plt
import mglearn
from sklearn.cluster import KMeans
from sklearn.datasets import make_moons
from sklearn.cluster import AgglomerativeClustering
from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScaler
from sklearn.metrics.cluster import adjusted_rand_scoreX, y = make_moons(n_samples=200, noise=0.05, random_state=0)
# 将数据缩放成平均值为0、方差为1
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(algorithm.__class__.__name__, adjusted_rand_score(y, clusters)))
plt.show()

输出图形:

上图是:利用监督 ARI 分数在two_moons数据集上比较随机分配、k 均值、凝聚聚类和DBSCAN。调整 rand 指数给出了符合直觉的结果,随机簇分配的分数为 0,而 DBSCAN(完美地找到了期望中的聚类)的分数为 1。

用这种方式评估聚类时,一个常见的错误是使用accuracy_score而不是adjusted_rand_ score、normalized_mutual_info_score或其他聚类指标。使用精度的问题在于,它要求分配的簇标签与真实值完全匹配。但簇标签本身毫无意义——唯一重要的是哪些点位于同一个簇中。

from sklearn.metrics.cluster import adjusted_rand_score
from sklearn.metrics import accuracy_score# 这两种点标签对应于相同的聚类
clusters1 = [0, 0, 1, 1, 0]
clusters2 = [1, 1, 0, 0, 1]
# 精度为0,因为二者标签完全不同
print("Accuracy: {:.2f}".format(accuracy_score(clusters1, clusters2)))
# 调整rand分数为1,因为二者聚类完全相同
print("ARI: {:.2f}".format(adjusted_rand_score(clusters1, clusters2)))

输出结果:

Accuracy: 0.00
ARI: 1.00

2. 在没有真实值的情况下评估聚类

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

有一些聚类的评分指标不需要真实值,比如轮廓系数(silhouette coeffcient)。但它们在实践中的效果并不好。轮廓分数计算一个簇的紧致度,其值越大越好,最高分数为 1。虽然紧致的簇很好,但紧致度不允许复杂的形状。下面代码示例,利用轮廓分数在 two_moons 数据集上比较 k 均值、凝聚聚类和 DBSCAN:

import numpy as np
import matplotlib.pyplot as plt
import mglearn
from sklearn.cluster import KMeans
from sklearn.datasets import make_moons
from sklearn.cluster import AgglomerativeClustering
from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScaler
from sklearn.metrics.cluster import silhouette_scoreX, y = make_moons(n_samples=200, noise=0.05, random_state=0)
# 将数据缩放成平均值为0、方差为1
scaler = StandardScaler()
scaler.fit(X)
X_scaled = scaler.transform(X)
fig, axes = plt.subplots(1, 4, figsize=(15, 3), subplot_kw={'xticks': (), 'yticks': ()})
# 创建一个随机的簇分配,作为参考
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: {:.2f}".format(silhouette_score(X_scaled, random_clusters)))
algorithms = [KMeans(n_clusters=2), AgglomerativeClustering(n_clusters=2), DBSCAN()]
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("{} : {:.2f}".format(algorithm.__class__.__name__, silhouette_score(X_scaled, clusters)))
plt.show()

输出:

上图四:利用无监督的轮廓分数在 two_moons 数据集上比较随机分配、k 均值、凝聚聚类和 DBSCAN(更符合直觉的 DBSCAN 的轮廓分数低于 k 均值找到的分配)。

如上图所见,k 均值的轮廓分数最高,尽管我们可能更喜欢 DBSCAN 的结果。对于评估聚类,稍好的策略是使用基于鲁棒性的(robustness-based)聚类指标。这种指标先向数据中添加一些噪声,或者使用不同的参数设定,然后运行算法,并对结果进行比较。其思想是,如果许多算法参数和许多数据扰动返回相同的结果,那么它很可能是可信的。

即使我们得到一个鲁棒性很好的聚类或者非常高的轮廓分数,但仍然不知道聚类中是否有任何语义含义,或者聚类是否反映了数据中我们感兴趣的某个方面。回到人脸图像的例子,我们希望找到类似人脸的分组,比如男人和女人、老人和年轻人,或者有胡子的人和没胡子的人。假设我们将数据分为两个簇,关于哪些点应该被聚类在一起,所有算法的结果一致。我们仍不知道找到的簇是否以某种方式对应于我们感兴趣的概念。算法找到的可能是侧视图和正面视图、夜间拍摄的照片和白天拍摄的照片,或者iPhone拍摄的照片和安卓手机拍摄的照片。要想知道聚类是否对应于我们感兴趣的内容,唯一的办法就是对簇进行人工分析。

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

将 k 均值、DBSCAN 和凝聚聚类算法应用于 Wild 数据集中的 Labeled Faces,查看它们是否找到了有趣的结构。我们将使用数据的特征脸表示,它由包含 100 个成分的 PCA(whiten=True) 生成:

import numpy as np
from sklearn.cluster import DBSCAN
from sklearn.datasets import fetch_lfw_peoplepeople = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape
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
X_people = people.data[mask]
y_people = people.target[mask]
# 将灰度值缩放到0到1之间,而不是在0到255之间
# 以得到更好的数据稳定性
X_people = X_people / 255.# 从lfw数据中提取特征脸,并对数据进行变换
from sklearn.decomposition import PCA
pca = PCA(n_components=100, whiten=True, random_state=0)
pca.fit_transform(X_people)
X_pca = pca.transform(X_people)# 用 DBSCAN 分析人脸数据集。
# 应用默认参数的DBSCAN
dbscan = DBSCAN()
labels = dbscan.fit_predict(X_pca)
print("Unique labels: {}".format(np.unique(labels)))

用DBSCAN分析人脸数据集输出:Unique labels: [-1]  所有返回的标签都是-1,因此所有数据都被 DBSCAN 标记为“噪声”。我们可以改变两个参数来改进这一点:第一,增大 eps,从而扩展每个点的邻域;第二, 减小 min_samples,从而将更小的点组视为簇。首先尝试改变 min_samples:

import numpy as np
from sklearn.cluster import DBSCAN
from sklearn.datasets import fetch_lfw_peoplepeople = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape
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
X_people = people.data[mask]
y_people = people.target[mask]
# 将灰度值缩放到0到1之间,而不是在0到255之间
# 以得到更好的数据稳定性
X_people = X_people / 255.# 从lfw数据中提取特征脸,并对数据进行变换
from sklearn.decomposition import PCA
pca = PCA(n_components=100, whiten=True, random_state=0)
pca.fit_transform(X_people)
X_pca = pca.transform(X_people)# 用 DBSCAN 分析人脸数据集。
# 改变min_samples
dbscan = DBSCAN(min_samples=3)
labels = dbscan.fit_predict(X_pca)
print("Unique labels: {}".format(np.unique(labels)))

输出结果:Unique labels: [-1]  。考虑由三个点构成的组,所有点也都被标记为噪声。再增大eps:

import numpy as np
from sklearn.cluster import DBSCAN
from sklearn.datasets import fetch_lfw_peoplepeople = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape
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
X_people = people.data[mask]
y_people = people.target[mask]
# 将灰度值缩放到0到1之间,而不是在0到255之间
# 以得到更好的数据稳定性
X_people = X_people / 255.# 从lfw数据中提取特征脸,并对数据进行变换
from sklearn.decomposition import PCA
pca = PCA(n_components=100, whiten=True, random_state=0)
pca.fit_transform(X_people)
X_pca = pca.transform(X_people)# 用 DBSCAN 分析人脸数据集。
# 改变min_samples,增大eps
dbscan = DBSCAN(min_samples=3, eps=15)
labels = dbscan.fit_predict(X_pca)
print("Unique labels: {}".format(np.unique(labels)))

改变min_samples,增大eps后输出:Unique labels: [-1  0]

从输出可以看出,使用更大的 eps(其值为 15),只得到了单一簇和噪声点。我们可以利用这一结果找出 “噪声”相对于其他数据的形状。为了进一步理解发生的事情,我们查看有多少点是噪声, 有多少点在簇内:

import numpy as np
from sklearn.cluster import DBSCAN
from sklearn.datasets import fetch_lfw_peoplepeople = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape
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
X_people = people.data[mask]
y_people = people.target[mask]
# 将灰度值缩放到0到1之间,而不是在0到255之间
# 以得到更好的数据稳定性
X_people = X_people / 255.# 从lfw数据中提取特征脸,并对数据进行变换
from sklearn.decomposition import PCA
pca = PCA(n_components=100, whiten=True, random_state=0)
pca.fit_transform(X_people)
X_pca = pca.transform(X_people)# 用 DBSCAN 分析人脸数据集。
# 改变min_samples,增大eps
dbscan = DBSCAN(min_samples=3, eps=15)
labels = dbscan.fit_predict(X_pca)
print("Unique labels: {}".format(np.unique(labels)))
# 计算所有簇中的点数和噪声中的点数。
# bincount不允许负值,所以我们需要加1。
# 结果中的第一个数字对应于噪声点。
print("Number of points per cluster: {}".format(np.bincount(labels + 1)))

输出结果:Number of points per cluster: [  37 2026]   有27个噪声点,下面查看所有噪声点:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import DBSCAN
from sklearn.datasets import fetch_lfw_peoplepeople = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape
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
X_people = people.data[mask]
y_people = people.target[mask]
# 将灰度值缩放到0到1之间,而不是在0到255之间
# 以得到更好的数据稳定性
# X_people = X_people / 255.# 从lfw数据中提取特征脸,并对数据进行变换
from sklearn.decomposition import PCA
pca = PCA(n_components=100, whiten=True, random_state=0)
pca.fit_transform(X_people)
X_pca = pca.transform(X_people)# 用 DBSCAN 分析人脸数据集。
# 改变min_samples,增大eps
dbscan = DBSCAN(min_samples=3, eps=15)
labels = dbscan.fit_predict(X_pca)noise = X_people[labels==-1]
fig, axes = plt.subplots(3, 9, subplot_kw={'xticks': (), 'yticks': ()}, figsize=(12, 4))
for image, ax in zip(noise, axes.ravel()):ax.imshow(image.reshape(image_shape), vmin=0, vmax=1)
plt.show()

输出结果:

这些是人脸数据集中被DBSCAN标记为噪声的样本,将这些图像与随机选择的人脸图像样本进行比较,我们可以猜测它们被标记为噪声的原因:第2行第9张图像显示一个人正在用玻璃杯喝水,还有人戴帽子的图像,在第2行第6张图像中,人脸前面有一只手。其他图像都包含奇怪的角度,或者太近或太宽的剪切。

这种类型的分析——尝试找出“奇怪的那一个”——被称为异常值检测(outlier detection)。如果这是一个真实的应用,那么我们可能会尝试更好地裁切图像,以得到更加均匀的数据。对于照片中的人有时戴着帽子、喝水或在面前举着某物,我们能做的事情很少。但需要知道它们是数据中存在的问题,我们应用任何算法都需要解决这些问题。

如果想要找到更有趣的簇,而不是一个非常大的簇,那么需要将 eps 设置得更小,取值在15和0.5(默认值)之间。我们来看一下 eps 不同取值对应的结果:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import DBSCAN
from sklearn.datasets import fetch_lfw_peoplepeople = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape
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
X_people = people.data[mask]
y_people = people.target[mask]
# 将灰度值缩放到0到1之间,而不是在0到255之间
# 以得到更好的数据稳定性
# X_people = X_people / 255.# 从lfw数据中提取特征脸,并对数据进行变换
from sklearn.decomposition import PCA
pca = PCA(n_components=100, whiten=True, random_state=0)
pca.fit_transform(X_people)
X_pca = pca.transform(X_people)# 用 DBSCAN 分析人脸数据集。
for eps in [1, 3, 5, 7, 9, 11, 13]:print("\neps={}".format(eps))dbscan = DBSCAN(eps=eps, min_samples=3)labels = dbscan.fit_predict(X_pca)print("Clusters present: {}".format(np.unique(labels)))print("Cluster sizes: {}".format(np.bincount(labels + 1)))

输出结果:

eps=1
Clusters present: [-1]
Cluster sizes: [2063]

eps=3
Clusters present: [-1]
Cluster sizes: [2063]

eps=5
Clusters present: [-1  0]
Cluster sizes: [2059    4]

eps=7
Clusters present: [-1  0  1  2  3  4  5  6]
Cluster sizes: [1954   75    4   14    6    4    3    3]

eps=9
Clusters present: [-1  0  1]
Cluster sizes: [1199  861    3]

eps=11
Clusters present: [-1  0]
Cluster sizes: [ 403 1660]

eps=13
Clusters present: [-1  0]
Cluster sizes: [ 119 1944]

对于较小的 eps,所有点都被标记为噪声。eps=7 时,我们得到许多噪声点和许多较小的簇。eps=9 时,仍得到许多噪声点,但我们得到了一个较大的簇和一些较小的簇。从 eps=11 开始,我们仅得到一个较大的簇和噪声。 有趣的是,较大的簇从来没有超过一个。最多有一个较大的簇包含大多数点,还有一些较小的簇。这表示数据中没有两类或三类非常不同的人脸图像,而是所有图像或多或少地都与其他图像具有相同的相似度(或不相似度)。eps=7 的结果看起来最有趣,它有许多较小的簇。我们可以通过将较小的簇中的点全部可视化来深入研究这一聚类:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import DBSCAN
from sklearn.datasets import fetch_lfw_peoplepeople = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape
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
X_people = people.data[mask]
y_people = people.target[mask]
# 将灰度值缩放到0到1之间,而不是在0到255之间
# 以得到更好的数据稳定性
# X_people = X_people / 255.# 从lfw数据中提取特征脸,并对数据进行变换
from sklearn.decomposition import PCA
pca = PCA(n_components=100, whiten=True, random_state=0)
pca.fit_transform(X_people)
X_pca = pca.transform(X_people)# 用 DBSCAN 分析人脸数据集。
dbscan = DBSCAN(min_samples=3, eps=7)
labels = dbscan.fit_predict(X_pca)
for cluster in range(max(labels) + 1):mask = labels == clustern_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])
plt.show()

会输出7个图像:

在每个簇内,人脸方向和面部表情也是固定的。有些簇中包含多个人的面孔,但他们的方向和表情都相似。这就是我们将 DBSCAN 算法应用于人脸数据集的分析结论。我们这里进行了人工分析,不同于监督学习中基于 R2 分数或精度的更为自动化的搜索方法。

用 k 均值分析人脸数据集

利用 DBSCAN 无法创建多于一个较大的簇。凝聚聚类和 k 均值更可能创建均匀大小的簇,但我们需要设置簇的目标个数。可以将簇的数量设置为数据集中的已知人数,虽然无监督聚类算法不太可能完全找到它们。相反,我们可以首先设置一个比较小的簇的数量,比如 10 个,这样我们可以分析每个簇:

import numpy as np
from sklearn.cluster import KMeans
from sklearn.datasets import fetch_lfw_peoplepeople = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape
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
X_people = people.data[mask]
y_people = people.target[mask]# 从lfw数据中提取特征脸,并对数据进行变换
from sklearn.decomposition import PCA
pca = PCA(n_components=100, whiten=True, random_state=0)
pca.fit_transform(X_people)
X_pca = pca.transform(X_people)# 用k均值提取簇
km = KMeans(n_clusters=10, random_state=0)
labels_km = km.fit_predict(X_pca)
print("Cluster sizes k-means: {}".format(np.bincount(labels_km)))

输出:Cluster sizes k-means: [  5 286 190 275 185 493   1 286   2 340]

k 均值聚类将数据划分为大小相似的簇,我们可以通过将簇中心可视化来进一步分析 k 均值的结果,由于我们是在PCA生成的表示中进行聚类,因此需要使用 pca.inverse_transform 将簇中心旋转回到原始空间并可视化:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.datasets import fetch_lfw_peoplepeople = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape
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
X_people = people.data[mask]
y_people = people.target[mask]# 从lfw数据中提取特征脸,并对数据进行变换
from sklearn.decomposition import PCA
pca = PCA(n_components=100, whiten=True, random_state=0)
pca.fit_transform(X_people)
X_pca = pca.transform(X_people)# 用k均值提取簇
km = KMeans(n_clusters=10, random_state=0)
labels_km = km.fit_predict(X_pca)
fig, axes = plt.subplots(2, 5, subplot_kw={'xticks': (), 'yticks': ()}, figsize=(12, 4))
for center, ax in zip(km.cluster_centers_, axes.ravel()):ax.imshow(pca.inverse_transform(center).reshape(image_shape), vmin=0, vmax=1)
plt.show()

输出:

k 均值找到的簇中心是非常平滑的人脸。这并不奇怪,因为每个簇中心都是 脸图像的平均。使用降维的 PCA 表示,可以增加图像的平滑度。聚类似乎捕捉到人脸的不同方向、不同表情,以及是否有衬衫领子。下面代码更详细的视图,对每个簇中心给出了簇中 5 张最典型的图像(该簇中与簇中心距离最近的图像)与 5 张最不典型的图像(该簇中与簇中心距离最远的图像):

import mglearn
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.datasets import fetch_lfw_peoplepeople = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape
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
X_people = people.data[mask]
y_people = people.target[mask]
# 将灰度值缩放到0到1之间,而不是在0到255之间
# 以得到更好的数据稳定性
# X_people = X_people / 255.# 从lfw数据中提取特征脸,并对数据进行变换
from sklearn.decomposition import PCA
pca = PCA(n_components=100, whiten=True, random_state=0)
pca.fit_transform(X_people)
X_pca = pca.transform(X_people)# 用k均值提取簇
km = KMeans(n_clusters=10, random_state=0)
labels_km = km.fit_predict(X_pca)
mglearn.plots.plot_kmeans_faces(km, pca, X_pca, X_people, y_people, people.target_names)
plt.show()

输出: 

从输出图像证实了我们之前的直觉,也证实了其他簇中方向的重要性。不过 “非典型的”点与簇中心不太相似,而且它们的分配似乎有些随意。这可以归因于以下事实:k均值对所有数据点进行划分,不像 DBSCAN 那样具有“噪声”点的概念。利用更多数量的簇,算法可以找到更细微的区别。但添加更多的簇会使得人工检查更加困难。

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

import numpy as np
from sklearn.cluster import AgglomerativeClustering
from sklearn.datasets import fetch_lfw_peoplepeople = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape
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
X_people = people.data[mask]
y_people = people.target[mask]# 从lfw数据中提取特征脸,并对数据进行变换
from sklearn.decomposition import PCA
pca = PCA(n_components=100, whiten=True, random_state=0)
pca.fit_transform(X_people)
X_pca = pca.transform(X_people)# 用ward凝聚聚类提取簇
agglomerative = AgglomerativeClustering(n_clusters=10)
labels_agg = agglomerative.fit_predict(X_pca)
print("Cluster sizes agglomerative clustering: {}".format(np.bincount(labels_agg)))

输出:Cluster sizes agglomerative clustering: [264 100 275 553  49  64 546  52  51 109]

凝聚聚类生成的也是大小相近的簇,其大小在 49 和 553 之间。这比 k 均值生成的簇更不均匀,但比 DBSCAN 生成的簇要更加均匀。通过计算 ARI 来度量凝聚聚类和 k 均值给出的两种数据划分是否相似:

from sklearn.metrics.cluster import adjusted_rand_score
import numpy as np
from sklearn.cluster import KMeans
from sklearn.cluster import AgglomerativeClustering
from sklearn.datasets import fetch_lfw_peoplepeople = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape
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
X_people = people.data[mask]
y_people = people.target[mask]# 从lfw数据中提取特征脸,并对数据进行变换
from sklearn.decomposition import PCA
pca = PCA(n_components=100, whiten=True, random_state=0)
pca.fit_transform(X_people)
X_pca = pca.transform(X_people)# 用k均值提取簇
km = KMeans(n_clusters=10, random_state=0)
labels_km = km.fit_predict(X_pca)# 用ward凝聚聚类提取簇
agglomerative = AgglomerativeClustering(n_clusters=10)
labels_agg = agglomerative.fit_predict(X_pca)print("ARI: {:.2f}".format(adjusted_rand_score(labels_agg, labels_km)))

输出结果:ARI: 0.09

ARI 只有 0.09,说明 labels_agg 和 labels_km 这两种聚类的共同点很少。这并不奇怪,原因在于以下事实:对于k均值,远离簇中心的点似乎没有什么共同点。下面我们绘制树状图来展示,但是我们将限制图中树的深度,因为如果分支到2063个数据点,图像将密密麻麻无法阅读:

from scipy.cluster.hierarchy import dendrogram, ward
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import fetch_lfw_peoplepeople = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape
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
X_people = people.data[mask]
y_people = people.target[mask]# 从lfw数据中提取特征脸,并对数据进行变换
from sklearn.decomposition import PCA
pca = PCA(n_components=100, whiten=True, random_state=0)
pca.fit_transform(X_people)
X_pca = pca.transform(X_people)linkage_array = ward(X_pca)
# 现在我们为包含簇之间距离的linkage_array绘制树状图
plt.figure(figsize=(20, 5))
dendrogram(linkage_array, p=7, truncate_mode='level', no_labels=True)
plt.xlabel("Sample index")
plt.ylabel("Cluster distance")
plt.show()

输出图像:

要想创建 10 个簇,我们在顶部有 10 条竖线的位置将树横切。对于人脸数据而言,似乎没有非常自然的切割点。有一些分支代表更为不同的组,但似乎没有一个特别合适的簇的数量。这并不奇怪,因为 DBSCAN 的结果是试图将所有的点都聚类在一起。我们将 10 个簇可视化,注意,在凝聚聚类中没有簇中心的概念,只是给出了每个簇的前几个点。在第一张图像的左侧给出了每个簇中的点的数量:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import AgglomerativeClustering
from sklearn.datasets import fetch_lfw_peoplepeople = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape
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
X_people = people.data[mask]
y_people = people.target[mask]# 从lfw数据中提取特征脸,并对数据进行变换
from sklearn.decomposition import PCA
pca = PCA(n_components=100, whiten=True, random_state=0)
pca.fit_transform(X_people)
X_pca = pca.transform(X_people)# 用ward凝聚聚类提取簇
agglomerative = AgglomerativeClustering(n_clusters=10)
labels_agg = agglomerative.fit_predict(X_pca)n_clusters = 10
for cluster in range(n_clusters):mask = labels_agg == clusterfig, axes = plt.subplots(1, 10, subplot_kw={'xticks': (), 'yticks': ()}, figsize=(15, 8))axes[0].set_ylabel(np.sum(mask))for image, label, asdf, ax in zip(X_people[mask], y_people[mask], labels_agg[mask], axes):ax.imshow(image.reshape(image_shape), vmin=0, vmax=1)ax.set_title(people.target_names[label].split()[-1], fontdict={'fontsize': 9})
plt.show()

输出了10个图像: 

虽然某些簇似乎具有语义上的主题,但许多簇都太大而实际上很难是均匀的。为了得到更加均匀的簇,我们可以再次运行算法,这次使用 40 个簇,并挑选出一些特别有趣的簇:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import AgglomerativeClustering
from sklearn.datasets import fetch_lfw_peoplepeople = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape
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
X_people = people.data[mask]
y_people = people.target[mask]# 从lfw数据中提取特征脸,并对数据进行变换
from sklearn.decomposition import PCA
pca = PCA(n_components=100, whiten=True, random_state=0)
pca.fit_transform(X_people)
X_pca = pca.transform(X_people)# 用ward凝聚聚类提取簇
agglomerative = AgglomerativeClustering(n_clusters=40)
labels_agg = agglomerative.fit_predict(X_pca)
print("cluster sizes agglomerative clustering: {}".format(np.bincount(labels_agg)))
n_clusters = 40
for cluster in [10, 13, 19, 22, 36]: # 手动挑选“有趣的”簇mask = labels_agg == clusterfig, axes = plt.subplots(1, 15, subplot_kw={'xticks': (), 'yticks': ()}, figsize=(15, 8))cluster_size = np.sum(mask)axes[0].set_ylabel("#{}: {}".format(cluster, cluster_size))for image, label, asdf, ax in zip(X_people[mask], y_people[mask], labels_agg[mask], axes):ax.imshow(image.reshape(image_shape), vmin=0, vmax=1)ax.set_title(people.target_names[label].split()[-1], fontdict={'fontsize': 9})for i in range(cluster_size, 15):axes[i].set_visible(False)
plt.show()

输出结果和图像:

 cluster sizes agglomerative clustering: [139  35  23   2 111  39 106  33   5 161  60  41  70  17  30  20 134  40 23  38  56 264   4  35  44  16  29 135  25  37  42  34   3  17  31   3  21  27  76  37]

这里聚类挑选出的似乎是“深色皮肤且微笑”“有领子的衬衫”“微笑的女性”“萨达姆” 和“高额头”。如果进一步详细分析,我们还可以利用树状图找到这些高度相似的簇。

聚类方法小结:聚类的应用与评估是一个非常定性的过程,通常在数据分析的探索阶段很有帮助。学习了三种聚类算法:k 均值、DBSCAN 和凝聚聚类。这三种算法都可以控制聚类的粒度(granularity)。k 均值和凝聚聚类允许指定想要的簇的数量,而 DBSCAN 允许用 eps 参数定义接近程度,从而间接影响簇的大小。三种方法都可以用于大型的现实世界数据集,都相对容易理解,也都可以聚类成多个簇。每种算法的优点稍有不同。k 均值可以用簇的平均值来表示簇,还可以被看作一种分解方法,每个数据点都由其簇中心表示。DBSCAN 可以检测到没有分配任何簇的“噪声点”,还可以帮助自动判断簇的数量。与其他两种方法不同,它允许簇具有复杂的形状,正如我们在 two_moons 的例子中所看到的那样。DBSCAN 有时会生成大小差别很大的簇,这可能是它的优点,也可能是缺点。凝聚聚类可以提供数据的可能划分的整个层次结构,可以通过树状图轻松查看。

对无监督学习算法的小结:无监督学习算法可用于探索性数据分析和预处理。找到数据的正确表 示对于监督学习和无监督学习的成功通常都至关重要,预处理和分解方法在数据准备中具有重要作用。分解、流形学习和聚类都是加深数据理解的重要工具,在没有监督信息的情况下,也是理解数据的仅有的方法。即使是在监督学习中,探索性工具对于更好地理解数据性质也很重要。通常来说,很难量化无监督算法的有用性,但这不妨碍使用它们来深入理解数据。学完这些方法,基本掌握了机器学习从业者每天使用的所有必要的学习算法。

另外:建议在 scikit-learn 中包含的二维玩具数据和现实世界数据集(比如 digits、iris 和 cancer 数据集)上尝试聚类和分解方法的学习使用,多动手试试。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/498352.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SAP PP bom历史导出 ALV 及XLSX 带ECN号

bom总数 104W PS超过XLSX上限 ,那就分文件 *&---------------------------------------------------------------------* *& Report ZRPT_PP_BOM_HIS_ECN *&---------------------------------------------------------------------* *& tcode:zpp0…

《代码随想录》Day20打卡!

《代码随想录》二叉树:二叉搜索树的最近公共祖先 本题的完整题目如下: 本题的思路如下: 1.之前写过一个二叉树的最近公共祖先,本题相比于另一道题,不同是本题是二叉搜索树,有一些可用的性质。 2.本题使用递…

初识MySQL · 库的操作

目录 前言: 增 有关编码 删 查 改 前言: 由前文可得,MySQL是目前主流的数据库,mysql是客户端,mysqld是一种网络服务,mysqld是一种数据库服务,而对于数据库来说,是一种存储数据…

Idea创建JDK17的maven项目失败

Idea创建JDK17的maven项目失败 Error occurred during initialization of VM Could not find agent library instrument on the library path, with error: Can’t find dependent libraries Possible solution: Check your maven runner VM options. Open Maven Runner setti…

Go-知识 模板

Go-知识 模板 1. 介绍2. Text/template 包3. Html/template 包4. 模板语法4.1 模板标签4.2 添加注释4.3 访问变量4.4 访问方法4.5 模板变量4.6 访问函数4.7 数据渲染4.8 条件判断4.9 循环遍历4.10 嵌入子模板4.11 局部变量4.12 输出字符串4.13 预定义的全局函数4.14 比较函数 1…

优化租赁小程序提升服务效率与用户体验的策略与实践

内容概要 在这个快速发展的商业环境中,租赁小程序成为了提升服务效率和用户体验的重要工具。通过对用户需求的深入挖掘,我们发现他们对于功能的便捷性、响应速度和界面的友好性有着极高的期待。因此,针对这些需求,完善租赁小程序…

基础数据结构--二叉树

一、二叉树的定义 二叉树是 n( n > 0 ) 个结点组成的有限集合,这个集合要么是空集(当 n 等于 0 时),要么是由一个根结点和两棵互不相交的二叉树组成。其中这两棵互不相交的二叉树被称为根结点的左子树和右子树。 如图所示&am…

shell学习变量(二)

这里写目录标题 一、概念1、环境变量2、本地变量3、系统变量 二、环境变量三、本地变量四、系统变量五、定义变量规则1、命名规则2、定义方式3、unset命令:删除变量 一、概念 1、环境变量 环境变量指的是再当前进程有效,并且能够被子进程调用&#xff…

自动驾驶3D目标检测综述(六)

停更了好久终于回来了(其实是因为博主去备考期末了hh) 这一篇接着(五)的第七章开始讲述第八章的内容。第八章主要介绍的是三维目标检测的高效标签。 目录 第八章 三维目标检测高效标签 一、域适应 (一)…

如何恢复永久删除的PPT文件?查看数据恢复教程!

可以恢复永久删除的PPT文件吗? Microsoft PowerPoint应用程序是一种应用广泛的演示程序,在人们的日常生活中经常使用。商人、官员、学生等在学习和工作中会使用PowerPoint做报告和演示。PowerPoint在人们的学习和工作生活中占主导地位,每天都…

四大自平衡树对比:AVL树、红黑树、B树与B+树

AVL树、红黑树、B树和B树的对比与应用场景 树系列相关文章(置顶) 1、从链表到平衡树:二叉查找树的退化与优化 2、自平衡二叉查找树:如何让二叉查找树始终保持高效 3、AVL树入门:理解自平衡二叉查找树的基础 4、红黑树全…

IOS safari 播放 mp4 遇到的坎儿

起因 事情的起因是调试 IOS 手机下播放服务器接口返回的 mp4 文件流失败。对于没调试过移动端和 Safari 的我来说着实费了些功夫,网上和AI也没有讲明白。好在最终大概理清楚了,在这里整理出来供有缘人参考。 问题 因为直接用 IOS 手机的浏览器打开页面…

Kubernetes Gateway API-2-跨命名空间路由

1 跨命名空间路由 Gateway API 具有跨命名空间路由的核心支持。当多个用户或团队共享底层网络基础设施时,这很有用,但必须对控制和配置进行分段,以尽量减少访问和容错域。 Gateway 和 Route(HTTPRoute,TCPRoute,GRPCRoute) 可以部署到不同的命名空间中,路由可以跨命名空间…

第十六届蓝桥杯模拟赛(第一期)(C语言)

判断质因数 如果一个数p是个质数,同时又是整数a的约数,则p称为a的一个质因数。 请问2024有多少个质因数。 了解 约数,又称因数。整数a整除整数b,b为a的因数(约数)质数,又称素数。只有1和它本身两…

AI安全的挑战:如何让人工智能变得更加可信

引言 随着人工智能(AI)技术在各个领域的广泛应用,尤其是在医疗、金融、自动驾驶和智能制造等行业,AI正在重塑我们的工作和生活方式。从提高生产效率到实现个性化服务,AI带来了前所未有的便利。然而,在享受这…

TiDB 的MPP架构概述

MPP架构介绍: 如图,TiDB Server 作为协调者,首先 TiDB Server 会把每个TiFlash 拥有的region 会在TiFlash上做交换,让表连接在一个TiFlash上。另外 TiFlash会作为计算节点,每个TiFlash都负责数据交换,表连接…

springboot499基于javaweb的城乡居民基本医疗信息管理系统(论文+源码)_kaic

摘 要 信息数据从传统到当代,是一直在变革当中,突如其来的互联网让传统的信息管理看到了革命性的曙光,因为传统信息管理从时效性,还是安全性,还是可操作性等各个方面来讲,遇到了互联网时代才发现能补上自古…

【SQL Server】教材数据库(1)

1 利用sql建立教材数据库,并定义以下基本表: 学生(学号,年龄,性别,系名) 教材(编号,书名,出版社编号,价格) 订购(学号…

RT-Thread中堆和栈怎么跟单片机内存相联系

现在RT-ThreadMCU的应用方式越来越普遍,RT-Thread需要配置MCU中的RAM到的系统中,进入系统内存管理,才能提供给基于实时系统的应用程序使用,比如给应用程序提供malloc、free等函数调用功能。在嵌入式软件开发中,我们经常…

Linux硬盘分区 --- fdisk命令MBR分区、添加硬盘、lsblk命令

一、MBR分区 如果想对硬盘进行分区可以使用“ fdisk ”命令,它会采用MBR格式将硬盘进行分区。MBR是传统的分区机制,支持 32 位和 64 位系统,最多只能创建 4 个主分区,或者 3 个主分区和 1 个扩展分区,只支持不超过 2T…