K-Means 原理

聚类分析是在没有给定划分类别情况下,根据数据相似度进行样本分组的一种方法。是一种非监督的学习算法。划分依据主要是自身的距离或相似度将他们划分为若干组,划分原则是组内样本最小化而组间(外部)距离最大化。

下载.jpeg

顺便说明一下分类和聚类的区别。分类是从特定的数据中挖掘模式,作出分类判断。聚类是根据数据本身特点,按照不同的模型来判断数据之间的相似性、相似性高的一组数据聚成一簇。

聚类分析中,最常用的算法是 K-Means 。K-Means 是基于距离的聚类方法,在最小误差函数的基础上将数据划分为预定的类数 K,采用距离作为相似性的评价指标,认为两个对象的距离越近,其相似度越高。适用于连续型数据。所以,对于 K-Means 的 K 点选择,是至关重要的,K点的选取一般是凭业务经验和一些取多个值交叉验证。

适用场景常用于用户分群分析,产品的销量等数据聚类分析。但是聚类无法提供明确的行动指向,聚类结果更多是为后期挖掘和分析工作提供预处理和参考,无法回答“为什么”和“怎么办”的问题,更无法为用户提供明确的解决问题的规则和条件(例如决策树条件或关联规则)。因此,聚类分析无法真正“解决”问题。

算法过程

  1. 从n个样本数据中随机选取k个对象作为初始的聚类中心;
  2. 分别计算每个样本到各个聚类中心的距离,将对象分配到距离最近的聚类中;
  3. 所有对象分配完成后,重新计算k个聚类的中心(类似重新计算虚拟中心);
  4. 与前一次计算得到的 K 个聚类中心比较,如果聚类中心发生变化,转至步骤 2,否则转至步骤 5;
  5. 当质心不发生变化时,停止并输出聚类结果。

度量距离:度量样本之间的相似性最常用的是欧几里得距离、曼哈顿距离和闵可夫斯基距离(Python 中目前支持欧氏距离)
**
伪代码如图所示
下载.png

参数说明

使用 sklearn 中的 K-Means 算法来实现具体功能。当然 K-Means 只是 sklearn.cluster 中的一个聚类库,实际上包括 K-Means 在内,sklearn.cluster 一共提供了 9 种聚类方法,比如 Mean-shift,DBSCAN,Spectral clustering(谱聚类)等。这些聚类方法的原理和 K-Means 不同,后续再详细说明。

1
2
from sklearn.cluster import KMeans
KMeans(n_clusters=8, init='k-means++', n_init=10, max_iter=300, tol=0.0001, precompute_distances='auto', verbose=0, random_state=None, copy_x=True, n_jobs=1, algorithm='auto')

具体参数说明

  • n_clusters: 即 K 值,一般需要多试一些 K 值来保证更好的聚类效果。可以随机设置一些 K 值,然后选择聚类效果最好的作为最终的 K 值;
  • max_iter: 最大迭代次数,如果聚类很难收敛的话,设置最大迭代次数可以及时得到反馈结果,否则程序运行时间会非常长;
  • n_init:初始化中心点的运算次数,默认是 10。程序是否能快速收敛和中心点的选择关系非常大,所以在中心点选择上多花一些时间,来争取整体时间上的快速收敛还是非常值得的。由于每一次中心点都是随机生成的,这样得到的结果就有好有坏,非常不确定,所以要运行 n_init 次, 取其中最好的作为初始的中心点。如果 K 值比较大的时候,可以适当增大 n_init 这个值;
  • init: 即初始值选择的方式,默认是采用优化过的 k-means++ 方式,也可以自己指定中心点,或者采用 random 完全随机的方式。自己设置中心点一般是对于个性化的数据进行设置,很少采用。random 的方式则是完全随机的方式,一般推荐采用优化过的 k-means++ 方式;
  • algorithm:k-means 的实现算法,有“auto” “full”“elkan”三种。一般来说建议直接用默认的”auto”。简单说下这三个取值的区别,如果选择”full”采用的是传统的 K-Means 算法,“auto”会根据数据的特点自动选择是选择“full”还是“elkan”。一般选择默认的取值,即“auto” 。

案例

下面一个针对航空公司客户用 K-Means 进行 RFM 分群案例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import pandas as pd

data = pd.read_csv('/data/consumption_data.csv')
from sklearn.cluster import KMeans

k = 3 #聚类的类别
iteration = 500 #聚类最大循环次数
data_zs = 1.0*(data - data.mean())/data.std() #数据标准化

model = KMeans(n_clusters = k, n_jobs = 4, max_iter = iteration) #分为k类,并发数4
model.fit(data_zs) #开始聚类

#简单打印结果
r1 = pd.Series(model.labels_).value_counts() #统计各个类别的数目
r2 = pd.DataFrame(model.cluster_centers_) #找出聚类中心
r = pd.concat([r2, r1], axis = 1) #横向连接(0是纵向),得到聚类中心对应的类别下的数目
r.columns = list(data.columns) + ['类别数目'] #重命名表头

r = pd.concat([data, pd.Series(model.labels_, index = data.index)], axis = 1) #详细输出每个样本对应的类别
r.columns = list(data.columns) + [u'聚类类别'] #重命名表头

r.head()

image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from sklearn.manifold import TSNE

tsne = TSNE()
tsne.fit_transform(data_zs) #进行数据降维
tsne = pd.DataFrame(tsne.embedding_, index = data_zs.index) #转换数据格式

import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来正常显示负号

#不同类别用不同颜色和样式绘图
d = tsne[r[u'聚类类别'] == 0]
plt.plot(d[0], d[1], 'r.')
d1 = tsne[r[u'聚类类别'] == 1]
plt.plot(d1[0], d1[1], 'go')
d2 = tsne[r[u'聚类类别'] == 2]
plt.plot(d2[0], d2[1], 'b*')
plt.show()

下载 (1).png