《scikit-learn》KMeans

至于聚类的地含义,以及我们熟悉的KMeans算法,基于层次的,基于密度的,我们之前都是学习过的,就不多说了,这里说一些在scikit-learn中是如何使用的,且说一些其他方面的。

一:代码直接开整

第一步,我们先自己造一些二维数据,并且用图画出来。

from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt

# 自己创造一个数据集,
# 自己创造一个,一千个样本的数据,每个数据是2个特征,一共有4个中心
x, y = make_blobs(n_samples=1000, n_features=2, centers=4, random_state=54)
print(x.shape)
print(y.shape)
print(y[0:8])  # 返回的y呢就是以0为开始整数。

fig, ax1 = plt.subplots(1)  # 生成子图的数量,第一个是画布,第二个是对象
ax1.scatter(x[:, 0], x[:, 1],
            marker='o',  # 点形状
            s=8)  # 点大小
plt.title('original plain samples')
plt.show()

# 如果我们想看看点的分布的话,也可以画出来的,只是这正是我们聚类偶有做的事情啊。
# color = ['red', 'black', 'green', 'blue']
# fig, ax1 = plt.subplots(1)  # 生成子图的数量,第一个是画布,第二个是对象
# for i in range(4):
#     ax1.scatter(x[y == i, 0], x[y == i, 1],  # 使用布尔索引
#                 marker='o',  # 点形状
#                 s=8,  # 点大小
#                 c=color[i])  # 设置点颜色
#
# plt.title('original classified samples')
# plt.show()

《scikit-learn》KMeans

第二步,我们建立KMeans模型进行拟合且就结果进行画图展示

# 开始聚类操作下
from sklearn.cluster import KMeans

n_clusters = 4
cluster = KMeans(n_clusters=n_clusters, random_state=9)
cluster = cluster.fit(x)  # 训练

pred = cluster.labels_
print(pred[0:8])  # 每个样本对应的结果
print(cluster.cluster_centers_)  # 返回得到的几个质心
print(cluster.inertia_)  # 总距离(所有点到各自质心的距离)的平方和,不是合适的模型评估指标

y_pred = cluster.fit_predict(x)
print(y_pred[0:8])  # 每个样本对应的结果

# 具体效果咋样,我们可视化一下看看
color = ['red', 'black', 'green', 'blue']
fig, ax1 = plt.subplots(1)  # 生成子图的数量,第一个是画布,第二个是对象
for i in range(4):
    ax1.scatter(x[y_pred == i, 0], x[y_pred == i, 1],  # 使用布尔索引
                marker='o',  # 点形状
                s=8,  # 点大小
                c=color[i])  # 设置点颜色
# 单独把质心画出来
ax1.scatter(cluster.cluster_centers_[:, 0], cluster.cluster_centers_[:, 1],  # 使用布尔索引
            marker='x',  # 点形状
            s=15,  # 点大小
            c='yellow')  # 设置点颜色

plt.title('the result after KMeans')
plt.show()

《scikit-learn》KMeans

二:评估指标
1:我们使用轮廓系数来衡量,好的聚类算法的表现是,簇内的是很相似很近的,簇外就是越差异越远越好。
样本与自身所在簇中的其他样本相似度a:等于样本与同簇中其他所有点的平均距离。
样本与其他簇中样本的相似度b:等于样本与其最近的一个其他簇中所有点的平均距离。
因此轮廓系数是:
《scikit-learn》KMeans

这个公式可以理解成:
《scikit-learn》KMeans

这个s值是处在[-1, 1]之间的,很明显,我们希望a越小越好,且b越大越好。因此,s值越接近于1越好,越接近于-1越不好。

要声明的是,轮廓系数是到按照每个样本进行计算的,也就是每个样本都是有个轮廓系数哈。如果一个簇大多数样本的轮廓系数很高,那么平均值就会很高,那么模型聚类就很好。
我们用代码进行尝试看看

# 衡量模型好坏的指标
from sklearn.metrics import silhouette_score
from sklearn.metrics import silhouette_samples

# 经过上面的计算,我们有了原始特征矩阵数据x,y,以及我们预测的y_pred

print(silhouette_score(x, y_pred))  # 计算轮廓系数,第一个是特征矩阵(样本坐标),第二个是聚类的结果
print(silhouette_score(x, cluster.labels_))  # 计算轮廓系数

silhouette_results = silhouette_samples(x, y_pred)
print(silhouette_results.shape)  # 展示每个样本的轮廓系数
print(silhouette_results.mean())  # 展示每个样本的轮廓系数综合后的平均值,就是silhouette_score的值。

2:Calinski-Harabaz指数
《scikit-learn》KMeans

因此我们希望其值越大越好啊。其效果也快,比轮廓系数计算快。

from sklearn.metrics import calinski_harabasz_score

print(calinski_harabasz_score(x, y_pred))
print(calinski_harabasz_score(x, cluster.labels_))

这里为什么要学习衡量指标呢,就是为了调整超参数使用的。

三:一些重要的参数
KMeans的第一步就是初始化质心,随机放置的效果也会不一样,最后会直接影响效果的,也会影响模型运行时间。

新的方法可以使KMeans++,他们开发出一个算法,使得初始化质心彼此远离,从而得到更加可靠的。我们的参数init,可以输入random,kmeans++,或一个n维数组(用户自行决定质心,维度是n_cluster X n_features),一般我们保持默认kmeans++不动即可。

random_state就是质心随机初始化的种子,指定了的话,控制每次初始化质心都在相同的位置。

如果不指定随机数种子,就按按照n_init来使用多个随机种子来运行多次,选择最好的。这个参数就是n_init,默认是10,我们可以增加这个次数,次数越大,KMeans运行时间越长。

如何让迭代停止呢?
max_iter:可以让迭代提前终止,最大的迭代次数。
tol:如过两次迭代之间Inertial下降的量小于该值就结束掉迭代,基本已经不会有大变化了。它是浮点数,默认是1e-4。

四:聚类用于压缩,矢量量化。
KMeans的一个应用就是将非结构化数据(图像声音等)进行矢量量化,非结构化数据往往占用比较大的存储空间,数据量大,运算缓慢,我们希望在保证数据质量的情况下,尽可能减少数据的大小,简化结构化数据的结构。

降维自带压缩属性。
我们之前学习过的特征选择,是选择出有价值的贡献最大的特征。
之前学习的PCA,是聚合特征信息,从而创造出更低维度价值更高的特征。

矢量量化就是在同等样本量上压缩信息的大小,不改变样本数目,也不改变特征数目。
举个例子,我们有40个样本有40个不同的样本信息,假如我们把他们进行效果很好的聚类,后面发现,他们属于4个簇,我们就用每个簇的质心俩替换/代表/表示该簇内所有样本信息,这样话,我们只需要四组信息就表示了40个样本的信息,我们只需要保存这个四个质心的信息,有点四舍五入的感觉哈。

这样一来,信息量会被压缩到很小,但是损失的信息又不大。
我们举个例子,压缩一个图像,比如我们选取RGB图像,压缩到64个质心。用64个质心来替换每个像素。

from sklearn.datasets import load_sample_image  # 导入图片数据的类

from sklearn.cluster import KMeans
from sklearn.utils import shuffle  # 洗牌,打乱数据顺序的函数。

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

# 加载图片来试一试
china = load_sample_image('china.jpg')
print(china.dtype)  # 数组类型是uint8,0~255
print(china.shape)  # (427, 640, 3),看来是RGB类型的数据。长度是427,宽度是640。RGB三通道

# 我们调整下数据形状
newimage = china.reshape(-1, 3)
print(newimage.shape)  # (273280, 3),一张图片全部行程了一列,序列化了。RGB三通道也就是一个像素是三个值,一个像素3个特征。

uniquePiexls = pd.DataFrame(newimage).drop_duplicates()
print(uniquePiexls.shape)  # 哎呀(96615, 3),说明重复度很大嘛。

# 图片的数据查看了,来看看效果。
plt.figure(figsize=(15, 15))
plt.title('original image')
plt.imshow(china)
plt.show()






# 加入哈,我们压缩到64个簇,计算每个原始样本到质心们的距离,找出最新的质心,然后用质心的数据来替换原始数据的值。我们可以观察原始数据的效果。
n_clusters = 64

# 预处理一下,所有的数字都压缩到了[0,1]之间
china = np.array(china, dtype=np.float64) / china.max()
print(china.shape)
print((china > 1).sum())
print((china < 0).sum())

image_array = np.reshape(china, (-1, 3))
print(image_array.shape)

# 上模型
# 训练方式一:太慢了
# kmeans = KMeans(n_clusters=n_clusters, random_state=12).fit(image_array)
# print(kmeans.cluster_centers_.shape)

# 训练方式二:先使用部分数据找出质心,再使用这些质心预测所有点,这样快多了啊
image_array_samples = shuffle(image_array)[:5000]  # 比如先使用5000个像素样本来搞事情
kmeans = KMeans(n_clusters=n_clusters, random_state=12).fit(image_array_samples)
print(kmeans.cluster_centers_.shape)

# 然后预测出所有的像素点的值
labels = kmeans.predict(image_array)  # 生成了簇的质心的簇索引。
print(labels.shape)

# 使用质心来替换所有样本啊

image_kmeans = kmeans.cluster_centers_[labels]
print(image_kmeans.shape)
image_kmeans = image_kmeans * china.max()  # 恢复到0-255
image_kmeans = image_kmeans.reshape(china.shape[0], china.shape[1], china.shape[2])  # 转换到三维。此时图像只有64个像素重复了。

plt.figure(figsize=(15, 15))
plt.title('after kmeans')
plt.imshow(image_kmeans)
plt.show()

# 至此我们把图像进行了压缩,我们只需要存储labels的序列,以及所有的质心,kmeans.cluster_centers_即可保存原始的图像。

原始图如下:
《scikit-learn》KMeans

经过kmeans压缩后的
《scikit-learn》KMeans

上一篇:重磅!退休3年多的谢长军今天突然被查,5天前还在开会献策碳中和


下一篇:4.K均值算法--应用