KMeans

理解聚类算法:k-means原理与实践
本文深入解读聚类算法的核心概念,尤其是k-means算法的原理及应用,通过实例演示如何实现k-means算法并展示其迭代过程。

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 每次迭代的具体效果,我们不妨自己来实现一下,代码如下(需要安装SciPymatplotlib) :

 
 
 
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 ,我干脆把它上传上来吧,其实是很容易自己生成的,点击这里下载。


<think>首先,用户的问题是:“我想在Visual Studio中下载kmeans相关资源 请问如何在Visual Studio中下载kmeans”。 从用户的上下文来看,用户引用了几个相关的引用: - [^1]: 提到Kmeans安装报错,需要Microsoft Visual C++ 14.0。 - [^2]: 提到KMeans报错,需要在导入KMeans之前设置环境变量。 - [^3]: 提到VisualStudio2019配置pcl1.9.1,下载链接和相关文件。 - [^4]: 提到KMeans聚类代码和算法描述。 用户说:“上述引用仅供参考,并不是用户真正的需求,请尽可能多的参考用户上一次的问题和你的回答。” 但在这个对话中,这是第一条用户消息,所以没有“上一次的问题和回答”。系统指令中提到参考用户上一次的问题,但这里是初始查询。 系统级指令要求: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如:[^1] 用户的查询是关于在Visual Studio中下载kmeans相关资源。用户提到“下载”,但kmeans是一个算法,通常不是可以直接下载的资源。用户可能指的是安装或使用kmeans库或实现。 从引用中: - [^1] 显示安装kmeans时遇到错误,需要Microsoft Visual C++ 14.0。 - [^2] 显示在导入KMeans之前需要设置环境变量。 - [^3] 提到下载PCL(Point Cloud Library),这可能与kmeans无关,但用户引用它。 - [^4] 描述了kmeans算法。 用户说“在Visual Studio中下载kmeans”,但Visual Studio是一个IDE,并不是一个下载中心。用户可能想安装kmeans库来使用在Visual Studio中。 可能的意图: - 用户想使用kmeans聚类算法在C++或Python项目中。 - 用户可能在Windows上使用Visual Studio作为IDE。 - 从引用[^2]看,用户提到了sklearn(scikit-learn)的KMeans,这是在Python中。 - 引用[^1]提到kmeans安装报错,暗示用户可能在尝试安装一个Python包。 综合来看,用户可能想在Visual Studio环境中设置一个kmeans实现,可能通过Python或C++。 在Visual Studio中,用户可以使用Python扩展,或者C++项目。 常见的kmeans实现: - 在Python中:使用scikit-learn的KMeans。 - 在C++中:可能有各种库,如OpenCV、MLPack等。 从用户的引用: - [^1] 提到kmeans安装报错,涉及lib.c,这可能是一个C扩展,所以可能是一个Python包。 - [^2] 明确提到from sklearn.cluster import KMeans。 所以,用户很可能想使用scikit-learn的KMeans在Python项目中,并使用Visual Studio作为编辑器或IDE。 用户提到“下载kmeans”,但scikit-learn通常通过pip安装,不需要直接从Visual Studio下载。 用户可能在Windows上,Visual Studio可以提供C++构建工具。 回答结构: - 由于系统指令要求回答结构清晰,逐步解决问题。 - 使用中文。 - 生成相关问题。 - 引用相关段落末尾添加引用标识。 关键点: 1. 确定用户需求:在Visual Studio中下载kmeans资源。可能意味着安装库或设置环境。 2. 从引用:安装kmeans可能需要Microsoft Visual C++构建工具,因为[^1]显示错误:"error: Microsoft Visual C++ 14.0 is required." 3. [^2] 提到导入前设置环境变量。 4. [^3] 关于下载PCL,但可能与kmeans无关,除非用户混淆。 步骤回答: 1. 解释kmeans不是直接可下载的资源;通常是一个库或算法实现。 2. 建议在Python中使用scikit-learn的KMeans,因为引用[^2]提到。 3. 在Visual Studio中,可以通过安装Python扩展来使用Python。 4. 安装scikit-learn:使用pip install scikit-learn,但可能需要C++构建工具,如引用[^1]所示。 5. 提供如何安装构建工具的步骤。 6. 如果用户想使用C++,建议相关库。 生成相关问题。 完整回答: - 引言:澄清需求。 - 步骤:如何在Visual Studio中设置kmeans使用。 - 结束:生成相关问题。 引用标识:在引用相关引用时添加[^n]。 例如: - 安装可能需要Microsoft Visual C++,如引用[^1]所述。 - 在导入KMeans之前设置环境变量[^2]。 最终回答应真实可靠。 </think>基于您的查询,您希望在Visual Studio环境中下载或使用KMeans相关的资源。KMeans是一种聚类算法,通常通过第三方库(如Python的scikit-learn或C++的ML库)实现,而不是Visual Studio内置的资源。Visual Studio(VS)是一个集成开发环境(IDE),主要用于开发、调试和运行代码。下载KMeans资源时,您需要根据编程语言(如Python或C++)安装对应的库和依赖项。以下我将逐步指导您完成在Visual Studio中设置KMeans环境的过程,结合您提供的引用内容进行说明。 ### 步骤1: 确认Visual Studio的配置需求 在Windows系统中安装KMeans相关库(尤其是Python库)时,经常需要Microsoft Visual C++构建工具来编译C/C++扩展。您的引用[^1]显示,安装KMeans时报错:"error: Microsoft Visual C++ 14.0 is required",这表明缺少必要的构建工具[^1]。 - **操作建议**: - 如果您尚未安装这些工具,请下载并安装“Build Tools for Visual Studio”。从Microsoft官网下载: [Build Tools下载链接](https://visualstudio.microsoft.com/downloads/)。 - 选择“Visual Studio Build Tools”并安装,确保勾选“C++ build tools”选项(包括MSVC v140或更高版本)。 - **为什么需要这一步?**:许多Python库(如scikit-learn)依赖C++扩展,VS构建工具提供编译器支持编译这些扩展[^1]。 ### 步骤2: 在Visual Studio中设置Python环境(推荐使用scikit-learn的KMeansKMeans最常用的实现是通过Python的scikit-learn库。Visual Studio支持Python开发,您可以使用VS的Python扩展来安装和运行代码。您的引用[^2]提到需要在导入KMeans前设置环境变量以避免报错[^2]。 - **操作步骤**: 1. **安装Python扩展**: - 打开Visual Studio(建议使用VS 2019或更高版本)。 - 转到“扩展” > “管理扩展”,搜索“Python”,安装“Python Development”扩展。 - 重启VS完成安装。 2. **创建Python项目**: - 在VS中,选择“文件” > “新建” > “项目”,然后选择“Python应用程序”。 3. **安装scikit-learn库**: - 在VS的Python环境中,打开“Python环境”窗口(视图 > 其他窗口 > Python环境)。 - 在“包(PyPI)”选项卡中,搜索“scikit-learn”,点击“安装”来安装库。 - 或者,在终端中运行命令: ```bash pip install scikit-learn ``` 4. **设置环境变量以防止报错**: - 在您的Python代码开头添加以下行(如引用[^2]所示),以避免OMP相关错误: ```python import os os.environ['OMP_NUM_THREADS'] = '1' # 限制线程数,防止冲突 import pandas as pd from sklearn.cluster import KMeans ``` - 示例完整代码: ```python import os os.environ['OMP_NUM_THREADS'] = '1' import numpy as np from sklearn.cluster import KMeans # 示例数据:生成随机点 data = np.random.rand(100, 2) # 初始化KMeans(聚类数为3) kmeans = KMeans(n_clusters=3, random_state=0) kmeans.fit(data) print("聚类中心:", kmeans.cluster_centers_) ``` 5. **运行代码**: - 在VS中按F5运行代码。确保选择正确的Python解释器(例如,Python 3.8或更高版本)。 ### 步骤3: 可选C++实现(如果需要) 如果您的项目需要C++版本的KMeans,可以使用库如OpenCV、MLPack或PCL(Point Cloud Library)。您的引用[^3]提到下载PCL文件[^3],但PCL主要用于点云处理,可能不直接适用于标准KMeans;引用[^4]描述了KMeans算法原理[^4]。 - **操作建议**: 1. **安装C++库**: - 对于OpenCV(包含KMeans): - 下载OpenCV: [OpenCV官网](https://opencv.org/releases/),选择Windows版本。 - 在VS中配置:创建C++项目,添加OpenCV的头文件和库路径(教程见官方文档)。 - 对于MLPack(专门用于聚类): - 下载MLPack: [MLPack GitHub](https://github.com/mlpack/mlpack),使用vcpkg安装(VS的包管理器)。 2. **示例C++代码结构**(使用OpenCV): ```cpp #include <opencv2/opencv.hpp> int main() { // 示例数据 cv::Mat data(100, 2, CV_32F); cv::randu(data, 0, 1); // 执行KMeans cv::Mat labels, centers; int clusters = 3; cv::kmeans(data, clusters, labels, cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::MAX_ITER, 100, 0.01), 3, cv::KMEANS_PP_CENTERS, centers); std::cout << "聚类中心: " << centers << std::endl; return 0; } ``` - 在VS中,添加OpenCV依赖:项目属性 > VC++目录 > 包含目录和库目录。 ### 注意事项 - **错误处理**:如果安装时仍报错,确保: - VS构建工具已正确安装[^1]。 - 设置环境变量`OMP_NUM_THREADS=1`以防止多线程问题[^2]。 - 下载的库版本匹配您的VS版本(例如,VS 2019对应MSVC v142)[^3]。 - **资源来源**:KMeans算法本身不需要“下载”,而是通过库实现。引用[^4]解释了KMeans的核心原理:迭代更新聚类中心和样本分类[^4]。 ### 相关问题 1. 如何在Visual Studio中解决Python库安装时的C++编译错误? 2. scikit-learn的KMeans与其他聚类算法(如DBSCAN)相比有什么优缺点? 3. 在C++项目中,如何优化KMeans算法的性能以满足大数据集需求? 如果您提供更多细节(如编程语言或具体错误),我可以提供更针对性的帮助!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值