k-means sklearn学习笔记

k-means 聚类

接下来将介绍一种无监督机器学习模型:聚类算法。

聚类算法直接从数据的内在性质中学习最优的划分结果或者确定离散标签类型。

虽然在Scikit-learn或其他地方有许多聚类算法,但最简单、最容易理解的聚类算法可能还得算是k-means聚类算法了,

在sklearn.cluster.KMeans中实现。

%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
sns.set()

简介

k-means算法在不带标签的多维数据集中寻找确定数量的簇。最优的聚类结果需要符合以下两个假设。

· “簇中心点”(cluster center)是属于该簇的所有数据点坐标的算术平均值。

· 一个簇的每个点到该簇中心点的距离,比到其他簇中心点的距离短。

首先生成一个二维数据集,该数据集包含4个明显的簇,由于要演示无监督算法,因此去除可视化图中的标签:

from sklearn.datasets.samples_generator import make_blobs

X,y_true = make_blobs(n_samples=300, centers=4, cluster_std=0.60, random_state=0)
plt.scatter(X[:,0], X[:,1], s=50);

k-means sklearn学习笔记_第1张图片

通过肉眼观察,能轻松地挑选出4个簇,而k-means算法可以自动完成4个簇的识别工作,并且在sklearn中使用通用的评估器API:

from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=4)
kmeans.fit(X)
y_kmeans = kmeans.predict(X)
plt.scatter(X[:,0], X[:,1], c=y_kmeans, s=50, cmap='viridis')

centers = kmeans.cluster_centers_
plt.scatter(centers[:,0], centers[:,1], c='black', s=200, alpha=0.5);

k-means sklearn学习笔记_第2张图片

k-means算法:期望最大化

期望最大化(expectation-maximization, E-M)是一种非常强大的算法,应用与数据科学的很多场景中。

k-means是该算法的一个非常简单并且易于理解的应用,下面将简单介绍E-M算法。简单来说,期望最大化方法包含以下步骤。

(1)猜测一些簇中心点。

(2)重复直至收敛

a.期望步骤(expectation):将点分配至离其最近的簇中心点。

b.最大化步骤(maximization):将簇中心点设置为所有点坐标的平均值

k-means算法实现:

from sklearn.metrics import pairwise_distances_argmin

def find_clusters(X, n_clusters, rseed=2):
    # 1.随机选择簇中心点
    rng = np.random.RandomState(rseed)
    i = rng.permutation(X.shape[0])[:n_clusters]
    centers = X[i]
    
    while True:
        #2a. 基于最近的中心指定标签
        labels = pairwise_distances_argmin(X, centers)
        
        #2b. 根据点的平均值找到新的中心
        new_centers = np.array([X[labels == i].mean(0)
                               for i in range(n_clusters)]) 
        
        #2c. 确认收敛
        if np.all(centers == new_centers):
            break
        centers = new_centers
        
    return centers, labels

centers, labels = find_clusters(X,4)
plt.scatter(X[:,0], X[:,1], c=labels, s=50, cmap='viridis');

k-means sklearn学习笔记_第3张图片

使用期望最大化算法时的注意事项

在使用期望最大化算法时,需要注意几个问题。

可能不会达到全局最优结果

首先,虽然E-M算法可以在每一步汇总改进结果,但是它并不保证可以获得全局最优的解决方案。

例如,如果再上述简单的步骤中使用一个随机种子(random seed),那么某些初始值可能会导致很糟糕的聚类结果:
centers, labels = find_clusters(X,4, rseed=8)
plt.scatter(X[:,0], X[:,1], c=labels, s=50, cmap='viridis');

k-means sklearn学习笔记_第4张图片

虽然E-M算法最终收敛了,但是并没有收敛至全局最优配置。因此,该算法通常需要较多次尝试。

簇数量必须事先定好

k-means还有一个显著地问题:你必须告诉该算法簇数量,因为它无法从数据中自动学习到簇的数量。

centers, labels = find_clusters(X,6)
plt.scatter(X[:,0], X[:,1], c=labels, s=50, cmap='viridis');

k-means sklearn学习笔记_第5张图片

k-means算法智能确定线性聚类边界

k-means 的基本模型假设表明,当簇中心点呈现非线性的复杂形状时,该算法通常不起作用。

k-means 聚类的边界总是线性的,这就意味着当边界很复杂时,算法会失效,用下面的数据来演示kmeans 算法得到的簇标签:

from sklearn.datasets import make_moons
X,y = make_moons(200, noise=.05, random_state=0)

labels = KMeans(2, random_state=0).fit_predict(X)
plt.scatter(X[:,0], X[:, 1], c=labels, s=50,cmap='viridis');

k-means sklearn学习笔记_第6张图片

我们可以通过一个核变换将数据投影到更高维的空间,投影后的数据使线性分离成为可能。

这种核k-means算法在sklearn的SpectralClustering评估器中实现,它使用最近邻图来计算数据的高维表示,然后用k-means算法分配标签

from sklearn.cluster import SpectralClustering
model = SpectralClustering(n_clusters=2,
                          affinity='nearest_neighbors',
                          assign_labels='kmeans')
labels = model.fit_predict(X)
plt.scatter(X[:,0], X[:, 1], c=labels, s=50,cmap='viridis');

/usr/local/lib/python2.7/dist-packages/sklearn/manifold/spectral_embedding_.py:234: UserWarning: Graph is not fully connected, spectral embedding may not work as expected.
  warnings.warn("Graph is not fully connected, spectral embedding"

k-means sklearn学习笔记_第7张图片

可以看出,通过核变换方法,核k-means就能够找到簇之间复杂的非线性边界了。

当数据量较大时,k-means会很慢

由于k-means的每次迭代都必须获取数据集所有的点,因此随着数据量的增加,算法会变得缓慢。

这时可以每一步仅适用数据集的一个个子集来更新簇中心点。

这就是批处理k-means算法的核心思想,该算法在sklearn.cluster.MiniBatchKMeans中实现。

该算法的接口和标准的KMeans接口相同

案例

1. 案例1:用kmeans算法处理手写数字

这次试试能不能不适用原始的标签数据信息,就用kmeans算法识别出类似的数字。

from sklearn.datasets import load_digits
digits = load_digits()
digits.data.shape
(1797, 64)
kmeans = KMeans(n_clusters=10, random_state=0)
clusters = kmeans.fit_predict(digits.data)
kmeans.cluster_centers_.shape
(10, 64)
fig, ax  = plt.subplots(2, 5, figsize=(8,3))
centers = kmeans.cluster_centers_.reshape(10,8,8)
for axi, center in zip(ax.flat, centers):
    axi.set(xticks=[], yticks=[])
    axi.imshow(center, interpolation='nearest', cmap=plt.cm.binary)

k-means sklearn学习笔记_第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]
from sklearn.metrics import accuracy_score
accuracy_score(digits.target, labels)
0.7935447968836951
from sklearn.metrics import confusion_matrix
mat = confusion_matrix(digits.target, labels)
sns.heatmap(mat.T, square=True, annot=True, fmt='d', cbar=False,
           xticklabels=digits.target_names,
           yticklabels=digits.target_names)
plt.xlabel('true label')
plt.ylabel('predict label');

k-means sklearn学习笔记_第9张图片

更进一步地,使用t-分布邻域嵌入算法在执行k-means之前对数据进行预处理。

t-SNE是一个非线性嵌入算法,特别擅长保留簇中的数据点。下面来看看如何实现:

from sklearn.manifold import TSNE

# 投影数据
tsne = TSNE(n_components=2, init='pca', random_state=0)
digits_proj = tsne.fit_transform(digits.data)

#计算类
kmeans = KMeans(n_clusters=10, random_state=0)
clusters = kmeans.fit_predict(digits_proj)

# 排列标签
labels = np.zeros_like(clusters)
for i in range(10):
    mask = (clusters == i)
    labels[mask] = mode(digits.target[mask])[0]
    
# 计算准确度
accuracy_score(digits.target, labels)
0.9371174179187535

案例2: 将k-means用于色彩压缩

聚类算法可以应用与色彩压缩,设想你有一副包含几百万中颜色的图像,

但其实大多数图像中的很大一部分色彩通常是不会被注意到的,而且图像中的狠毒像素都拥有类似或者相同的颜色。

import PIL
from sklearn.datasets import load_sample_image
china = load_sample_image('china.jpg')
ax = plt.axes(xticks=[],yticks=[])
ax.imshow(china);
/usr/local/lib/python2.7/dist-packages/sklearn/datasets/base.py:762: DeprecationWarning: `imread` is deprecated!
`imread` is deprecated in SciPy 1.0.0, and will be removed in 1.2.0.
Use ``imageio.imread`` instead.
  images = [imread(filename) for filename in filenames]
/usr/local/lib/python2.7/dist-packages/sklearn/datasets/base.py:762: DeprecationWarning: `imread` is deprecated!
`imread` is deprecated in SciPy 1.0.0, and will be removed in 1.2.0.
Use ``imageio.imread`` instead.
  images = [imread(filename) for filename in filenames]

k-means sklearn学习笔记_第10张图片

china.shape
(427, 640, 3)
data = china/255.0 #转换成0~1区间值
data = data.reshape(427*640, 3)
data.shape 
(273280, 3)
def plot_pixels(data, title, colors=None, N=10000):
    if colors is None:
        colors = data
        
    rng = np.random.RandomState(0)
    i = rng.permutation(data.shape[0])[:N]
    colors = colors[i]
    R, G, B = data[i].T
    
    fig, ax = plt.subplots(1, 2, figsize=(16,6))
    ax[0].scatter(R, G, color=colors, marker='.')
    ax[0].set(xlabel='Red', ylabel='Green', xlim=(0, 1), ylim=(0, 1))
    
    ax[1].scatter(R, B, color=colors, marker='.')
    ax[1].set(xlabel='Red', ylabel='Blue', xlim=(0,1), ylim=(0,1))
    
    fig.suptitle(title, size=20);
plot_pixels(data, title='Input color space:16 million possible colors')

k-means sklearn学习笔记_第11张图片

现在对像素空间(特征矩阵)使用k-means聚类,将1600万中颜色缩减到16色。

因为处理的是一个非常大的数据集,所以将使用MinBatchKMeans算法对数据集的子集进行计算。

这种算法比标准的kmeans算法速度更快

from sklearn.cluster import MiniBatchKMeans
kmeans = MiniBatchKMeans(200)
kmeans.fit(data)
new_colors = kmeans.cluster_centers_[kmeans.predict(data)]

plot_pixels(data, colors=new_colors, title='Reduced color space:16 colors')

k-means sklearn学习笔记_第12张图片

用计算的结果对原始像素重新着色,即每个像素被指定为距离其最近的簇中心点的颜色。

用新的颜色在图像空间里重新画图:

plt.close()
china_recolored = new_colors.reshape(china.shape)

fig, ax = plt.subplots(1, 2,figsize=(16,6),
                     subplot_kw=dict(xticks=[], yticks=[]))
fig.subplots_adjust(wspace=0.005)
ax[0].imshow(china)
ax[0].set_title('Original Image', size=16)
ax[1].imshow(china_recolored)
ax[1].set_title('16-color Image', size=16);

k-means sklearn学习笔记_第13张图片

虽然右图丢失了某些细节,但是图像总体上还是非常容易辨识的。

右图实现了将近1000000的压缩比。

你可能感兴趣的:(sklearn)