K-Means 概念定义:

K-Means 是一种基于距离的排他的聚类划分方法。

上面的 K-Means 描述中包含了几个概念:

  • 聚类(Clustering):K-Means 是一种聚类分析(Cluster Analysis)方法。聚类就是将数据对象分组成为多个类或者簇 (Cluster),使得在同一个簇中的对象之间具有较高的相似度,而不同簇中的对象差别较大。
  • 划分(Partitioning):聚类可以基于划分,也可以基于分层。划分即将对象划分成不同的簇,而分层是将对象分等级。
  • 排他(Exclusive):对于一个数据对象,只能被划分到一个簇中。如果一个数据对象可以被划分到多个簇中,则称为可重叠的(Overlapping)。
  • 距离(Distance):基于距离的聚类是将距离近的相似的对象聚在一起。基于概率分布模型的聚类是在一组对象中,找到能符合特定分布模型的对象的集合,他们不一定是距离最近的或者最相似的,而是能完美的呈现出概率分布模型所描述的模型。

K-Means 问题描述:

给定一个 n 个对象的数据集,它可以构建数据的 k 个划分,每个划分就是一个簇,并且 k ≤ n。同时还需满足:

  1. 每个组至少包含一个对象。
  2. 每个对象必须属于且仅属于一个簇。

Simply speaking, K-Means clustering is an algorithm to classify or to group your objects based on attributes/features, into K number of groups. K is a positive integer number. The grouping is done by minimizing the sum of squares of distances between data and the corresponding cluster centroid. Thus, the purpose of K-means clustering is to classify the data.

例如,有如下包含 10 条数据的集合。集合中每项描述了一个人的身高(Height: inches)和体重(Weight: kilograms)。

Height Weight
-------------
(73.0, 72.6)
(61.0, 54.4)
(67.0, 99.9)
(68.0, 97.3)
(62.0, 59.0)
(75.0, 81.6)
(74.0, 77.1)
(66.0, 97.3)
(68.0, 93.3)
(61.0, 59.0)

通过按照身高和体重的聚类,可以将上述 10 条数据分组成 3 类。

Height Weight
-------------
(67.0, 99.9)
(68.0, 97.3)
(66.0, 97.3)
(68.0, 93.3) (73.0, 72.6)
(75.0, 81.6)
(74.0, 77.1) (61.0, 54.4)
(62.0, 59.0)
(61.0, 59.0)

分类结果可以描述为:中等身高并且很重、很高并且中等体重、矮并且轻。如果用图形来观察分组状况则结果一目了然。

K-Means 算法实现:

由于 K-Means 算法值针对给定的完整数据集进行操作,不需要任何特殊的训练数据,所以 K-Means 是一种无监督的机器学习方法(Unsupervised Machine Learning Technique)。

K-Means 算法最常见的实现方式是使用迭代式精化启发法的 Lloyd's algorithm

  • 给定划分数量 k。创建一个初始划分,从数据集中随机地选择 k 个对象,每个对象初始地代表了一个簇中心(Cluster Centroid)。对于其他对象,计算其与各个簇中心的距离,将它们划入距离最近的簇。
  • 采用迭代的重定位技术,尝试通过对象在划分间移动来改进划分。所谓重定位技术,就是当有新的对象加入簇或者已有对象离开簇的时候,重新计算簇的平均值,然后对对象进行重新分配。这个过程不断重复,直到各簇中对象不再变化为止。
randomly assign all data items to a cluster
loop until no change in cluster assignments
compute centroids for each cluster
reassign each data item to cluster of closest centroid
end

简洁点儿的表述即为:

initialize clustering
loop
update centroids
update clustering
end loop

应用 K-Means 算法到上述身高与体重的示例,聚类过程如下图所示。

K-Means 优缺点:

当结果簇是密集的,而且簇和簇之间的区别比较明显时,K-Means 的效果较好。对于大数据集,K-Means 是相对可伸缩的和高效的,它的复杂度是 O(nkt),n 是对象的个数,k 是簇的数目,t 是迭代的次数,通常 k << n,且 t << n,所以算法经常以局部最优结束。

K-Means 的最大问题是要求先给出 k 的个数。k 的选择一般基于经验值和多次实验结果,对于不同的数据集,k 的取值没有可借鉴性。另外,K-Means 对孤立点数据是敏感的,少量噪声数据就能对平均值造成极大的影响。

Basic K-Means - Lloyd's algorithm C# 代码实现:

Code below referenced from Machine Learning Using C# Succinctly by James McCaffrey, and article K-Means Data Clustering Using C#.

 using System;

 namespace ClusterNumeric
{
class ClusterNumProgram
{
static void Main(string[] args)
{
Console.WriteLine("\nBegin k-means clustering demo\n"); double[][] rawData = new double[][];
rawData[] = new double[] { , 72.6 };
rawData[] = new double[] { , 54.4 };
rawData[] = new double[] { , 99.9 };
rawData[] = new double[] { , 97.3 };
rawData[] = new double[] { , 59.0 };
rawData[] = new double[] { , 81.6 };
rawData[] = new double[] { , 77.1 };
rawData[] = new double[] { , 97.3 };
rawData[] = new double[] { , 93.3 };
rawData[] = new double[] { , 59.0 }; Console.WriteLine("Raw unclustered height (in.) weight (kg.) data:\n");
Console.WriteLine(" ID Height Weight");
Console.WriteLine("---------------------");
ShowData(rawData, , true, true); int numClusters = ;
Console.WriteLine("\nSetting numClusters to " + numClusters); Console.WriteLine("Starting clustering using k-means algorithm");
Clusterer c = new Clusterer(numClusters);
int[] clustering = c.Cluster(rawData);
Console.WriteLine("Clustering complete\n"); Console.WriteLine("Final clustering in internal form:\n");
ShowVector(clustering, true); Console.WriteLine("Raw data by cluster:\n");
Console.WriteLine(" ID Height Weight");
ShowClustered(rawData, clustering, numClusters, ); Console.WriteLine("\nEnd k-means clustering demo\n");
Console.ReadLine();
} static void ShowData(
double[][] data, int decimals,
bool indices, bool newLine)
{
for (int i = ; i < data.Length; ++i)
{
if (indices == true)
Console.Write(i.ToString().PadLeft() + " "); for (int j = ; j < data[i].Length; ++j)
{
double v = data[i][j];
Console.Write(v.ToString("F" + decimals) + " ");
} Console.WriteLine("");
} if (newLine == true)
Console.WriteLine("");
} static void ShowVector(int[] vector, bool newLine)
{
for (int i = ; i < vector.Length; ++i)
Console.Write(vector[i] + " "); if (newLine == true)
Console.WriteLine("\n");
} static void ShowClustered(
double[][] data, int[] clustering,
int numClusters, int decimals)
{
for (int k = ; k < numClusters; ++k)
{
Console.WriteLine("===================");
for (int i = ; i < data.Length; ++i)
{
int clusterID = clustering[i];
if (clusterID != k) continue;
Console.Write(i.ToString().PadLeft() + " ");
for (int j = ; j < data[i].Length; ++j)
{
double v = data[i][j];
Console.Write(v.ToString("F" + decimals) + " ");
}
Console.WriteLine("");
}
Console.WriteLine("===================");
}
}
} public class Clusterer
{
private int numClusters; // number of clusters
private int[] clustering; // index = a tuple, value = cluster ID
private double[][] centroids; // mean (vector) of each cluster
private Random rnd; // for initialization public Clusterer(int numClusters)
{
this.numClusters = numClusters;
this.centroids = new double[numClusters][];
this.rnd = new Random(); // arbitrary seed
} public int[] Cluster(double[][] data)
{
int numTuples = data.Length;
int numValues = data[].Length;
this.clustering = new int[numTuples]; for (int k = ; k < numClusters; ++k) // allocate each centroid
this.centroids[k] = new double[numValues]; InitRandom(data); Console.WriteLine("\nInitial random clustering:");
for (int i = ; i < clustering.Length; ++i)
Console.Write(clustering[i] + " ");
Console.WriteLine("\n"); bool changed = true; // change in clustering?
int maxCount = numTuples * ; // sanity check
int ct = ;
while (changed == true && ct <= maxCount)
{
++ct; // k-means typically converges very quickly
UpdateCentroids(data); // no effect if fail
changed = UpdateClustering(data); // no effect if fail
} int[] result = new int[numTuples];
Array.Copy(this.clustering, result, clustering.Length);
return result;
} private void InitRandom(double[][] data)
{
int numTuples = data.Length; int clusterID = ;
for (int i = ; i < numTuples; ++i)
{
clustering[i] = clusterID++;
if (clusterID == numClusters)
clusterID = ;
}
for (int i = ; i < numTuples; ++i)
{
int r = rnd.Next(i, clustering.Length);
int tmp = clustering[r];
clustering[r] = clustering[i];
clustering[i] = tmp;
}
} private void UpdateCentroids(double[][] data)
{
int[] clusterCounts = new int[numClusters];
for (int i = ; i < data.Length; ++i)
{
int clusterID = clustering[i];
++clusterCounts[clusterID];
} // zero-out this.centroids so it can be used as scratch
for (int k = ; k < centroids.Length; ++k)
for (int j = ; j < centroids[k].Length; ++j)
centroids[k][j] = 0.0; for (int i = ; i < data.Length; ++i)
{
int clusterID = clustering[i];
for (int j = ; j < data[i].Length; ++j)
centroids[clusterID][j] += data[i][j]; // accumulate sum
} for (int k = ; k < centroids.Length; ++k)
for (int j = ; j < centroids[k].Length; ++j)
centroids[k][j] /= clusterCounts[k]; // danger?
} private bool UpdateClustering(double[][] data)
{
// (re)assign each tuple to a cluster (closest centroid)
// returns false if no tuple assignments change OR
// if the reassignment would result in a clustering where
// one or more clusters have no tuples. bool changed = false; // did any tuple change cluster? int[] newClustering = new int[clustering.Length]; // proposed result
Array.Copy(clustering, newClustering, clustering.Length); double[] distances = new double[numClusters]; // from tuple to centroids for (int i = ; i < data.Length; ++i) // walk through each tuple
{
for (int k = ; k < numClusters; ++k)
distances[k] = Distance(data[i], centroids[k]); int newClusterID = MinIndex(distances); // find closest centroid
if (newClusterID != newClustering[i])
{
changed = true; // note a new clustering
newClustering[i] = newClusterID; // accept update
}
} if (changed == false)
return false; // no change so bail // check proposed clustering cluster counts
int[] clusterCounts = new int[numClusters];
for (int i = ; i < data.Length; ++i)
{
int clusterID = newClustering[i];
++clusterCounts[clusterID];
} for (int k = ; k < numClusters; ++k)
if (clusterCounts[k] == )
return false; // bad clustering Array.Copy(newClustering, clustering, newClustering.Length); // update
return true; // good clustering and at least one change
} // Euclidean distance between two vectors for UpdateClustering()
private static double Distance(double[] tuple, double[] centroid)
{
double sumSquaredDiffs = 0.0;
for (int j = ; j < tuple.Length; ++j)
sumSquaredDiffs += (tuple[j] - centroid[j]) * (tuple[j] - centroid[j]);
return Math.Sqrt(sumSquaredDiffs);
} // helper for UpdateClustering() to find closest centroid
private static int MinIndex(double[] distances)
{
int indexOfMin = ;
double smallDist = distances[];
for (int k = ; k < distances.Length; ++k)
{
if (distances[k] < smallDist)
{
smallDist = distances[k];
indexOfMin = k;
}
}
return indexOfMin;
}
}
}

运行结果如下:

参考资料

本篇文章《K-Means 聚类算法》由 Dennis Gao 发表自博客园个人博客,未经作者本人同意禁止以任何的形式转载,任何自动的或人为的爬虫转载行为均为耍流氓。

K-Means 聚类算法的更多相关文章

  1. k均值聚类算法原理和(TensorFlow)实现

    顾名思义,k均值聚类是一种对数据进行聚类的技术,即将数据分割成指定数量的几个类,揭示数据的内在性质及规律. 我们知道,在机器学习中,有三种不同的学习模式:监督学习.无监督学习和强化学习: 监督学习,也 ...

  2. K均值聚类算法

    k均值聚类算法(k-means clustering algorithm)是一种迭代求解的聚类分析算法,其步骤是随机选取K个对象作为初始的聚类中心,然后计算每个对象与各个种子聚类中心之间的距离,把每个 ...

  3. 机器学习实战---K均值聚类算法

    一:一般K均值聚类算法实现 (一)导入数据 import numpy as np import matplotlib.pyplot as plt def loadDataSet(filename): ...

  4. 基于改进人工蜂群算法的K均值聚类算法(附MATLAB版源代码)

    其实一直以来也没有准备在园子里发这样的文章,相对来说,算法改进放在园子里还是会稍稍显得格格不入.但是最近邮箱收到的几封邮件让我觉得有必要通过我的博客把过去做过的东西分享出去更给更多需要的人.从论文刊登 ...

  5. K均值聚类算法的MATLAB实现

    1.K-均值聚类法的概述    之前在参加数学建模的过程中用到过这种聚类方法,但是当时只是简单知道了在matlab中如何调用工具箱进行聚类,并不是特别清楚它的原理.最近因为在学模式识别,又重新接触了这 ...

  6. 聚类之K均值聚类和EM算法

    这篇博客整理K均值聚类的内容,包括: 1.K均值聚类的原理: 2.初始类中心的选择和类别数K的确定: 3.K均值聚类和EM算法.高斯混合模型的关系. 一.K均值聚类的原理 K均值聚类(K-means) ...

  7. [聚类算法] K-means 算法

    聚类 和 k-means简单概括. 聚类是一种 无监督学习 问题,它的目标就是基于 相似度 将相似的子集聚合在一起. k-means算法是聚类分析中使用最广泛的算法之一.它把n个对象根据它们的属性分为 ...

  8. 转载: scikit-learn学习之K-means聚类算法与 Mini Batch K-Means算法

    版权声明:<—— 本文为作者呕心沥血打造,若要转载,请注明出处@http://blog.csdn.net/gamer_gyt <—— 目录(?)[+] ================== ...

  9. 转载 | Python AI 教学│k-means聚类算法及应用

    关注我们的公众号哦!获取更多精彩哦! 1.问题导入 假如有这样一种情况,在一天你想去某个城市旅游,这个城市里你想去的有70个地方,现在你只有每一个地方的地址,这个地址列表很长,有70个位置.事先肯定要 ...

  10. FCM聚类算法介绍

    FCM算法是一种基于划分的聚类算法,它的思想就是使得被划分到同一簇的对象之间相似度最大,而不同簇之间的相似度最小.模糊C均值算法是普通C均值算法的改进,普通C均值算法对于数据的划分是硬性的,而FCM则 ...

随机推荐

  1. 完美判断iframe是否加载完成

    var iframe = document.createElement("iframe"); iframe.style.width = "265px"; ifr ...

  2. “.Net 社区虚拟大会”(dotnetConf) 2016 Day 3 Keynote: Scott Hanselman

    美国时间 6月7日--9日,为期三天的微软.NET社区虚拟大会正式在 Channel9 上召开,美国时间6.9 是第三天, Scott Hanselman 做Keynote.今天主题围绕的是.NET ...

  3. .NET Core中间件的注册和管道的构建(2)---- 用UseMiddleware扩展方法注册中间件类

    .NET Core中间件的注册和管道的构建(2)---- 用UseMiddleware扩展方法注册中间件类 0x00 为什么要引入扩展方法 有的中间件功能比较简单,有的则比较复杂,并且依赖其它组件.除 ...

  4. 【.net 深呼吸】跨应用程序域执行程序集

    应用程序域,你在网上可以查到它的定义,凡是概念性的东西,大伙儿只需要会搜索就行,内容看了就罢,不用去记忆,更不用去背,“名词解释”是大学考试里面最无聊最没水平的题型. 简单地说,应用程序域让你可以在一 ...

  5. iOS系列文章

    本博客全为原创,如果借鉴了其他文章会在博文的下面进行说明.欢迎转载,但要在文章中给出原文链接,谢谢. 有链接的说明已经发布,没有链接的说明还没有发布. 并不是所有的博文都在这里罗列,有兴趣的可以看博客 ...

  6. 小兔JS教程(四)-- 彻底攻略JS数组

    在开始本章之前,先给出上一节的答案,参考答案地址: http://www.xiaotublog.com/demo.html?path=homework/03/index2 1.JS数组的三大特性 在J ...

  7. 最好的.NET开源免费ZIP库DotNetZip(.NET组件介绍之三)

    在项目开发中,除了对数据的展示更多的就是对文件的相关操作,例如文件的创建和删除,以及文件的压缩和解压.文件压缩的好处有很多,主要就是在文件传输的方面,文件压缩的好处就不需要赘述,因为无论是开发者,还是 ...

  8. BI分析受阻?FineBI推出SPA螺旋式分析新功能!

    过去,企业级的数据分析通常会有这么几种场景,业务部门托信息部门分析数据,结果报表一出,唇枪舌剑争论你我高低,数据不准,指标不对.信息部门欠缺业务概念,业务部门不懂技术逻辑,数据分析之路,暂时搁浅. 后 ...

  9. iOS 后台处理

    iOS 后台处理的常见用途 1.进入后台时候删除资源:应用处于挂起状态的时候所占用的资源越少,该应用被iOS终止的风险就越低.通过从内存中清理那些易于重新创建的资源,可以增加应用驻留内存的机会,因此可 ...

  10. BPM与 SAP & Oracle EBS集成解决方案分享

    一.需求分析 SAP和Oracle EBS都是作为全球顶级的的ERP产 品,得到了众多客户的青睐.然而由于系统庞大.价格昂贵以及定位不同,客户在实施过程中经常会面临以下困惑: 1.SAP如何实现&qu ...