矢量量化可以理解为数据的压缩,可以看作是一种降维的过程,只是和我们之前知道的降维算法的理论完全不一样。
举例来说,我们有一张600 x 600 个像素点的图片,每个像素点有一个颜色,那么这个图片上存在的信息就有360000个。图片上有很多的像素点之间的颜色差异微乎其微,那么我们就可以将这些像素点看作是同一个像素点(相当于KMeans的质心),将这些像素点的颜色替换为同一个颜色。图片的矢量量化就是将每个像素点的颜色用其所处的质心的颜色来代替。(个人理解,可能不是很正确)
图片的矢量量化有两种方式:
本次代码使用到的相关库
import numpy as np
import pandas as pd
from sklearn.datasets import load_sample_image # 加载图片数据
from sklearn.utils import shuffle # 将数据随机打乱
from sklearn.metrics import pairwise_distances_argmin # 计算数据点之间的距离
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
import warnings
%matplotlib inline
warnings.filterwarnings("ignore") # 忽略警告
源图像总共有273280个像素点,每个点对应一个RGB颜色值。
# 导入颐和园的图片
china = load_sample_image("china.jpg")
china.shape # (427, 640, 3)
# 展示原图
plt.figure(figsize=(10,10))
plt.imshow(china)
查看原始数据。将相同的颜色去重后还是有9w+不同的颜色
# 查看原始数据的信息
width, height, dim = china.shape # 记录花的维度信息
# 将三维信息变为二维,每一列行三个数据,对应RGB的颜色
data_df = pd.DataFrame(data=china.reshape((width * height), dim))
print(data_df.shape) # (273280, 3)
# 去除相同的颜色
data_unique = data_df.drop_duplicates()
print(data_unique.shape) # (96615, 3)
实现思路:从9W+的数据中随机抽取2000个样本,进行训练模型,再调用predict方法将27W+的数据映射到相应的质心,最后将27w+的每个像素点替换为质心的颜色
# 为了加快计算,我们将所有的数据归一化到0-1之间
# 每一个数据除以该列的最大值,就将数据归一化到0-1之间了
data_unique /= data_unique.max()
data_df /= data_df.max() # 原始数据也一定要记得归一化
# 最终要保留的颜色数目,可以粗略的看一下原图估计要保留的数量
n_clusters = 50
# 将9w+的颜色数据乱序,随机抽取2000来训练模型
data_sample = shuffle(data_unique, random_state=1)[:2000]
# 训练模型
km = KMeans(n_clusters=n_clusters, random_state=1).fit(data_sample)
# 获得质心
centers = km.cluster_centers_
centers.shape # (80, 3)
# 调用predict将27w+的数据映射到对应的质心
labels = km.predict(data_df)
# labels 中的数值就对应质心centers的索引
print(labels.shape) # (273280,)
np.unique(labels)
# 将每个样本的颜色换为质心的颜色
new_data = data_df.copy()
for i in range(labels.shape[0]) :
new_data.iloc[i, :] = centers[labels[i]]
pd.DataFrame(data=new_img_KMeans.reshape(width*height, dim)).drop_duplicates().shape # (50, 3)
# 矢量化后的图片只有50种颜色
原图与矢量化后的图片对比
plt.figure(figsize=(16, 8))
# 第一张子图展示原图
plt.subplot(1, 2, 1)
plt.imshow(china)
plt.axis('off') # 不显示坐标轴
plt.title("row image")
# 子图二展示KMeans矢量化后的图片
plt.subplot(1, 2, 2)
plt.title("KMeans image")
plt.imshow(new_img_KMeans)
plt.axis("off")
实现思路:随机从9W+的样本中抽取50个点作为质心,计算27W+个点到这50个质心的距离,将每个点归类带对应的质心,用质心的颜色替换每一个点的颜色。这个方法与KMeans 的区别在于,这里的质心是我们再原始数据中随机抽取的,并不是通过模型计算得出的
# 将9w+的数据打乱并随机抽取50个点作为质心
data_unorder = shuffle(data_unique, random_state=1)
# 随机出50个索引,设置随机种子可以使得每次随机到的数据是一样的,方便调试
np.random.seed(20)
random_index = np.random.choice(np.arange(data_unorder.shape[0]), 50)
# 选出随机质心
random_centers = data_unorder.iloc[random_index, :].values
random_centers.shape
# pairwise_distances_argmin(x1, x2)
# 计算x1中的每个点与x2中的点的最短距离,
# 并返回x1中每个点对应x2中最短距离点在先x2中的索引
labels_random = pairwise_distances_argmin(data_df, random_centers)
# 这里的data_df是归一化过后的,应为前面使用的data_unique也是归一化后的数据
labels_random.shape # (273280,)
# 替换颜色
new_data_random = data_df.copy()
for i in range(data_df.shape[0]) :
new_data_random.iloc[i, :] = random_centers[labels_random[i]]
new_img_random = new_data_random.values.reshape(width, height, dim)
两种方式结果对比
# 展示原图,KMeans,随机质心法的结果对比
plt.figure(figsize=(8, 17))
# 第一张子图展示原图
plt.subplot(3, 1, 1)
plt.imshow(china)
plt.title("row image")
plt.axis("off")
# 第二张子图展示KMeans结果
plt.subplot(3, 1, 2)
plt.imshow(new_img_KMeans)
plt.title("Kmeans image")
plt.axis("off")
# 第三张子图展示随机质心法
plt.subplot(3, 1, 3)
plt.imshow(new_img_random)
plt.title("random center image")
plt.axis("off")
从结果来看,KMeans方法的损失比较少,对一些比较重要的数据的保留程度比较大,而随机质心法,完全是纯随机的选择,最终的结果完全取决于质心的选择