K均值聚类

最近在自学图灵教材《Python机器学习基础教程》,在csdn以博客的形式做些笔记。

k均值聚类是最简单也最常用的聚类算法之一。它试图找到代表数据特定区域的簇中心 。算法交替执行以下两个步骤:将每个数据点分配给最近的簇中心,然后将每个簇中心设置为所分配的所有数据点的平均值。如果簇的分配不再发生变化,那么算法结束。

K均值聚类算法

下面将举例说明算法流程

第一幅图为我们的原始数据。对于这个数据集,首先我们需要指定寻找的簇为三个,于是第一步将会声明三个随机数据点为簇中心来对算法进行初始化(见上图图二,簇中心用三角形表示)。然后开始迭代算法。首先每个数据点将会被分配给距离最近的簇中心(见上图图三)。接下来,将簇中心修改为所分配点的平均值(见上图图四)。然后反复进行上述两个过程,直到簇中心分配的数据点保持不变时,算法结束(在这个例子中,一共进行了三次迭代,见上图)。而每给定新的数据点,k 均值会将其分配给最近的簇中心。

下图为上述例子中k均值聚类算法学习到的边界:

 下面将给出运用scikit-learn对上述数据进行k均值聚类的应用的代码

from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans
# 生成模拟的二维数据
X, y = make_blobs(random_state=1)
# 构建聚类模型
kmeans = KMeans(n_clusters=3)#设置簇的个数为3
kmeans.fit(X)

算法运行期间,为 X 中的每个训练数据点分配一个簇标签。我们可以在 kmeans.labels_ 属性中找到这些标签:

print("Cluster memberships:\n{}".format(kmeans.labels_))

 我们也可以用 predict 方法为新数据点分配簇标签。预测时会将最近的簇中心分配给每个新数据点,但现有模型不会改变。对训练集运行 predict 会返回与 labels_ 相同的结果。

可以看到,聚类算法与分类算法有些相似,每个元素都有一个标签。但并不存在真实的标签,因此标签本身并没有先验意义。对于我们刚刚在数据集上运行的聚类算法,这意味着我们不应该为其中一组的标签是 0、另一组的标签是 1 这一事实赋予任何意义。再次运行该算法可能会得到不同的簇 编号,原因在于初始化的随机性质

当然我们也可以使用更多或者更少的簇中心:

fig, axes = plt.subplots(1, 2, figsize=(10, 5))
# 使用2个簇中心:
kmeans = KMeans(n_clusters=2)
kmeans.fit(X)
assignments = kmeans.labels_
mglearn.discrete_scatter(X[:, 0], X[:, 1], assignments, ax=axes[0])
# 使用5个簇中心:
kmeans = KMeans(n_clusters=5)
kmeans.fit(X)
assignments = kmeans.labels_
mglearn.discrete_scatter(X[:, 0], X[:, 1], assignments, ax=axes[1])

 算法缺点

对于k均值,即使给定算法正确的簇的个数,也无法保证一定可以找到正确的簇。因为每个簇仅仅是由其中心定义的,着意味着每个簇都是凸的(比如圆形)。所以k均值只能找到相对简单的形状。k 均值还假设所有簇在某种程度上具有相同的“直径”,它总是将簇之间的边界刚好画 在簇中心的中间位置。

比如下面的例子:

你可能会认为,左下方的密集区域是第一个簇,右上方的密集区域是第二个,中间密度较小的区域是第三个。但事实上,簇 0 和簇 1 都包含一些远离簇中其他点的点。 k 均值还假设所有方向对每个簇都同等重要。这使得它无法处理一些数据,就如下面的例子所示:

# 生成一些随机分组数据
X, y = make_blobs(random_state=170, n_samples=600)
rng = np.random.RandomState(74)
# 变换数据使其拉长
transformation = rng.normal(size=(2, 2))
X = np.dot(X, transformation)
# 将数据聚类成3个簇
kmeans = KMeans(n_clusters=3)
kmeans.fit(X)
y_pred = kmeans.predict(X)
# 画出簇分配和簇中心
plt.scatter(X[:, 0], X[:, 1], c=y_pred, cmap=mglearn.cm3)
plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1],
 marker='^', c=[0, 1, 2], s=100, linewidth=2, cmap=mglearn.cm3)
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")

 

 

上图显示了一个二维数据集,数据中包含明确分开的三部分。由于 k 均值仅考虑到最近簇中心的距离,所以它对上述数据集进行了错误的分类,它无法处理该类型的数据。

如果簇的形状更加复杂,比如two_moons (sklearn.datasets自带数据集)数据,那么 k 均值的表现也很差:

# 生成模拟的two_moons数据
from sklearn.datasets import make_moons
X, y = make_moons(n_samples=200, noise=0.05, random_state=0)
# 将数据聚类成2个簇
kmeans = KMeans(n_clusters=2)
kmeans.fit(X)
y_pred = kmeans.predict(X)
# 画出簇分配和簇中心
plt.scatter(X[:, 0], X[:, 1], c=y_pred, cmap=mglearn.cm2, s=60)
plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1],
 marker='^', c=[mglearn.cm2(0), mglearn.cm2(1)], s=100, linewidth=2)
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")

 

 很显然,k均值再次进行了错误的分类。

一种解决方法

虽然 k 均值是一种聚类算法,但在 k 均值和分解方法(比如主成分分析) 之间存在一些相似之处。PCA 试图找到数据中方差最大的方向,这通常对应于数据的“极值”或“部分”。PCA试图将数据点表示为一些分量之和。与之相反,k 均值则尝试利用簇中心来表示每个数据点。你可以将其看作仅用一个分量来表示每个数据点,该分量由簇中心给出。这种 观点将 k 均值看作是一种分解方法,其中每个点用单一分量来表示,这种观点被称为矢量量化(vector quantization)

上面这段话看不懂也没关系,下面我们举一个例子,相信你就能马上明白。

让我们回到 two_moons 数据。如果我们使用更多的簇中心,我们可以用 k 均值找到一种更具表现力的表示:

X, y = make_moons(n_samples=200, noise=0.05, random_state=0)
kmeans = KMeans(n_clusters=10, random_state=0)
kmeans.fit(X)
y_pred = kmeans.predict(X)
plt.scatter(X[:, 0], X[:, 1], c=y_pred, s=60, cmap='Paired')
plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1], s=60,
 marker='^', c=range(kmeans.n_clusters), linewidth=2, cmap='Paired')
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")
print("Cluster memberships:\n{}".format(y_pred))

现在我们使用了10个簇中心,换言之,每个点现在都被分配了0到9之间的一个数字。于是我们可以构建10个新的特征 。只有表示该点对应的簇中心的那个特征不为 0,其他特征均为 0。利用这个 10 维表示,现在可以用线性模型来划分两个半月形,而利用原始的两个特征是无法做到这一点的。将到每个簇中心的距离作为特征, 还可以得到一种表更好的数据表示。可以利用 kmeans 的 transform 方法来完成这一点。

distance_features = kmeans.transform(X)
print("Distance feature shape: {}".format(distance_features.shape))
print("Distance features:\n{}".format(distance_features))

 至于具体的线性模型实现过程,在此不做阐述,大家可以自己实现并在评论区交流。

当然k均值算法还具有一个缺点,就是对簇形状的假设的约束性较强,而且还要求指定所要寻找的簇的个数(在现实世界的应用中可能并不知道这个数字)。

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值