聊一聊聚类问题—K-means

2,752 阅读6分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

今天聊一聊典型的聚类问题 K-means 以及其代码实现。

范围

  • 降维,聚类问题
  • 生成模型

无监督学习

  • 难于评估
  • 难于调参

聚类问题

个人认为这是一个比较简单容易理解的机器学习算法,但是具有一定实用性。

一个例子

假设我们在超市做客群分析,不同顾客有不同关注点,不同顾客按特征可以划分为不同群体,但是如何划分和划分为那些群体我们都不知道。我们可以让没有任何知识背景的小朋友尝试对动物进行分类,他们就可以根据动物体型和颜色、有没有翅膀等一些特征对动物做出简单分类。

相似的东西进行分组

K-means

k-means 是一种簇搜寻中心的无监督的算法。K-means 是一种迭代的不确定(k 到底指定为多少是 k-means 难点)方法,所谓迭代,是指算法的步骤不断重复产生的每个簇都可以用以下的指标来进行评估。

D=X1,X2,,XND={X^1,X^2,\cdots,X^N}
  • 簇的位置: 找簇中心的坐标ci,i=[1,k]c^i ,i=[1,k],k-means 初始化的时候随机选择一个点作为中心点,然后每个步骤迭代找到新的中心,在新的中心附近的点都相似,并被划分到同一个组

  • 然后判断每一个样本属于某一个类别 b1n={1 0b_1^n = \begin{cases} 1\ 0 \end{cases}

    • 簇的半径: 簇内每个点到簇中心的距离的平均差
    • 簇的规模: 簇内点的总数
    • 簇的密度:簇的规模和簇的半径的比值
  • 然后更新每一个簇的中心位置ci=xnbinxnXnbinc^i = \frac{\sum_{x^n} b^n_ix^n}{\sum_{X^n}b_i^n}

距离

明氏距离

对于样本点xi(x1i,x2i,,xmi)x_i(x_{1}^i,x_{2}^i,\dots , x_{m}^i) 和样本点xi(x1j,x2j,,xmj)x_i(x_{1}^j,x_{2}^j,\dots , x_{m}^j)

dmin(xi,xj)=(k=1mxikxjkp)1pd_{min}(x_i,x_j) = (\sum_{k=1}^m|x_{ik} - x_{jk}|^p)^{\frac{1}{p}}

根据明氏距离我们根据不同 p 取值来得到曼哈顿距离、欧式距离和切比雪夫距离

曼哈顿距离、欧式距离和切比雪夫距离

  • 当 p = 1 时,曼哈顿距离(街区距离)(Manhattan Distance)
dmin(xi,xj)=(k=1mxikxjk)d_{min}(x_i,x_j) = (\sum_{k=1}^m|x_{ik} - x_{jk}|)
  • 当 p = 2 时,欧式距离(Euclidean Distance)
dmin(xi,xj)=k=1mxikxjk2d_{min}(x_i,x_j) = \sqrt{\sum_{k=1}^m|x_{ik} - x_{jk}|^2}
  • pp \rightarrow \infty 切比雪夫距离(Chebyshev Distance)
dmin(xi,xj)=limp(k=1mxikxjkp)1p=maxk(xkixkj)d_{min}(x_i,x_j) = \lim_{p \rightarrow \infty}(\sum_{k=1}^m|x_{ik} - x_{jk}|^p)^{\frac{1}{p}} = \max_{k} (|x_{k}^i - x_{k}^j|)

K-means 模型的评估

如何评估输出的簇的好坏呢?因为是无监督问题,无法给出如精度、召回率、准确率、F1 分数或其他类似指标的评估方法,我们将采用所谓轮廓系数来评估 Kmeans 的结果。轮廓系数的值介于-1 到 1。

  • 负值说明簇的半径大于簇之间的距离,也就是两个簇之间有重叠
  • 值越大,也就是越接近 1 表示聚类结果越好

轮廓系数

我们知道轮廓系数用于衡量 Kmean 模型的好坏,那么我们应该如何计算轮廓系数。 S1=yiximax(xi,yi)S_1 = \frac{y_i - x_i}{\max (x_i,y_i)}

  • xix_i 表示在簇 C 里点 i 到簇内其他点距离的平均值
  • 然后计算 i 点到其他簇的所有点的距离平均值,选择其中最小值yiy_i

每个簇里所有点的轮廓系数的平均值可以用来衡量这个簇的质量,所有点的轮廓系数的平局值可以用来衡量聚类分簇的质量。

def get_random_data():
    x_1 = np.random.normal(loc=0.2,scale=0.2,size=(100,100))
    x_2 = np.random.normal(loc=0.9,scale=0.1,size=(100,100))
    #np.r_是按列连接两个矩阵,就是把两矩阵上下相加,要求列数相等,类似于pandas中的concat()     
    x = np.r_[x_1,x_2]
    return x
x = get_random_data()
# plt.cla()
plt.figure()
plt.title("Generated Data")
plt.scatter(x[:,0],x[:,1])
plt.show()
print(x.shape)

kmean_001.png

from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
def form_clusters(x,k):
    # k 是划分出的簇的个数
    no_clusters = k
    model = KMeans(n_clusters=no_clusters,init='random')
    model.fit(x)
    labels = model.labels_
#     print(labels)
    # 计算轮廓系数     
    sh_score = silhouette_score(x,labels)
    return sh_score
sh_scores = []
for i in range(1,5):
    sh_score = form_clusters(x,i+1)
    sh_scores.append(sh_score)
no_clusters = [i+1 for i in range(1,5)]
plt.figure(2)
plt.plot(no_clusters,sh_scores)
plt.title("Cluster Quality")
plt.xlabel("No of clusters k")
plt.ylabel("Sihouette Coefficient")
plt.show()

kmean_002.png

k-means 是一种迭代的算法,大致上步骤如下

  1. 从数据集中随机选择 k 个点作为簇的初始中心点
  2. 然后执行以下步骤直到收敛
    1. 将点分配给最近的簇中心,计算这个点和簇中心点的距离
    2. 基于本次迭代过程中分配的点重新计算簇中心
    3. 如果分配点和上一次迭代过程里的都一样,则算法收敛到一个最优解,退出循环。

基本概念

  • 要得到簇的数量,需要指定 k 值
  • 质心:均值,即向量各个维度平均即可
  • 距离度量: 常用欧几里得距离和余弦相识度
  • 优化目标:mini=1kxCidist(ci,x)2 min \sum_{i=1}^k \sum_{x \in C_i} dist(c_i,x)^2

kmean 算法

  1. 指定聚类 k
  2. 初始化 k 个类群的质心,初始化质心的方法有如下两种
  • 随机法:这个比较好理解就是随机指定初始化质心
  • 最远距离法:
  1. 依次计算每一个样本点到各类群质心的距离,根据最小距离的原则,把每一个样本点分配到距离他最近的质心。
  2. 分配完每一个样本点,更新每一个类群的质心,Kmeans 算法的质心计算公式为该类群所有样本点的均值向量,第 i 类群的CiC_i 的质心计算公式为
μi=1nixCix\mu_i =\frac{1}{n_i} \sum_{x \in C_i} x
  1. 判断是否满足停止条件
  • 指定迭代次数
  • 指定质心变动范围阈值 ξ\xi 如果 μtμt1<ξ|\mu_t - \mu_{t-1}| < \xi

kmean 算法

  • 技术型方法 希望每一个样本离他被分配到的簇尽可能地近,而离其他簇尽可能地远,对应样本点 i 的轮廓系数。
s(i)=b(i)a(i)maxb(i),a(i)=minDiCj,jimaxminDiCj,DiCjs(i) = \frac{b(i) - a(i)}{\max { b(i), a(i) }} = \frac{\min { D_{iC_{j}} , j \ne i }}{\max { \min { D_{iC_{j}}, D_{iC_{j}} } }}
  • a(i)a(i) 就是同类间距离

  • b(i)b(i) 就是与异类的距离

  • 同类间距离 a(i)=avga(i) = avg

  • 异类间距离 b(i)=minCavgDavgb(i) = \min{ C_{avg} D_{avg} \dots }

  • 其实实际上我们并不是将样本点与其他类别每一个样本点求距离,取而代之是计算样本点和其他类别的质心的距离。

  • 业务方法 在我们聚类结束之后,需要观察分类结果,然后推断这些群体是否有对应业务场景。

聚类的注意事项

  • 连续类型数据标准化 因为我们选择不同单位我们数据变化会很大,也会影响距离的计算。 所以在做聚类之前需要对数据进行标准化
xi=xixminxmaxxminx_i = \frac{x_i - x_{min}}{x_{max} - x_{min}}
  • 分类型数据标准化 由于聚类算法的距离计算是面对数值型变量,因此当变量中包括分类类型变量时,聚类算法不能直接处理,而要先对分类类型变量进行数值化处理,引入虚拟变量
记录xx1x_1x2x_2x3x_3
1A100
2A010
3A001

但是值得注意是,虽然采用虚拟变量的方法来解决了分类型变量的距离计算问题,但是通过虚拟变量进行计算将导致分类型变量的权重大于其他数值型变量。例如上表所示,记录1和记录2虽然只是一个相差一个水平,但是两者在变量x距离变为 (10)2+(01)2+(00)2=21.414 \sqrt{(1-0)^2 + (0-1)^2 + (0 -0)^2} = \sqrt{2} \approx 1.414

为了保证分类类型变量的权重和数值型变量一致,会同样要对虚拟变量进行标准化,

12=0.707\sqrt{\frac{1}{2}} =\approx 0.707
(120)2+(012)2+(00)2=1\sqrt{(\sqrt{\frac{1}{2}}-0)^2 + (0-\sqrt{\frac{1}{2}})^2 + (0 -0)^2} = 1