机器学习--K均值聚类


一、无监督学习

机器学习的一种方法,没有给定事先标记的训练实例,自动对输入的数据进行分类或分群。

优点:

  • 算法不受监督信息的约束,可能考虑到新的信息
  • 不需要标签数据,极大程度扩大数据样本

主要应用:

  • 聚类分析
  • 关联规则
  • 维度缩减

二、KMeans聚类

2.1 概览

  • 根据数据与中心点距离划分类别
  • 基于类别数据更新中心点
  • 重复过程直到收敛

特点:

  1. 实现简单,收敛快
  2. 需要指定类别数量

K-均值聚类:以空间中k个点为中心进行聚类,对最靠近他们的对象归类,是聚类算法中最为基础但也最为重要的算法。

2.2 理论介绍

k均值聚类是基于样本集合划分的聚类算法。k均值聚类将样本集合划分为k个子集,构成k个类,将n个样本分到k个类中,每个样本到期所属类的中心的距离最小。每个样本只能属于一个类,所以k均值聚类是硬聚类。下面分别介绍k均值聚类的模型、策略、算法。

大家可以直接查看概览和2.3中的案例来对k均值聚类有个直观的理解。

2.2.1 模型

给定n个样本的集合X= { x 1 . x 2 , x 3 , . . . x n } \{x_1.x_2,x_3,...x_n\} {x1.x2,x3,...xn},每个样本由一个特征向量表示,特征向量的维数是m。k均值聚类的目标是将n个样本分到k个不同的类或簇中(假设k<n)。k个类 G 1 , G 2 . . . G k G_1,G_2...G_k G1,G2...Gk形成样本集合X的划分。其中 G i ∩ G j = ∅ G_i\cap{G_j}=\varnothing GiGj=, ⋃ i = 1 k G i = X \quad \bigcup_{i=1}^{k} G_i=X i=1kGi=X。用C表示划分,一个划分对应着一个聚类结果。
划分C是一个多对一的函数。k均值聚类的模型是一个从样本到类的函数。

2.2.2 策略

k均值聚类归结为样本集合X的划分,或者从样本到类的函数的选择问题。k均值聚类的策略时通过损失函数的最小化选取最优的划分或函数 C ∗ C^* C
首先,采用欧式距离平方作为样本之间的距离 d ( x i , x j ) d(x_i,x_j) d(xi,xj)
d ( x i , x j ) = ∑ k = 1 m ( x k i − x k j ) 2 = ∣ ∣ x i − x j ∣ ∣ 2 d(x_i,x_j)=\sum_{k=1}^m(x_{ki}-x_{kj})^2=||x_i-x_j||^2 d(xi,xj)=k=1m(xkixkj)2=xixj2
然后定义样本与其所属类的中心之间的距离的总和为损失函数,即
W ( C ) = ∑ i = 1 k ∑ c ( i ) = 1 ∣ ∣ x i − x l ˉ ∣ ∣ 2 W(C)=\sum_{i=1}^k\sum_{c(i)=1}||x_i-\bar{x_l}||^2 W(C)=i=1kc(i)=1xixlˉ2

其中 x l ˉ = ( x 1 l ˉ , x 2 l ˉ . . . x n l ˉ ) T \bar{x_l}=(\bar{x_{1l}},\bar{x_{2l}}...\bar{x_{nl}})^T xlˉ=(x1lˉ,x2lˉ...xnlˉ)T l l l 个类的均值或中心, n l = ∑ i = 1 n I ( C ( i ) = l ) n_l=\sum_{i=1}^nI(C(i)=l) nl=i=1nI(C(i)=l)是指示函数,取值为1或0。函数 W ( C ) W(C) W(C)也称为能量,表示相同类中的样本相似的程度。

k均值聚类就是求解最优化问题:
C ∗ = a r g m i n C W ( C ) C^*=arg\underset{C}{min}W(C) C=argCminW(C)
= a r g m C i n ∑ l = 1 k ∑ C ( i ) = 1 ∣ ∣ x i − x j ∣ ∣ 2 =arg\underset{C}min\sum_{l=1}^k\sum_{C(i)=1}||x_i-x_j||^2 =argCminl=1kC(i)=1xixj2

arg min 就是使后面这个式子达到最小值时的变量的取值

相似的样本被聚类到同类中,损失函数最小,这个目标函数的最优化能达到聚类的效果。

但是,这是一个组合优化问题。n个样本分到k类,所有可能分法的数目是:
S ( n , k ) = 1 k ! ∑ l = 1 k ( − 1 ) k − 1 ( k l ) k n S(n,k)=\frac{1}{k!}\sum_{l=1}^k(-1)^{k-1}\begin{pmatrix} k \\ l \end{pmatrix}k^n S(n,k)=k!1l=1k(1)k1(kl)kn
这个问题是指数级的。事实上,k均值聚类的最优解求解问题是NP困难问题。现实中采用迭代的方法求解。

2.2.3 算法

k均值聚类的算法是一个迭代的过程,每一次迭代包括2个步骤。首先选择k个类的中心,将样本逐个指派到与其最近的中心的类中,得到一个聚类结果;然后更新每个类的样本的均值,作为类的新中新;重复以上步骤,直到收敛为止。

算法叙述:
输入:n个样本的集合X;
输出:样本集合的聚类 C ∗ C^* C

  1. 初始化。令t=0,随机选择k个样本点作为初始聚类中心 m ( 0 ) = ( m 1 ( 0 ) , m 2 ( 0 ) , m 3 ( 0 ) , , . . . , m k ( 0 ) ) m^{(0)}=(m_1^{(0)},m_2^{(0)},m_3^{(0),},...,m_k^{(0)}) m(0)=(m1(0),m2(0),m3(0),,...,mk(0))
  2. 对样本进行聚类。对固定的类中心 m ( t ) = ( m 1 ( t ) , m 2 ( t ) , . . . , m k ( t ) ) m(t)=(m_1^{(t)},m_2^{(t)},...,m_k^{(t)}) m(t)=(m1(t),m2(t),...,mk(t)),其中 m l ( t ) m_l^{(t)} ml(t)为类 G l G_l Gl的中心,计算每个样本到类中心的距离,将每个样本指派到与其最近的中心的类中,构成聚类结果 C ( t ) C^{(t)} C(t)
  3. 计算新的类中心。对聚类结果 C ( t ) C^{(t)} C(t),计算当前各个类中的样本的均值,作为新的类中心的 m ( t + 1 ) = ( m 1 ( t + 1 ) , m 2 ( t + 1 ) , . . . , m k ( t + 1 ) ) m^{(t+1)}=(m_1^{(t+1)},m_2^{(t+1)},...,m_k^{(t+1)}) m(t+1)=(m1(t+1),m2(t+1),...,mk(t+1))
  4. 如果迭代收敛或符合停止条件(中心点不再变化),输出 C ∗ = C ( t ) C^*=C^{(t)} C=C(t)。否则,返回step(2)

k均值聚类算法的复杂度为 O ( m n k ) , O(mnk), O(mnk),其中m是样本维数,n是样本个数,k是类别个数

2.3 案例讲解

给定含有5个样本的集合 X = [ 0 0 1 5 5 2 0 0 0 2 ] X=\begin{bmatrix} 0 & 0 &1&5&5 \\ 2 & 0 &0 &0 &2\end{bmatrix} X=[0200105052] 试用k均值聚类算法将样本聚到2个类中。

  1. 选择2个点作为类的中心。假设选择 m 1 ( 0 ) = x 1 = ( 0 , 2 ) T , m 2 ( 0 ) = x 2 = ( 0 , 0 ) T m_1^{(0)}=x_1=(0,2)^T,m_2^{(0)}=x_2=(0,0)^T m1(0)=x1=(0,2)T,m2(0)=x2=(0,0)T
  2. m 1 ( 0 ) , m 2 ( 0 ) m_1^{(0}),m_2^{(0)} m1(0),m2(0)作为 G 1 ( 0 ) , G 2 ( 0 ) G_1^{(0)},G_2^{(0)} G1(0),G2(0)的中心,计算 x 3 = ( 1 , 0 ) T , x 4 = ( 5 , 0 ) T , x 5 = ( 5 , 2 ) T x_3=(1,0)^T,x_4=(5,0)^T,x_5=(5,2)^T x3=(1,0)T,x4=(5,0)T,x5=(5,2)T m 1 ( 0 ) , m 2 ( 0 ) m_1^{(0)},m_2^{(0)} m1(0),m2(0)的欧式距离。
    d ( x 3 , m 1 ( 0 ) ) = 1 + 2 2 = 5 , d ( x 3 , m 2 ( 0 ) ) = 1 d(x_3,m_1^{(0)})=1+2^2=5,d(x_3,m_2^{(0)})=1 d(x3,m1(0))=1+22=5,d(x3,m2(0))=1
    d ( x 4 , m 1 ( 0 ) ) = 5 2 + 2 2 = 29 , d ( x 4 , m 1 ( 0 ) ) = 5 2 = 25 d(x_4,m_1^{(0)})=5^2+2^2=29,d(x_4,m_1^{(0)})=5^2=25 d(x4,m1(0))=52+22=29,d(x4,m1(0))=52=25
    d ( x 5 , m 1 ( 0 ) ) = 5 2 = 25 , d ( x 5 , m 1 ( 0 ) ) = 5 2 = 29 d(x_5,m_1^{(0)})=5^2=25,d(x_5,m_1^{(0)})=5^2=29 d(x5,m1(0))=52=25,d(x5,m1(0))=52=29

所以分别把 x 3 放 到 G 2 ( 0 ) , x 4 放 到 G 2 ( 0 ) , x 5 放 到 G 1 ( 0 ) x_3放到G_2^{(0)},x_4放到G_2^{(0)},x_5放到G_1^{(0)} x3G2(0),x4G2(0)x5G1(0)中。

  1. 得到新的类 G 1 ( 1 ) = { x 1 , x 5 } , G 2 ( 1 ) = { x 2 , x 3 , x 4 } G_1^{(1)}=\{ x_1,x_5\},G_2^{(1)}=\{x_2,x_3,x_4\} G1(1)={x1,x5},G2(1)={x2,x3,x4},计算新的类中心 m 1 ( 1 ) , m 2 ( 1 ) m_1^{(1)},m_2^{(1)} m1(1),m2(1)

m 1 ( 1 ) = ( x 1 + x 5 2 ) = ( 2.5 , 2 ) T m_1^{(1)}=(\frac{x_1+x_5}{2})=(2.5,2)^T m1(1)=(2x1+x5)=(2.5,2)T
m 2 ( 1 ) = ( x 2 + x 3 + x 4 3 ) = ( 2 , 0 ) T m_2^{(1)}=(\frac{x_2+x_3+x_4}{3})=(2,0)^T m2(1)=(3x2+x3+x4)=(2,0)T

  1. 重复2,3 ,发现得到了类没有改变
  2. 聚类结束, G 1 ( 1 ) = { x 1 , x 5 } , G 2 ( 1 ) = { x 2 , x 3 , x 4 } G_1^{(1)}=\{ x_1,x_5\},G_2^{(1)}=\{x_2,x_3,x_4\} G1(1)={x1,x5},G2(1)={x2,x3,x4}

2.4 Python实现

为了判断模型的预测结果,我们事先给数据集提供了分类信息。
代码和数据集我都已经上传到GitHub中,有需要可以前往下载。访问不了可以私信找我。

2.4.1 导入数据处理相关库以及读取数据

import pandas as pd
import numpy as np
data = pd.read_csv("data.csv")
data.head()
V1	V2	labels
0	2.072345	-3.241693	0
1	17.936710	15.784810	0
2	1.083576	7.319176	0
3	11.120670	14.406780	0
4	23.711550	2.557729	0

2.4.2 查看相关数据并进行可视化展示

X=data.drop(['labels'],axis=1)
Y = data.loc[:,'labels']

为了方便后面的理解,我们先查看一下Y的信息频率,关于这个信息频率后面会用到。

具体的pandas操作可前往数据处理–pandas下查看

pd.value_counts(Y)
2    1156
1     954
0     890
Name: labels, dtype: int64

为了方便我们直观的理解,我们将数据通过matplotlib的方法进行可视化。

# 可视化数据
from matplotlib import pyplot as plt
fig1 = plt.figure()
label0 = plt.scatter(X.loc[:,'V1'][Y==0],X.loc[:,'V2'][Y==0])
label1 = plt.scatter(X.loc[:,'V1'][Y==1],X.loc[:,'V2'][Y==1])
label2 = plt.scatter(X.loc[:,'V1'][Y==2],X.loc[:,'V2'][Y==2])
plt.title("un-labeld data")
plt.xlabel("V1")
plt.ylabel("V2")
plt.legend((label0,label1,label2),('label0','label1','label2'))
plt.show()

在这里插入图片描述

2.4.3 导入sklearn并训练模型

其中n_clusters=3对应着我们分三类,random_state保证随机性

from sklearn.cluster import KMeans
KM=KMeans(n_clusters=3,random_state=0)
KM.fit(X)

我们经过模型训练后,获取三个中心点。

centers = KM.cluster_centers_
centers
array([[ 40.68362784,  59.71589274],
       [ 69.92418447, -10.11964119],
       [  9.4780459 ,  10.686052  ]])

为了直观的感知模型训练的效果,我们进行可视化展示。

fig2 = plt.figure()
label0 = plt.scatter(X.loc[:,'V1'][Y==0],X.loc[:,'V2'][Y==0])
label1 = plt.scatter(X.loc[:,'V1'][Y==1],X.loc[:,'V2'][Y==1])
label2 = plt.scatter(X.loc[:,'V1'][Y==2],X.loc[:,'V2'][Y==2])
plt.title("un-labeld data")
plt.xlabel("V1")
plt.ylabel("V2")
plt.legend((label0,label1,label2),('label0','label1','label2'))
plt.scatter(centers[:,0],centers[:,1])
plt.show()

下图中三个红色的点即为三个中心点,我们初步发现效果还是不错的,接下来我们通过查看准确度来评估我们的模型。
在这里插入图片描述

2.4.4 评估模型

我们对预测后的数据进行一个新的频率显示,对比我们之前提到的原始频率,我们发现预测后的数据并没有做到一一对应。因为我们模型训练时,并没有指定,因此我们需要做一步校正。

y_predict = KM.predict(X)
pd.value_counts(y_predict)

预测前的数据

2    1156
1     954
0     890
Name: labels, dtype: int64

预测后的数据

0    1149
1     952
2     899
dtype: int64

因此,此时的准确度也不高。

from sklearn.metrics import accuracy_score
accuracy = accuracy_score(Y,y_predict)
0.31966666666666665

我们首先将2幅图展示出来,让大家有个直观的感受。

fig3 = plt.subplot(121)
label0 = plt.scatter(X.loc[:,'V1'][Y==0],X.loc[:,'V2'][Y==0])
label1 = plt.scatter(X.loc[:,'V1'][Y==1],X.loc[:,'V2'][Y==1])
label2 = plt.scatter(X.loc[:,'V1'][Y==2],X.loc[:,'V2'][Y==2])
plt.title("un-labeld data")
plt.xlabel("V1")
plt.ylabel("V2")
plt.legend((label0,label1,label2),('label0','label1','label2'))

fig4 = plt.subplot(122)
label0 = plt.scatter(X.loc[:,'V1'][y_predict==0],X.loc[:,'V2'][y_predict==0])
label1 = plt.scatter(X.loc[:,'V1'][y_predict==1],X.loc[:,'V2'][y_predict==1])
label2 = plt.scatter(X.loc[:,'V1'][y_predict==2],X.loc[:,'V2'][y_predict==2])
plt.title("labeld data")
plt.xlabel("V1")
plt.ylabel("V2")
plt.legend((label0,label1,label2),('label0','label1','label2'))

plt.show()

在这里插入图片描述

我们可以发现,虽然聚类的效果不错,但是并没有做到一一对应,同时我们发现了预测的label0对应着原来的label2,预测的label1对应着原来的label1,预测的label2对应着原来的label0.

根据上述结论,我们进行一步校正。

# 进行数据校正
y_corrected = []
for i in y_predict:
    if i==0:
        y_corrected.append(2)
    elif i==1:
        y_corrected.append(1)
    else:
        y_corrected.append(0)

经过校正后,我们发现和之前的频率基本一致了

pd.value_counts(y_corrected)
2    1149
1     952
0     899
dtype: int64

同时我们的准确度也高达了0.997

accuracy_score(y_corrected,Y)
0.997

最后我们将结果可视化出来。

y_corrected = np.array(y_corrected)
fig5 = plt.subplot(121)
label0 = plt.scatter(X.loc[:,'V1'][Y==0],X.loc[:,'V2'][Y==0])
label1 = plt.scatter(X.loc[:,'V1'][Y==1],X.loc[:,'V2'][Y==1])
label2 = plt.scatter(X.loc[:,'V1'][Y==2],X.loc[:,'V2'][Y==2])
plt.title("un-labeld data")
plt.xlabel("V1")
plt.ylabel("V2")
plt.legend((label0,label1,label2),('label0','label1','label2'))

fig6 = plt.subplot(122)
label0 = plt.scatter(X.loc[:,'V1'][y_corrected==0],X.loc[:,'V2'][y_corrected==0])
label1 = plt.scatter(X.loc[:,'V1'][y_corrected==1],X.loc[:,'V2'][y_corrected==1])
label2 = plt.scatter(X.loc[:,'V1'][y_corrected==2],X.loc[:,'V2'][y_corrected==2])
plt.title("labeld data")
plt.xlabel("V1")
plt.ylabel("V2")
plt.legend((label0,label1,label2),('label0','label1','label2'))

plt.show()

在这里插入图片描述

三、常用的其他聚类算法

3.1 均值漂移聚类(Meanshift)

  • 在中心点一定区域检索数据点
  • 更新中心
  • 重复流程到中心点稳定

特点:

  • 自动发现类别数量,不需要人工选择
  • 需要选择区域半径

3.2 DBSCAN算法(基于密度的空间聚类算法)

  • 基于区域点密度筛选有效数据
  • 基于有效数据向周边扩张,直到没有新点加入

特点:

  1. 过滤噪音数据
  2. 不需要人为选择类别数量
  3. 数据密度不同时影响结果

总结

以上python实现部分主要来自慕课网中flare_zhao老师的机器学习课程,有兴趣的可以去学习。
理论知识的学习可以参考李航老师的《统计学习方法》,讲解的很好。

  • 4
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Anonymous&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值