KMeans做矢量量化

K-Means聚类最重要的应用之一是非结构数据(图像,声音)上的矢量化(VQ)。非结构化数据往往占用大量的储存空间,文件本身也会比较大,运算非常缓慢,我们希望能够在保证数据质量的前提下,尽量地缩小非结构化数据的大小,或者简化非结构化数据的结构。矢量量化就可以帮助我们实现这个目的。KMeans聚类的矢量量化本质是一种降维运用,但它与特征选择或PCA的降维思路不同,特征选择的降维是直接选取对模型贡献大的特征,PCA的降维是聚合信息,而矢量化的降维是在同等样本量上压缩信息的大小,即不改变数据的维度,不改变特征的数目也不改变样本的数目,只改变在这些特征下的样本的信息量。对于图像来说,一张图片上的信息可以被聚类如下表示:

这是一组40个样本的数据,分别含有40组不同的信息(x1,x2),我们将所有样本点聚成4类,找出4个质心,我们认为,这些点和他们所属的质心非常相似,因此他们所承载的信息就约等于他们所在的簇的质心所承载的信息。于是,我们可以使用每个样本所在的簇的质心来覆盖原有的样本。这样,40个样本带有的40种取值,就被我们压缩成4组取值,虽然样本量还是40个,但是这40个样本所带的取值其实只有4个,就是分出来的4个质心。 用K-Means聚类中获得的质心来替代原有的数据,可以把数据的信息量压缩到非常小,但又不损失太多信息。接下来就通过一张图片的矢量化来看一看K-Means如何实现压缩数据大小,却不损失太多信息量。

1.使用K-Means矢量量化压缩图片信息

import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.metrics import pairwise_distances_argmin #对两个序列中的点进行距离匹配的函数
from sklearn.datasets import load_sample_image #导入图片数据所用的类
from sklearn.utils import shuffle #洗牌

实例化图片并可视化 

china=load_sample_image("china.jpg")
plt.figure(figsize=(15,15))
plt.imshow(china) #导入3维形成的图片

china.shape
(427, 640, 3)

有427*640个点,每个点有3个特征决定颜色,这里使用RGB编码,每个像素由三个数字来定义

import pandas as pd
newimage=china.reshape(427*640,3)
pd.DataFrame(newimage).drop_duplicates().shape #去除重复值
(96615, 3)

即该图片由96615种颜色组成,我们希望来试试看,能否使用K-Means将颜色压缩到64种,还不严重损失图像的质量。为此,我们要使用K-Means将9w多种颜色聚类成64类,然后使用64个簇的质心来代替全部的9w种颜色。

为了比较,我们还要画出随机压缩到64种颜色的矢量化图像,我们需要随机选取64个样本点作为质心,计算原数据中每个样本点到它们的距离来找出离每个样本最近的随机质心,然后用每个样本所对应的随机质心来替代原样本。两种状况下,我们观察图像可视化之后的状况,以查看图片信息的损失。

plt.imshow在浮点数上表现非常优异,在这里把china中的数据,转换为浮点数,压缩到[0,1]之间---归一化

china=np.array(china,dtype=np.float64)/china.max()

把china从图像格式转换为矩阵格式

w,h,d=original_shape=tuple(china.shape)

assert相当于raise error if not,表示为“不为True就报错”,这里要求d必须等于3,如果不等于就报错

扫描二维码关注公众号,回复: 14691595 查看本文章
assert d==3
image_array=np.reshape(china,(w*h,d))
image_array.shape
(273280, 3)

首先,先使用1000个数据来找出质心

n_clusters=64
image_array_sample=shuffle(image_array,random_state=0)[:1000]
kmeans=KMeans(n_clusters=n_clusters,random_state=0).fit(image_array_sample)

找出质心后,按照已存在的质心对所有数据进行聚类 

labels=kmeans.predict(image_array) #这27w个样本点所对应的簇的质心索引

使用质心替换所有的样本 

image_kmeans=image_array.copy()
for i in range(w*h):
    image_kmeans[i]=kmeans.cluster_centers_[labels[i]]

查看新生成图片的信息 

pd.DataFrame(image_kmeans).drop_duplicates().shape
(64, 3) 

恢复图片的结构 

image_kmeans=image_kmeans.reshape(w,h,d)
image_kmeans.shape
(427, 640, 3)

完整代码:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.metrics import pairwise_distances_argmin #对两个序列中的点进行距离匹配的函数
from sklearn.datasets import load_sample_image #导入图片数据所用的类
from sklearn.utils import shuffle #洗牌
china=load_sample_image("china.jpg")
n_clusters=64
china=np.array(china,dtype=np.float64)/china.max()
w,h,d=original_shape=tuple(china.shape)
assert d==3
image_array=np.reshape(china,(w*h,d))
image_array_sample=shuffle(image_array,random_state=0)[:1000]
kmeans=KMeans(n_clusters=n_clusters,random_state=0).fit(image_array_sample)
labels=kmeans.predict(image_array)
image_kmeans=image_array.copy()
for i in range(w*h):
    image_kmeans[i]=kmeans.cluster_centers_[labels[i]]
image_kmeans=image_kmeans.reshape(w,h,d)

2.对图片进行随机的矢量量化

centroid_random=shuffle(image_array,random_state=0)[:n_clusters]
labels_random=pairwise_distances_argmin(centroid_random,image_array,axis=0)
image_random=image_array.copy()
for i in range(w*h):
    image_random[i]=centroid_random[labels_random[i]]
image_random=image_random.reshape(w,h,d)

3.对原图、KMeans矢量化、随机矢量化三张图可视化

plt.figure(figsize=(10,10))
plt.axis('off')#不要显示坐标轴
plt.title('original image (96615 colors)')
plt.imshow(china)

plt.figure(figsize=(10,10))
plt.axis('off')
plt.title('Quantized image (64 colors,K-Means)')
plt.imshow(image_kmeans)

plt.figure(figsize=(10,10))
plt.axis('off')
plt.title('Quantized image (64 colors,Random)')
plt.imshow(image_random)
plt.show()

观察以上三张图片,我们可以得出:K-Means没有降低维度却实现了对数据进行压缩,矢量化后并没有损失太多信息,成功的地使用64种颜色代替原来的9w+多种颜色;而随机选择的图片颜色效果明显不如上面两幅图,颜色损失过多。 

猜你喜欢

转载自blog.csdn.net/weixin_60200880/article/details/127433098