这个分析笔记由Jake Vanderplas编辑汇总。 源代码和license文件在GitHub。 中文翻译由派兰数据在派兰大数据分析平台上完成。 源代码在GitHub上。
今天我们要探索K means算法,这是一个无监督学习的聚类算法。
我们首先进行基本的设置:
In [1]:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
# use seaborn plotting defaults
import seaborn as sns; sns.set()
K-means是一个无监督聚类的算法,也就是它根据数据本身的属性来将数据划分成不同的类别(而不是根据给定的标签)。
K-means是一个相对来说比较容易理解的算法。它每次都会根据数据的分布去寻找聚类中心。每一个点都被划分为不同的分类,每一个点与自己类别的中心在距离上都是最近的。
我们来看一看KMeans在简单的数据分布中是如何运作的。我们将用不同颜色去给不同类别的点上色。
In [2]:
from sklearn.datasets.samples_generator import make_blobs
X, y = make_blobs(n_samples=300, centers=4,
random_state=0, cluster_std=0.60)
plt.scatter(X[:, 0], X[:, 1], s=50);
我们肉眼就可以看出这个数据集合的四个分类。如果您需要对这些数据做一个十分详尽的划分的话,需要的搜索空间将会是指数级别的。幸运的是,scikit-learn实现了一个众所周知的最大期望 (Expectation Maximization)过程,以至于上述问题可以非常快的解决。
In [3]:
from sklearn.cluster import KMeans
est = KMeans(4) # 4个聚类
est.fit(X)
y_kmeans = est.predict(X)
plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=50, cmap='rainbow');
从图中,我们可以看到这个算法划分出的数据集合和我们肉眼划分的几乎一模一样!
In [4]:
from fig_code import plot_kmeans_interactive
plot_kmeans_interactive();
这个算法一般来说会找到并收敛于最优的聚类中心。
In [5]:
from sklearn.datasets import load_digits
digits = load_digits()
In [6]:
est = KMeans(n_clusters=10)
clusters = est.fit_predict(digits.data)
est.cluster_centers_.shape
Out[6]:
我们可以看到现在64维的数据已经被分为了10类。我们来看一看这10个中心分别代表着什么。
In [7]:
fig = plt.figure(figsize=(8, 3))
for i in range(10):
ax = fig.add_subplot(2, 5, 1 + i, xticks=[], yticks=[])
ax.imshow(est.cluster_centers_[i].reshape((8, 8)), cmap=plt.cm.binary)
我们看到,即使没有之前给定的标签。KMeans算法也可以很好的找到数字的分类的中心。
但是数字的顺序被改变了,让我们来解决这个问题:
In [8]:
from scipy.stats import mode
labels = np.zeros_like(clusters)
for i in range(10):
mask = (clusters == i)
labels[mask] = mode(digits.target[mask])[0]
为了更好的评测结果,我们用PCA算法来看一看真实的标签和K-means算法估计的标签的异同:
In [9]:
from sklearn.decomposition import PCA
X = PCA(2).fit_transform(digits.data)
kwargs = dict(cmap = plt.cm.get_cmap('rainbow', 10),
edgecolor='none', alpha=0.6)
fig, ax = plt.subplots(1, 2, figsize=(8, 4))
ax[0].scatter(X[:, 0], X[:, 1], c=labels, **kwargs)
ax[0].set_title('learned cluster labels')
ax[1].scatter(X[:, 0], X[:, 1], c=digits.target, **kwargs)
ax[1].set_title('true labels');
我们再来看一看K-means算法在没有标签信息的时候的分类准确率:
In [10]:
from sklearn.metrics import accuracy_score
accuracy_score(digits.target, labels)
Out[10]:
80%-不错!我们再来看一看预测结果的混淆矩阵:
In [11]:
from sklearn.metrics import confusion_matrix
print(confusion_matrix(digits.target, labels))
plt.imshow(confusion_matrix(digits.target, labels),
cmap='Blues', interpolation='nearest')
plt.colorbar()
plt.grid(False)
plt.ylabel('true')
plt.xlabel('predicted');
再一次说明,这是一个完全的无监督的估计,它达到了80%的正确率。
In [12]:
from sklearn.datasets import load_sample_image
china = load_sample_image("china.jpg")
plt.imshow(china)
plt.grid(False);
这个图片本身是存储在3维的矩阵中的(高度,宽度,RGB):
In [13]:
china.shape
Out[13]:
我们可以把这幅图片当作一个数据点的在3维颜色空间中的集群。我们会对调整颜色RGB数据至0-1之间,并且重新调整矩阵的结构使其成为一个典型的scikit-learn的输入:
In [14]:
X = (china / 255.0).reshape(-1, 3)
print(X.shape)
我们现在有 273,280 个3维数据点。
我们的任务就是运用KMeans去压缩$256^3$种颜色,让颜色的种类总数减小(比如减到64色)。我们想在所有的数据中寻找$N_{color}$个类别中心,然后去用这64个类别中心去创建一副新的图像,在这个新的图像中,原有图像数据中的颜色会根据就近原则被最近的数据中心的颜色代替。
在这里我们使用MiniBatchKMeans
,一个更完备,适用于更大的数据集的预测器:
In [15]:
from sklearn.cluster import MiniBatchKMeans
In [16]:
# reduce the size of the image for speed
n_colors = 64
X = (china / 255.0).reshape(-1, 3)
model = MiniBatchKMeans(n_colors)
labels = model.fit_predict(X)
colors = model.cluster_centers_
new_image = colors[labels].reshape(china.shape)
new_image = (255 * new_image).astype(np.uint8)
# create and plot the new image
with sns.axes_style('white'):
plt.figure()
plt.imshow(china)
plt.title('input: 16 million colors')
plt.figure()
plt.imshow(new_image)
plt.title('{0} colors'.format(n_colors))
我们可以比较输入的图片和输出的图片:我们已经将$256^3$种颜色压缩成了64种。