【机器学习】算法原理详细推导与实现(六):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. cogs 176. [USACO Feb07] 奶牛聚会 dijkstra

    176. [USACO Feb07] 奶牛聚会 ★☆   输入文件:sparty.in   输出文件:sparty.out   简单对比时间限制:3 s   内存限制:16 MB 译: zqzas N ...

  2. springcloud之断路器(Hystrix)

    在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign来调用.为了保证其高可用,单个服务 ...

  3. 安装dbeaver,The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone.

    在连接mysql时,出现了以下错误: 解决方法是 在数据库链接指定useUnicode=true&useSSL=false&characterEncoding=utf8&ser ...

  4. Redis常用命令详细介绍

    一.字符串 字符串键是Redis最基本的键值对类型,将一个单独的键和一个单独的值关联起来.通过字符串键,不仅可以存储和读取字符串,如果输入能被解释为整数和浮点数,还能执行自增或自减操作. 1.SET: ...

  5. [bzoj1297] [洛谷P4159] [SCOI2009] 迷路

    Description windy在有向图中迷路了. 该有向图有 N 个节点,windy从节点 0 出发,他必须恰好在 T 时刻到达节点 N-1. 现在给出该有向图,你能告诉windy总共有多少种不同 ...

  6. pikachu漏洞练习之sql注入

    这里因为实验的时候只记录了一部分所以就展示一部分 1.1.1数字型注入 (1)看到界面发现是查询id功能,没有在url里看到有传参所以应该是post方法提交数据. (2)进行sql注入之前我们最好是先 ...

  7. RocketMQ 实战之快速入门

    原文地址:https://www.jianshu.com/p/824066d70da8 最近 RocketMQ 刚刚上生产环境,闲暇之时在这里做一些分享,主要目的是让初学者能快速上手RocketMQ. ...

  8. selenium常见的元素定位方法

    一.获取元素 1)通过谷歌浏览器自动的工具访问百度首页,我们可以看到,页面上的元素都是由一行行的代码组成的,它们之间有层级地组织起来,每个元素之间都有不同的标签和值,我们可以通过这些不同的标签和值来找 ...

  9. 个人作业四——Alpha测试

    个人作业四--Alpha测试 这个作业属于哪个课程 软件工程 这个作业要求在哪里 作业要求 团队名称 GP工作室 这个作业的目标 对其他小组的项目进行测试 测试人员 许佳文 学号 2017310242 ...

  10. openpyxl库实现对excel文档进行编辑(追加写入)

    首先,这个库只支持xlsx格式的excel文件 预期,对”excel_test.xlsx“的A1单元格写入”hello word“ 1.安装”openpyxl“库,pip install openpy ...