虽然k均值是一种聚类算法,但在k均值和分解方法之间存在一些相似之处。k均值尝试利用簇中心来表示每个数据点,可以看作仅用一个分量来表示每个数据点,该分量由簇中心给出。这种观点将k均值看作是一种分解方法,其中每个点用单一分量来表示。这种观点被称为矢量量化。
我们来并排比较PCA、NMF和k均值,分别显示提取的分量,以及利用100个分量对测试集中人脸的重建。对于k均值,重建就是在训练集中找到最近的簇中心:
import mglearn.plots
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs,make_moons
from sklearn.cluster import KMeans
from sklearn.datasets import fetch_lfw_people
from sklearn.model_selection import train_test_split
import numpy as np
from sklearn.decomposition import PCA,NMF
people=fetch_lfw_people(data_home = "C:\\Users\\86185\\Downloads\\",min_faces_per_person=20,resize=.7)
image_shape=people.images[0].shape
mask=np.zeros(people.target.shape,dtype=np.bool_)
for target in np.unique(people.target):
mask[np.where(people.target==target)[0][:50]]=1
X_people=people.data[mask]
y_people=people.target[mask]
X_train,X_test,y_train,y_test=train_test_split(
X_people,y_people,stratify=y_people,random_state=0
)
nmf=NMF(n_components=100,random_state=0)
nmf.fit(X_train)
pca=PCA(n_components=100,random_state=0)
pca.fit(X_train)
kmeans=KMeans(n_clusters=100,random_state=0)
kmeans.fit(X_train)
X_reconstrycted_pca=pca.inverse_transform(pca.transform(X_test))
X_reconstrycted_kmeans=kmeans.cluster_centers_[kmeans.predict(X_test)]
X_reconstrycted_nmf=np.dot(nmf.transform(X_test),nmf.components_)
fig,axes=plt.subplots(3,5,figsize=(8,8),
subplot_kw={'xticks':(),'yticks':()})
fig.suptitle('对比')
for ax,comp_kmeans,comp_pca,comp_nmf in zip(axes.T,kmeans.cluster_centers_,pca.components_,nmf.components_):
ax[0].imshow(comp_kmeans.reshape(image_shape))
ax[1].imshow(comp_pca.reshape(image_shape),cmap='viridis')
ax[2].imshow(comp_nmf.reshape(image_shape))
axes[0,0].set_ylabel('kmeans')
axes[1,0].set_ylabel('pca')
axes[2,0].set_ylabel('nmf')
plt.show()
fig,axes=plt.subplots(4,5,subplot_kw={'xticks':(),'yticks':()},fig=(8,8))
fig.suptitle('R')
for ax,orig,rec_kmeans,rec_pca,rec_nmf in zip(axes.T,X_test,X_reconstrycted_kmeans,X_reconstrycted_pca,X_reconstrycted_nmf):
ax[0].imshow(orig.reshape(image_shape))
ax[1].imshow(rec_kmeans.reshape(image_shape))
ax[2].imshow(rec_pca.reshape(image_shape))
ax[3].imshow(rec_nmf.reshape(image_shape))
axes[0,0].set_ylabel('original')
axes[1,0].set_ylabel('kmeans')
axes[2,0].set_ylabel('pca')
axes[3,0].set_ylabel('nmf')
plt.show()
利用k均值做矢量量化的一个有趣之处在于,可以用比输入维度更多的簇来对数据进行编码。
回到two_moons数据集,利用PCA或NMF,我们对这个数据无能为力,因为它只有两个维度。使用PCA或NMF将其降到一维,将会完全破坏数据的结构。但通过使用更多的簇中心,我们可以用k均值找到一种更具表现力的表示。
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs,make_moons
from sklearn.cluster import KMeans
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),linewidths=2,cmap='Paired')
plt.xlabel('Feature 0')
plt.ylabel('Feature 1')
plt.show()
print('簇:\n{}'.format(y_pred))
我们使用了10个簇中心,也就是说,现在的每个点都被分配了0到9之间的一个数字。我们可以将其看作10个分量表示的数据(我们有10个新特征),只有表示该点对应的簇中心的哪个特征不为0,其他特征均为0。利用这10维表示,现在可以用线性模型来划分两个半月形,而利用原始的两个特征是不可能做到这一点的。
将到每个簇中心的距离作为特征,还可以得到一种表现力更强的数据表示。可以利用kmeans的transform方法来完成这一点:
distance_features=kmeans.transform(X)
print('距离特征形状:{}'.format(distance_features.shape))
print('距离特征:{}'.format(distance_features))
k均值是非常流行的聚类算法,因为它不仅相对容易理解和实现,而且运行速度也相对较快。k均值可以轻松扩展到大型数据集。
k均值的缺点之一在于,它依赖于随机初始化,也就是说,算法的输出依赖于随机种子。默认情况下,scikit-learn用10种不同的随机初始化将算法运行10次,并返回最佳结果。