机器学习的一种方法,没有给定事先标记的训练实例,自动对输入的数据进行分类或分群。
优点:
主要应用:
特点:
K-均值聚类:以空间中k个点为中心进行聚类,对最靠近他们的对象归类,是聚类算法中最为基础但也最为重要的算法。
k均值聚类是基于样本集合划分的聚类算法。k均值聚类将样本集合划分为k个子集,构成k个类,将n个样本分到k个类中,每个样本到期所属类的中心的距离最小。每个样本只能属于一个类,所以k均值聚类是硬聚类。下面分别介绍k均值聚类的模型、策略、算法。
大家可以直接查看概览和2.3中的案例来对k均值聚类有个直观的理解。
给定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
划分C是一个多对一的函数。k均值聚类的模型是一个从样本到类的函数。
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=1∑m(xki−xkj)2=∣∣xi−xj∣∣2
然后定义样本与其所属类的中心之间的距离的总和为损失函数,即
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=1∑kc(i)=1∑∣∣xi−xlˉ∣∣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=1∑kC(i)=1∑∣∣xi−xj∣∣2
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=1∑k(−1)k−1(kl)kn
这个问题是指数级的。事实上,k均值聚类的最优解求解问题是NP困难问题。现实中采用迭代的方法求解。
k均值聚类的算法是一个迭代的过程,每一次迭代包括2个步骤。首先选择k个类的中心,将样本逐个指派到与其最近的中心的类中,得到一个聚类结果;然后更新每个类的样本的均值,作为类的新中新;重复以上步骤,直到收敛为止。
算法叙述:
输入:n个样本的集合X;
输出:样本集合的聚类 C ∗ C^* C∗。
k均值聚类算法的复杂度为 O ( m n k ) , O(mnk), O(mnk),其中m是样本维数,n是样本个数,k是类别个数
给定含有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个类中。
所以分别把 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)} x3放到G2(0),x4放到G2(0),x5放到G1(0)中。
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
为了判断模型的预测结果,我们事先给数据集提供了分类信息。
代码和数据集我都已经上传到GitHub中,有需要可以前往下载。访问不了可以私信找我。
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
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()
其中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()
下图中三个红色的点即为三个中心点,我们初步发现效果还是不错的,接下来我们通过查看准确度来评估我们的模型。
我们对预测后的数据进行一个新的频率显示,对比我们之前提到的原始频率,我们发现预测后的数据并没有做到一一对应。因为我们模型训练时,并没有指定,因此我们需要做一步校正。
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()
特点:
特点:
以上python实现部分主要来自慕课网中flare_zhao老师的机器学习课程,有兴趣的可以去学习。
理论知识的学习可以参考李航老师的《统计学习方法》,讲解的很好。