机器学习14-数据聚类

前言

  无监督学习(Unsupervised Learning)着重于发现数据本身的分布特点。与监督学习(Supervised Learning)不同,无监督学习不需要对数据进行标记。这样,在节省大量人工的同时,也让可以利用的数据规模变得不可限量。
  从功能角度讲,无监督学习模型可以帮助我们发现数据的“群落”,同时也可以寻找“离群”的样本;另外,对于特征维度特别高的样本,我们同样可以通过无监督的学习对数据进行降维,保留最具有区分性的低维度特征。

数据聚类

  数据聚类是无监督学习的主流应用之一。最为经典并且易用的聚类模型,当属K均值(K-means)算法。该算法要求我们预先设定聚类的个数,然后不断更新聚类中心;经过几轮这样的迭代,最后的目标就是要让所有数据点到其所属聚类中心距离的平方和趋于稳定。

K-均值算法

  1. 随机布设K个特征空间内的点作为初始的聚类中心
  2. 根据每个数据的特征向量,从K个聚类中心中寻找距离最近的一个,并且把该数据标记为从属于这个聚类中心
  3. 在所有的数据都被标记过聚类中心之后,根据这些数据新分配的类簇,重新计算各个簇的聚类中心
  4. 如果一轮下来,所有的数据点从属的聚类中心与上一次的分配的聚簇没有变化,那么迭代可以停止;否则回到步骤二继续循环
    这里写图片描述

数据描述

使用手写数字图像数据的完整版本。完整的手写数字图像分为两个数据集合,其中训练数据样本3823条,测试数据1797条;图像数据通过8*8的像素矩阵表示,共有64个像素维度;一个目标维度用来标记每个图像样本代表的数字类别。该数据没有缺失的特征值,并且不论是训练还是测试样本,在数字类别方面都采样得非常平均,是一份非常规整的数据集。

code

import numpy as np
import pandas as pd
from sklearn.cluster import KMeans
from sklearn import metrics

#使用pandas分别读取训练数据与测试数据集
digits_train = pd.read_csv('http://archive.ics.uci.edu/ml/machine-learning-databases/optdigits/optdigits.tra', header=None)
digits_test = pd.read_csv('http://archive.ics.uci.edu/ml/machine-learning-databases/optdigits/optdigits.tes', header=None)

#从训练与测试数据集上都分离出64维度的像素特征与1维度的数字目标。
X_train = digits_train[np.arange(64)]
y_train = digits_train[64]

X_test = digits_test[np.arange(64)]
y_test = digits_test[64]

kmeans = KMeans(n_clusters=10)
kmeans.fit(X_train)
y_pred = kmeans.predict(X_test)

#使用ARI进行KMeans聚类性能评估
print(metrics.adjusted_rand_score(y_test, y_pred))
#out[]: 0.6677731542678766

利用轮廓系数评价不同类簇数量的K-means聚类实例

import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score

#分割出3*2=6个子图,并在1号子图作图。
plt.subplot(3, 2, 1)
#初始化原始数据点
x1 = np.array([1, 2, 3, 1, 5, 6, 5, 5, 6, 7, 8, 9, 7, 9])
x2 = np.array([1, 3, 2, 2, 8, 6, 7, 6, 7, 1, 2, 1, 1, 3])
X = np.array(list(zip(x1, x2))).reshape((len(x1), 2))

#在1号子图做出原始数据点阵的分布。
plt.xlim([0, 10])
plt.ylim([0, 10])
plt.title('Instances')
plt.scatter(x1, x2)

colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k', 'b']
markers = ['o', 's', 'D', 'v', '^', 'p', '*', '+']

clusters = [2, 3, 4, 5, 8]
subplot_counter = 1
sc_scores = []
for t in clusters:
    subplot_counter +=1
    plt.subplot(3, 2, subplot_counter)
    kmeans_model = KMeans(n_clusters=t).fit(X)

    for i, l in enumerate(kmeans_model.labels_):
        plt.plot(x1[i], x2[i], color=colors[l], marker=markers[l], ls='None')

    plt.xlim([0, 10])
    plt.ylim([0, 10])
    sc_score = silhouette_score(X, kmeans_model.labels_, metric='euclidean')
    sc_scores.append(sc_score)
    #绘制轮廓系数与不同类簇数量的直观显示图
    plt.title('K=%s, silhouette coefficient=%0.3f' %(t, sc_score))
plt.figure()
plt.plot(clusters, sc_scores, '*-')
plt.xlabel('Number of clusters')
plt.ylabel('Silhouette Coefficient Score')
plt.show()

out

这里写图片描述
这里写图片描述

特点分析

K-means聚类模型所采用的迭代式算法,直观易懂并且非常实用。只是有两大缺陷:①容易收敛到局部最优解。这是算法自身的理论缺陷所造成的,无法轻易地从模型设计上弥补,却可以通过执行多次K-means算法来挑选性能表现更好的初始中心点,这样的工程方法代替。②需要预先设定簇的数量。

一种“肘部”观察法–粗略预估相对合理的类簇个数

因为K-means模型最终期望所有数据点到其所属的类簇距离的平方和趋于稳定,所以我们可以通过观察这个数值随着K的走势来找出最佳的类簇数量。理想条件下,这个折线在不断下降并且趋于平缓的过程中会有斜率的拐点,同时意味着从这个拐点对应的K值开始,类簇中心的增加不会过于破坏数据聚类的结构。

demo

import numpy as np
from sklearn.cluster import  KMeans
from scipy.spatial.distance import cdist
import matplotlib.pyplot as plt

#使用均匀分布函数随机三个簇,每个簇周围10个数据样本。
cluster1 = np.random.uniform(0.5, 1.5, (2, 10))
cluster2 = np.random.uniform(5.5, 5.4, (2, 10))
cluster3 = np.random.uniform(3.0, 4.0, (2, 10))
#cluster1:2*10的numpy.ndarray ,其中每个元素e,0.5<=e<1.5
#[[1.39080876 1.29487435 1.49740262 0.65051787 1.07807684 1.18259848 1.21701924 0.99499536 0.80161768 0.67702382]
#[1.1974295  1.19775442 1.30237798 1.00460935 0.58442236 0.82107174 1.44056969 0.71225803 0.56054247 1.3009035 ]]

#绘制30个数据样本的分布图像
#cluster1,cluster2,cluster3按行拼接起来再转置
X = np.hstack((cluster1, cluster2, cluster3)).T
#X:
# [[1.39080876 1.1974295 ]
#  [1.29487435 1.19775442]
#  [1.49740262 1.30237798]
#  [0.65051787 1.00460935]
#  [1.07807684 0.58442236]
#  [1.18259848 0.82107174]
#  [1.21701924 1.44056969]
#  [0.99499536 0.71225803]
#  [0.80161768 0.56054247]
#  [0.67702382 1.3009035 ]
#  [5.49482588 5.44584753]
#  [5.47683882 5.408807  ]
#  [5.43988704 5.4221159 ]
#  [5.49709822 5.45061113]
#  [5.48513972 5.48810633]
#  [5.4205895  5.4846828 ]
#  [5.44853443 5.4017957 ]
#  [5.46667551 5.46100313]
#  [5.44246988 5.45905491]
#  [5.44735158 5.46226437]
#  [3.30646659 3.5563691 ]
#  [3.02328948 3.78882544]
#  [3.71199165 3.08475477]
#  [3.3152504  3.07635641]
#  [3.81260422 3.74598237]
#  [3.57650922 3.38543256]
#  [3.66950306 3.73714519]
#  [3.49615472 3.02012612]
#  [3.06106733 3.8572852 ]
#  [3.93847104 3.84530988]]
plt.scatter(X[:, 0], X[:, 1])
plt.xlabel('x1')
plt.ylabel('x2')
plt.show()

#测试9种不同聚类中心数量下每种情况的聚类质量,并作图。
K = range(1, 10)
meandistortions = []

for k in K:
    kmeans = KMeans(n_clusters=k)
    kmeans.fit(X)
    #用cdist函数计算每个点与每个聚类中心的距离,结果是,行索引为每个点,列索引为每个聚类中心的距离矩阵。
    #np.min取出每行最小值,即每个点到它所属的聚类中心的距离
    #用sum(,axis=1)计算矩阵列和,即算出每个点到它所属的中心的距离和
    meandistortions.append(sum(np.min(cdist(X, kmeans.cluster_centers_, 'euclidean'), axis=1))/X.shape[0])

plt.plot(K, meandistortions, 'bx-')
plt.xlabel('k')
plt.ylabel('Average Dispersion')
plt.title('Selecting k with the Elbow Methhod')
plt.show()

随机采样的三个类簇的数据点,如下图所示:
这里写图片描述
通过下图可以观察到,类簇数量为1或2的时候,样本距所属类簇的平均距离的下降速度很快,这说明更改K值会让整体聚类结构有很大改变,也意味着新的聚类数量让算法有更大的收敛空间,这样的K值不能反映真实的类簇数量。而当K=3时,平均距离的下降速度有了显著放缓,这意味着进一步增加K值不再会有利于算法的收敛,也同时暗示着K=3是相对最佳的类簇数量。
这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_38195197/article/details/81264868