至于聚类的地含义,以及我们熟悉的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()
第二步,我们建立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()
二:评估指标
1:我们使用轮廓系数来衡量,好的聚类算法的表现是,簇内的是很相似很近的,簇外就是越差异越远越好。
样本与自身所在簇中的其他样本相似度a:等于样本与同簇中其他所有点的平均距离。
样本与其他簇中样本的相似度b:等于样本与其最近的一个其他簇中所有点的平均距离。
因此轮廓系数是:
这个公式可以理解成:
这个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指数
因此我们希望其值越大越好啊。其效果也快,比轮廓系数计算快。
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_即可保存原始的图像。
原始图如下:
经过kmeans压缩后的