【机器学习】算法原理详细推导与实现(六):k-means算法

之前几个章节都是介绍有监督学习,这个章节介绍无监督学习,这是一个被称为k-means的聚类算法,也叫做k均值聚类算法

聚类算法

在讲监督学习的时候,通常会画这样一张图:

这时候需要用logistic回归或者SVM将这些数据分成正负两类,这个过程称之为监督学习,是因为对于每一个训练样本都给出了正确的类标签。

在无监督学习中,经常会研究一些不同的问题。假如给定若干个点组成的数据集合:

所有的点都没有像监督学习那样给出类标签和所谓的学习样本,这时候需要依靠算法本身来发现数据中的结构。在上面的这张图中,可以很明显的发现这些数据被分成了两簇,所以一个无监督学习算法将会是聚类算法,算法会将这样的数据聚集成几个不同的类。

聚类算法很多应用场景,举几个最常用的:

  1. 在生物学应用中,经常需要对不同的东西进行聚类,假设有很多基因的数据,你希望对它们进行聚类以便更好的理解不同种类的基因对应的生物功能
  2. 在市场调查中,假设你有一个数据库,里面保存了不同顾客的行为,如果对这些数据进行聚类,可以将市场分为几个不同的部分从而可以对不同的部分指定相应的销售策略
  3. 在图片的应用中,可以将一幅照片分成若干个一致的像素子集,去尝试理解照片的内容
  4. 等等...

聚类的基本思想是:给定一组数据集合,聚集成若干个属性一致的类。

k-means聚类

这个算法被称之为k-means聚类算法,用于寻找数据集合中的类,算法的输入是一个无标记的数据集合\({x^{(1)},x^{(2)},...,x^{(m)}}\),因为这是无监督学习算法,所以在集合中只能看到\(x\),没有类标记\(y\)。k-means聚类算法是将样本聚类成\(k\)个簇(cluster),具体算法步骤如下:

step 1 随机选取k个聚类质心点(cluster centroids),那么就等于存在了\(k\)个簇\(c^{(k)}\):

\[\mu_1,\mu_2,...,\mu_k \in R^n
\]

step 2 对于每一个\(x^{(i)}\),需要计算与每个质心\(\mu_j\)的距离,\(x^{(i)}\)则属于与他距离最近质心\(\mu_j\)的簇\(c^{(j)}\):

\[c^{(j)}:=argmin_j||x^{(i)}-\mu_j||^2,j \in 1,2,...,k
\]

step 3 对于每一个类\(c^{(j)}\),重新计算该簇质心的值:

\[\mu_j:=\frac{\sum^m_{i=1}l\{c^{(i)}=j\}x^{(i)}}{\sum^m_{i=1}l\{c^{(i)}=j\}}
\]

之后需要重复step 2step 3直到算法收敛,下面图中对上述步骤进行解释,存在数据点如下所示:

假设我们取\(k=2\),那么会在数据集合中随机选取两个点作为质心,即下图中红色的点\(\mu_1\)和蓝色的点\(\mu_2\):

分别计算每一个\(x^{(i)}\)和质心\(\mu_1\)、\(\mu_2\)的距离,\(x^{(i)}\)离哪个\(\mu_j\)更近,那么\(x^{(i)}\)就属于哪个\(c^{(j)}\),即哪些点离红色\(\mu_1\)近则属于\(c^{(1)}\),离蓝色近则属于\(c^{(2)}\)。第一次将\(x^{(i)}\)分类后效果如下:

下一步是更新簇\(c^{(j)}\)的质心,计算所有红色点的平均值,得到新的质心\(\mu_{1\_new}\);计算所有蓝色点的平均值,得到新的质心\(\mu_{2\_new}\),如下图所示:

再次重复计算每一个\(x^{(i)}\)和质心的距离,更新质心的值。多次迭代收敛后,即使进行更多次的迭代,\(x^{(i)}\)的类别和质心的值都不会再改变了:

这里涉及到一个问题,如何保证k-means是收敛的?前面算法强调的是结束条件是收敛,为了保证算法完全收敛,这里引入畸变函数(distortion function):

\[J(c,\mu)=\sum^m_{i=1}||x^{(i)}-\mu_{c^{(i)}}||^2
\]

\(J(c,\mu)\)表示每个样本点\(x^{(i)}\)到其质心距离的平方和,当\(J(c,\mu)\)没有达到最小值,可以固定\(c^{(j)}\)更新每个簇的质心\(\mu_j\),质心变化后固定质心的值\(\mu_j\)重新划分簇\(c^{(j)}\)也变化,不断迭代。当\(J(c,\mu)\)达到最小值时,\(\mu_j\)和\(c^{(j)}\)也同时收敛。(这个过程和前面【机器学习】算法原理详细推导与实现(五):支持向量机(下)中的SMO优化算法算法的过程很相似,都是固定一组值或者一个值,更新另外一组或者一个值,使其函数优化到极值,这个过程叫做坐标上升,这里不作推导)。实际上可能会有多组\(\mu\)和\(c\)能够使得\(J(c,\mu)\)取得最小值,但是这种情况并不多见。

由于畸变函数\(J(c,\mu)\)是非凸函数,所以意味着不能保证取的最小值是全局最小值,也就是说k-means对随机取的质心的初始位置比较敏感。一般达到局部最优已经满足分类的需求了,如果比较介意的话,可以多跑几次k-means算法,然后取\(J(c,\mu)\)最小值的\(\mu\)和\(c\)。

k值确定

很多人不知道怎么确定数据集需要分多少个类(簇),因为数据是无监督学习算法,k值需要认为的去设定。所以这里会提供两种方法去确定k值。

第一种方法

观察法,本文的例子可以看出,把数据集画在图中显示,就能很明显的看到应该划分2个类(簇):

并非所有的数据都像上面的数据一样,一眼可以看出来分2个类(簇),所以介绍一般比较常用的第二种方法。

第二种方法

轮廓系数(Silhouette Coefficient),是聚类效果好坏的一种评价方式。最早由 Peter J. Rousseeuw 在 1986 提出。它结合内聚度和分离度两种因素。可以用来在相同原始数据的基础上用来评价不同算法、或者算法不同运行方式对聚类结果所产生的影响。

按照上面计算k-means算法的步骤计算完后,假设\(x^{(i)}\)属于簇\(c^{(i)}\),那么需要计算如下两个值:

  1. \(a(i)\),计算\(x^{(i)}\)与同一簇中其他点的平均距离,代表\(x^{(i)}\)与同一簇中其他点不相似的程度
  2. \(b(i)\),计算\(x^{(i)}\)与另一个与\(x^{(i)}\)最近的簇\(c\),与簇\(c\)内所有点平均距离,代表\(x^{(i)}\)与最相邻簇\(c\)的不相似程度

最终轮廓系数的计算公式为:

\[S(i)=\frac{b(i)-a(i)}{max(a(i),b(i))}
\]

轮廓系数的范围为\([-1, 1]\),越趋近于1代表内聚度和分离度都相对较优。

所以可以在k-means算法开始的时候,先设置k值的范围\(k \in [2, n]\),从而计算k取每一个值的轮廓系数,轮廓系数最小的那个k值就是最优的分类总数。

实例

假设存在数据集为如下样式:

import matplotlib.pyplot as plt
import numpy as np
from sklearn.cluster import KMeans
from sklearn import metrics # figsize绘图的宽度和高度,也就是像素
plt.figure(figsize=(8, 10))
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)
# print(X) # x,y轴的绘图范围
plt.xlim([0, 10])
plt.ylim([0, 10])
plt.title('sample')
plt.scatter(x1, x2)
# 点的颜色
colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k', 'b']
# 点的形状
markers = ['o', 's', 'D', 'v', '^', 'p', '*', '+']
plt.show()

虽然观察法可以知道这个数据集合只要设置\(k=3\)就好了,但是这里还是想用轮廓系数来搜索最佳的k值。假设不知道的情况下,这里取\(k \in [2, 3, 4, 5, 8]\):

# 测试的k值
tests = [2, 3, 4, 5, 8]
subplot_counter = 1
for t in tests:
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')
# 每个点对应的标签值
# print(kmeans_model.labels_)
plt.xlim([0, 10])
plt.ylim([0, 10])
plt.title('K = %s, Silhouette Coefficient = %.03f' % (t, metrics.silhouette_score(X, kmeans_model.labels_, metric='euclidean')))

得到的结果为:

可以看到当\(k=3\)时轮廓系数最大为0.722

数据和代码下载请关注公众号【 机器学习和大数据挖掘 】,后台回复【 机器学习 】即可获取

【机器学习】算法原理详细推导与实现(六):k-means算法的更多相关文章

  1. 经典算法题每日演练——第十六题 Kruskal算法

    原文:经典算法题每日演练--第十六题 Kruskal算法 这篇我们看看第二种生成树的Kruskal算法,这个算法的魅力在于我们可以打一下算法和数据结构的组合拳,很有意思的. 一:思想 若存在M={0, ...

  2. 分布式系列文章——Paxos算法原理与推导

    Paxos算法在分布式领域具有非常重要的地位.但是Paxos算法有两个比较明显的缺点:1.难以理解 2.工程实现更难. 网上有很多讲解Paxos算法的文章,但是质量参差不齐.看了很多关于Paxos的资 ...

  3. 【转载】分布式系列文章——Paxos算法原理与推导

    转载:http://linbingdong.com/2017/04/17/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E5%88%97%E6%96%87%E7%AB%A0 ...

  4. 强化学习-学习笔记7 | Sarsa算法原理与推导

    Sarsa算法 是 TD算法的一种,之前没有严谨推导过 TD 算法,这一篇就来从数学的角度推导一下 Sarsa 算法.注意,这部分属于 TD算法的延申. 7. Sarsa算法 7.1 推导 TD ta ...

  5. 机器学习实战 - python3 学习笔记(一) - k近邻算法

    一. 使用k近邻算法改进约会网站的配对效果 k-近邻算法的一般流程: 收集数据:可以使用爬虫进行数据的收集,也可以使用第三方提供的免费或收费的数据.一般来讲,数据放在txt文本文件中,按照一定的格式进 ...

  6. 多层神经网络BP算法 原理及推导

    首先什么是人工神经网络?简单来说就是将单个感知器作为一个神经网络节点,然后用此类节点组成一个层次网络结构,我们称此网络即为人工神经网络(本人自己的理解).当网络的层次大于等于3层(输入层+隐藏层(大于 ...

  7. AdaBoost 算法原理及推导

    AdaBoost(Adaptive Boosting):自适应提升方法. 1.AdaBoost算法介绍 AdaBoost是Boosting方法中最优代表性的提升算法.该方法通过在每轮降低分对样例的权重 ...

  8. 《机器学习实战》中的程序清单2-1 k近邻算法(kNN)classify0都做了什么

    from numpy import * import operator import matplotlib import matplotlib.pyplot as plt from imp impor ...

  9. KNN 与 K - Means 算法比较

    KNN K-Means 1.分类算法 聚类算法 2.监督学习 非监督学习 3.数据类型:喂给它的数据集是带label的数据,已经是完全正确的数据 喂给它的数据集是无label的数据,是杂乱无章的,经过 ...

随机推荐

  1. Java ArrayList类的简单介绍

    ArrayList类的说明: ArrayList类是List接口的实现类,java.util.ArrayList集合数据存储的结构是数组结构. 特点: 元素增删慢,查找快.(由于日常开发中使用最多的功 ...

  2. JS中Cookie、localStorage、sessionStorage三者的区别

    cookie:大小4k,一般由服务器生成,可设置失效时间,关闭浏览器后失效,与服务器通信时:每次都会携带HTTP头中,如果使用cookie保存过多数据会带来性能问题 localhostStorage: ...

  3. ST表竞赛模板

    void RMQ_init(){//ST表的创建模板 ;i<n;i++) d[i][]=mo[i]; ;(<<j)<=n;j++) ;i+(<<j)-<n;i ...

  4. 分布式唯一ID自增(雪花算法)

    public class IdWorker { // ==============================Fields===================================== ...

  5. 【javaScript】js出现allocation size overflow以及字符串拼接优化

    字符串拼接长一点了,就出现了allocation size overflow异常! 先创建缓冲字符串数组,最后将数组转化为字符串 <script type="text/javascri ...

  6. Linux起源

    Linux起源 操作系统出现时间线: Unix1970年诞生 ,71年用C语言重写 Apple II 诞生于1976年 window诞生于1985年 Linux诞生于1991年,由大学生Linus T ...

  7. python3小脚本-监控服务器性能并插入mysql数据库

    操作系统: centos版本 7.4 防火墙 关闭 selinux 关闭 python版本 3.6 mysql版本 5.7 #操作系统性能脚本 [root@localhost sql]# cat cp ...

  8. Frameworks.Entity.Core 6 Specification

    Specification internal 1 A logic AND Specification密封类AndSpecification<T>继承 抽象类CompositeSpecifi ...

  9. 团队项目——Alpha1版本

    团队项目-Alpha版本发布1 一.格式描述 这个作业属于哪个课程 https://edu.cnblogs.com/campus/xnsy/GeographicInformationScience/ ...

  10. JS DOM中Ajax的使用

    一.概念 全称:Asynchronors Javascript XML  异步JS数据交换格式. [Asynchronous]:异步的,即在执⾏ AJAX 请求时不会阻塞后⾯代码的运⾏.[JavaSc ...