漫谈 Clustering (1): k-means

K-means聚类模型(超详细,含案例代码) K-means是一种常用的聚类算法,用于将数据集中的观测点分为不同的群组或簇。聚类是一种无监督学习方法,其目标是发现数据中隐藏的结构,将相似的数据点划分为同一组,同时将不相似的数据点划分为不同的组。 阅读详情

转:http://blog.pluskid.org/?p=17

本文是“漫谈 Clustering 系列”中的第 1 篇,参见本系列的其他文章。

好久没有写 blog 了,一来是 blog 下线一段时间,而租 DreamHost 的事情又一直没弄好;二来是没有太多时间,天天都跑去实验室。现在主要折腾 Machine Learning 相关的东西,因为很多东西都不懂,所以平时也找一些资料来看。按照我以前的更新速度的话,这么长时间不写 blog 肯定是要被闷坏的,所以我也觉得还是不定期地整理一下自己了解到的东西,放在 blog 上,一来梳理总是有助于加深理解的,二来也算共享一下知识了。那么,还是从 clustering 说起吧。

cluster_logo

Clustering 中文翻译作“聚类”,简单地说就是把相似的东西分到一组,同 Classification (分类)不同,对于一个 classifier ,通常需要你告诉它“这个东西被分为某某类”这样一些例子,理想情况下,一个 classifier 会从它得到的训练集中进行“学习”,从而具备对未知数据进行分类的能力,这种提供训练数据的过程通常叫做 supervised learning (监督学习),而在聚类的时候,我们并不关心某一类是什么,我们需要实现的目标只是把相似的东西聚到一起,因此,一个聚类算法通常只需要知道如何计算相似 度就可以开始工作了,因此 clustering 通常并不需要使用训练数据进行学习,这在 Machine Learning 中被称作 unsupervised learning (无监督学习)。

举一个简单的例子:现在有一群小学生,你要把他们分成几组,让组内的成员之间尽量相似一些,而组之间则差别大一些。最后分出怎样的结果,就取决于你对于“相似”的定义了,比如,你决定男生和男生是相似的,女生和女生也是相似的,而男生和女生之间则差别很大”,这样,你实际上是用一个可能取两个值“男”和“女”的离散变量来代表了原来的一个小学生,我们通常把这样的变量叫做“特征”。实际上,在这种情况下,所有的小学生都被映射到了两个点的其中一个上,已经很自然地形成了两个组,不需要专门再做聚类了。另一种可能是使用“身高”这个特征。我在读小学候,每周五在操场开会训话的时候会按照大家住的地方的地域和距离远近来列队,这样结束之后就可以结队回家了。除了让事物映射到一个单独的特征之外,一种常见的做法是同时提取 N 种特征,将它们放在一起组成一个 N 维向量,从而得到一个从原始数据集合到 N 维向量空间的映射——你总是需要显式地或者隐式地完成这样一个过程,因为许多机器学习的算法都需要工作在一个向量空间中。

那么让我们再回到 clustering 的问题上,暂且抛开原始数据是什么形式,假设我们已经将其映射到了一个欧几里德空间上,为了方便展示,就使用二维空间吧,如下图所示:

cluster

从数据点的大致形状可以看出它们大致聚为三个 cluster ,其中两个紧凑一些,剩下那个松散一些。我们的目的是为这些数据分组,以便能区分出属于不同的簇的数据,如果按照分组给它们标上不同的颜色,就是这个样子:

cluster

那么计算机要如何来完成这个任务呢?当然,计算机还没有高级到能够“通过形状大致看出来”,不过,对于这样的 N 维欧氏空间中的点进行聚类,有一个非常简单的经典算法,也就是本文标题中提到的 k-means 。在介绍 k-means 的具体步骤之前,让我们先来看看它对于需要进行聚类的数据的一个基本假设吧:对于每一个 cluster ,我们可以选出一个中心点 (center) ,使得该 cluster 中的所有的点到该中心点的距离小于到其他 cluster 的中心的距离。虽然实际情况中得到的数据并不能保证总是满足这样的约束,但这通常已经是我们所能达到的最好的结果,而那些误差通常是固有存在的或者问题本身的不可分性造成的。例如下图所示的两个高斯分布,从两个分布中随机地抽取一些数据点出来,混杂到一起,现在要让你将这些混杂在一起的数据点按照它们被生成的那个分布分开来:

gaussian

由于这两个分布本身有很大一部分重叠在一起了,例如,对于数据点 2.5 来说,它由两个分布产生的概率都是相等的,你所做的只能是一个猜测;稍微好一点的情况是 2 ,通常我们会将它归类为左边的那个分布,因为概率大一些,然而此时它由右边的分布生成的概率仍然是比较大的,我们仍然有不小的几率会猜错。而整个阴影部分是我们所能达到的最小的猜错的概率,这来自于问题本身的不可分性,无法避免。因此,我们将 k-means 所依赖的这个假设看作是合理的。

基于这样一个假设,我们再来导出 k-means 所要优化的目标函数:设我们一共有 N 个数据点需要分为 K 个 cluster ,k-means 要做的就是最小化

\displaystyle J = \sum_{n=1}^N\sum_{k=1}^K r_{nk} \|x_n-\mu_k\|^2

这个函数,其中 r_{nk} 在数据点 n 被归类到 cluster k 的时候为 1 ,否则为 0 。直接寻找 r_{nk} 和 \mu_k 来最小化 J 并不容易,不过我们可以采取迭代的办法:先固定 \mu_k ,选择最优的 r_{nk} ,很容易看出,只要将数据点归类到离他最近的那个中心就能保证 J 最小。下一步则固定 r_{nk},再求最优的 \mu_k。将 J 对 \mu_k 求导并令导数等于零,很容易得到 J 最小的时候 \mu_k 应该满足:

\displaystyle \mu_k=\frac{\sum_n r_{nk}x_n}{\sum_n r_{nk}}

亦即 \mu_k 的值应当是所有 cluster k 中的数据点的平均值。由于每一次迭代都是取到 J 的最小值,因此 J 只会不断地减小(或者不变),而不会增加,这保证了 k-means 最终会到达一个极小值。虽然 k-means 并不能保证总是能得到全局最优解,但是对于这样的问题,像 k-means 这种复杂度的算法,这样的结果已经是很不错的了。

下面我们来总结一下 k-means 算法的具体步骤:

  1. 选定 K 个中心 \mu_k 的初值。这个过程通常是针对具体的问题有一些启发式的选取方法,或者大多数情况下采用随机选取的办法。因为前面说过 k-means 并不能保证全局最优,而是否能收敛到全局最优解其实和初值的选取有很大的关系,所以有时候我们会多次选取初值跑 k-means ,并取其中最好的一次结果。
  2. 将每个数据点归类到离它最近的那个中心点所代表的 cluster 中。
  3. 用公式 \mu_k = \frac{1}{N_k}\sum_{j\in\text{cluster}_k}x_j 计算出每个 cluster 的新的中心点。
  4. 重复第二步,一直到迭代了最大的步数或者前后的 J 的值相差小于一个阈值为止。

按照这个步骤写一个 k-means 实现其实相当容易了,在 SciPy 或者 Matlab 中都已经包含了内置的 k-means 实现,不过为了看看 k-means 每次迭代的具体效果,我们不妨自己来实现一下,代码如下(需要安装 SciPy 和 matplotlib) :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#!/usr/bin/python
 
from __future__ import with_statement
import cPickle as pickle
from matplotlib import pyplot
from numpy import zeros, array, tile
from scipy.linalg import norm
import numpy.matlib as ml
import random
 
def kmeans(X, k, observer=None, threshold=1e-15, maxiter=300):
    N = len(X)
    labels = zeros(N, dtype=int)
    centers = array(random.sample(X, k))
    iter = 0
 
    def calc_J():
        sum = 0
        for i in xrange(N):
            sum += norm(X[i]-centers[labels[i]])
        return sum
 
    def distmat(X, Y):
        n = len(X)
        m = len(Y)
        xx = ml.sum(X*X, axis=1)
        yy = ml.sum(Y*Y, axis=1)
        xy = ml.dot(X, Y.T)
 
        return tile(xx, (m, 1)).T+tile(yy, (n, 1)) - 2*xy
 
    Jprev = calc_J()
    while True:
        # notify the observer
        if observer is not None:
            observer(iter, labels, centers)
 
        # calculate distance from x to each center
        # distance_matrix is only available in scipy newer than 0.7
        # dist = distance_matrix(X, centers)
        dist = distmat(X, centers)
        # assign x to nearst center
        labels = dist.argmin(axis=1)
        # re-calculate each center
        for j in range(k):
            idx_j = (labels == j).nonzero()
            centers[j] = X[idx_j].mean(axis=0)
 
        J = calc_J()
        iter += 1
 
        if Jprev-J < threshold:
            break
        Jprev = J
        if iter >= maxiter:
            break
 
    # final notification
    if observer is not None:
        observer(iter, labels, centers)
 
if __name__ == '__main__':
    # load previously generated points
    with open('cluster.pkl') as inf:
        samples = pickle.load(inf)
    N = 0
    for smp in samples:
        N += len(smp[0])
    X = zeros((N, 2))
    idxfrm = 0
    for i in range(len(samples)):
        idxto = idxfrm + len(samples[i][0])
        X[idxfrm:idxto, 0] = samples[i][0]
        X[idxfrm:idxto, 1] = samples[i][1]
        idxfrm = idxto
 
    def observer(iter, labels, centers):
        print "iter %d." % iter
        colors = array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
        pyplot.plot(hold=False)  # clear previous plot
        pyplot.hold(True)
 
        # draw points
        data_colors=[colors[lbl] for lbl in labels]
        pyplot.scatter(X[:, 0], X[:, 1], c=data_colors, alpha=0.5)
        # draw centers
        pyplot.scatter(centers[:, 0], centers[:, 1], s=200, c=colors)
 
        pyplot.savefig('kmeans/iter_%02d.png' % iter, format='png')
 
    kmeans(X, 3, observer=observer)

代码有些长,不过因为用 Python 来做这个事情确实不如 Matlab 方便,实际的 k-means 的代码只是 41 到 47 行。首先 3 个中心点被随机初始化,所有的数据点都还没有进行聚类,默认全部都标记为红色,如下图所示:

iter_00

然后进入第一次迭代:按照初始的中心点位置为每个数据点着上颜色,这是代码中第 41 到 43 行所做的工作,然后 45 到 47 行重新计算 3 个中心点,结果如下图所示:

iter_01

可以看到,由于初始的中心点是随机选的,这样得出来的结果并不是很好,接下来是下一次迭代的结果:

iter_02

可以看到大致形状已经出来了。再经过两次迭代之后,基本上就收敛了,最终结果如下:

iter_04

不过正如前面所说的那样 k-means 也并不是万能的,虽然许多时候都能收敛到一个比较好的结果,但是也有运气不好的时候会收敛到一个让人不满意的局部最优解,例如选用下面这几个初始中心点:

iter_00_bad

最终会收敛到这样的结果:

iter_03_bad

不得不承认这并不是很好的结果。不过其实大多数情况下 k-means 给出的结果都还是很令人满意的,算是一种简单高效应用广泛的 clustering 方法。

Update 2010.04.25: 很多人都问我要 cluster.pkl ,我干脆把它上传上来吧,其实是很容易自己生成的,点击这里下载。

K-mean均值算法原理讲解和代码实战 最近在学习吴恩达机器学习课程,刚刚学完第一个无监督学习算法,搭配着机器学习实战里面的k-mean实战,成功的将理论和实际结合了起来。接下来,咱们简单的分析算法原理之后,着重讲解一下源代码。 阅读详情

相关推荐

【WebApi + Sqlserver】 使用C#编写WebApi并打开sqlserver数据库进行访问,代码适用于简单练习,真实开发需自行完善

【WebApi + Sqlserver】 使用C#编写WebApi并打开sqlserver数据库进行访问,代码适用于简单练习,真实开发需自行完善

2302_78387246的博客 3592

sklearn入门——聚类算法KMeans

概述 聚类算法是无监督学习的代表算法之一,也叫“无监督分类”。目的是将数据分为若干有意义或有用的组,通常根据实际需求来进行划分。聚类可以用于降维和矢量量化(vectorquantization),可以将高维特征压缩到一列当中,常常用于图像,声音,视频等非结构化数据,可以大幅度压缩数据量。核心是将数据分为多个组,探索每个组的数据是否有联系。 KMeans的工作原理 簇与质心 KMeans将数据划分称若干个无交集的簇,每个簇就是一个一组聚集在一起的数据集,在一个簇中的数据视为同一类,簇是聚类的结果体现。 质心就

weixin_47037146的博客 5047

鸿蒙设备部署轻量级Web服务器NexServer实践

Web服务器作为互联网基础设施的核心组件,其轻量化部署在移动终端领域具有重要实践价值。本文以鸿蒙操作系统为技术载体,深入解析微内核架构如何通过资源隔离和分布式软总线实现服务高可用。重点探讨NexServer这一专为嵌入式场景设计的轻量级服务器,其ARM原生支持特性与15MB级内存占用,在HTTP/2协议处理与WebSocket通信中展现出独特优势。在智能家居控制面板、边缘计算节点等物联网典型场景中,鸿蒙设备结合NexServer的方案相比传统Nginx部署可降低60%资源消耗。通过实测数据对比,该组合在50

weixin_30411239的博客 481

机器学习算法之K-means(K均值聚类)算法

聚类 聚类,简单来说,就是将一个庞杂数据集中具有相似特征的数据自动归类到一起,称为一个簇,簇内的对象越相似,聚类的效果越好。它是一种无监督的学习(Unsupervised Learning)方法,不需要预先标注好的训练集。聚类与分类最大的区别就是分类的目标事先已知,例如猫狗识别,你在分类之前已经预先知道要将它分为猫、狗两个种类;而在你聚类之前,你对你的目标是未知的,同样以动物为例,对于一个动物集来...

tttaeyang的博客 8852

HDR标准核心参数全解析:从PQ/HLG到元数据,一篇搞懂技术选型

本文全面解析HDR标准的核心参数,包括PQ/HLG传输函数和元数据机制,帮助读者理解技术选型的关键因素。通过对比主流HDR标准的技术细节,提供从内容创作到硬件适配的实用指南,助力实现更真实的视觉体验。

weixin_31766003的博客 300

F5高可用HA配置详情

F5 HA详细配置

weixin_45843944的博客 3022

k-means算法

漫谈 Clustering (1): k-means 该文转自:http://blog.pluskid.org/?p=17 by pluskid, on 2008-12-29, in Machine Learning     41 comments 本

bookwormno1的专栏 3918

我每天如何使用 ChatGPT

我偶尔会用这个功能作为开始。我本人非常喜欢阅读,让 ChatGPT 来概括所有文章,感觉就像身处于但丁描述的地狱的某个层次那样痛苦。然而,有时一些文章实在是太啰嗦、无聊,或者写得让人不愉快(比如一个无趣记者对一个让人讨厌的人进行的漫无焦点的采访)。我需要获取这些文章中的一些信息,以便保持信息的更新和了解。在晚期资本主义的广告经济下,很多文章只在一个段落里提供新信息,其余都是无聊的填充内容,只是为了展示糟糕的广告,这实在是对人们的时间和生命的一种浪费。我有几次利用 ChatGPT 来。

KevinDai007的博客 3783

阿里云百炼大模型搭建实战:从API调用到RAG与微调

大模型技术正加速融入各类业务场景,但许多开发者在“搭建自己的大模型”时,常陷入从零预训练与现成模型应用的选择困境。事实上,基于云上大模型平台,通过API调用、Prompt工程、知识库检索增强(RAG)以及必要的微调,即可高效构建稳定可控的大模型应用。本文以阿里云百炼为例,从平台选型、模型选择、接口调用,到知识库挂载、微调与评测,完整梳理了将大模型落地到实际业务的关键路径,帮助工程师和产品同学快速上手,避开常见工程坑点。无论是准备在项目中引入大模型能力,还是希望从理论学习走向实战,都能从中获得可操作的参考。

weixin_33922672的博客 283

C语言实现惯导系统的间接粗对准

间接粗对准方法具有更好的角晃动抗干扰能力,只是在短时间内线晃动抗干扰能力相对弱些,若适当延长粗对准时间(一般在分钟量级),间接粗对准方法往往都能够取得良好的效果。间接粗对准方法是一种非常实用的捷联惯导粗对准方法,得到了广泛的应用。在惯导系统进入工作前,需要进行初始对准,使得惯导系统与当地导航坐标系指向一致,便于惯导系统更好地进入工作状态。博主利用C语言实现了间接粗对准,间接粗对准较之解析粗对准更有优势。以上函数中使用的其他函数块,已在之前或之后的博客中给出。

十八与她的博客 789

Android AMS——AMS初始化(二)

Android AMS 也是一个系统服务,属于系统服务中的引导服务,在 startBootstrapServices() 方法中启动。这里我们就来看一下 ActivityManagerService 的启动流程。

小旭的专栏 774

漫谈 Clustering (1): k-means(转)

转:http://blog.pluskid.org/?p=39 好久没有写 blog 了,一来是 blog 下线一段时间,而租 DreamHost 的事情又一直没弄好;二来是没有太多时间,天天都跑去实验室。现在主要折腾 Machine Learning 相关的东西,因为很多东西都不懂,所以平时也找一些资料来看。按照我以前的更新速度的话,这么长时间不写 blog 肯定是要被闷坏的,所以我也觉得还是...

weixin_33801856的博客 74

WOFOST作物模型(7):6.0.6版本的PCSE使用(多层水分平衡和SNOMIN碳氮模型的集成)

wofost81文件结构如上,其中所需的参数可从github上获取然后input下有两个需要调用的功能函数py文件,需要拖到自己的运行py文件同级目录下在使用土壤模块SNOMIN进行作物生长模型模拟时,需要为土壤剖面的不同层次提供多种输入变量。这些变量通常不随年份变化,因此会统一在土壤文件中定义。本节将说明如何将这些输入变量整合到一个字典中,进而生成SNOMIN能够读取的土壤文件。

所有专家都曾始于新手 637

转贴:漫谈 Clustering (1): k-means_拔剑-浆糊的传说_新浪博客

漫谈 Clustering (1): k-means http://blog.pluskid.org/?p=17 by pluskid, on 2008-12-29, inMachine Learning54 comments 本文是“漫谈 Clustering 系列”中的第 1 篇,参见本系列的其他文章。 好久没有写 blog 了,一来是 blog...

拔剑—浆糊的传说 159

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

上一篇: 漫谈 Clustering (番外篇): Vector Quantization
下一篇: 漫谈 Clustering (2): k-medoids
Savitch
博客等级 码龄16年 84粉丝 130原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值