学习笔记——K-means(1) 简要介绍、算法优劣、简单k-means的实现
来源:互联网 发布:财经数据网 编辑:程序博客网 时间:2024/05/17 01:49
l P6 2013.10.22 早上八点
问题:简单Kmeans(随机选取初始类中心)
简要介绍:
K-means是聚类算法中最简单的一种,聚类属于无监督学习,与监督学习中回归、朴素贝叶斯、SVM等有类别标签y不一样,聚类没有给定y,只有特征x,比如架设宇宙中的星星可以表示成三维空间中的点集(x,y,z),聚类的目的是找到每个样本x潜在的类别y,并将同类别y的样本x放在一起,比如上面提到的星星问题,聚类后的结果是一个一个的星团,星团里面的点相互之间的距离较近,星团与星团之间的距离就比较远了。
K-means算法是将样本聚类成k个簇(cluster),具体算法描述如下:
1、随机选取k个聚类质心点(cluster centroids)为。
2、重复下面过程直到收敛 {
对于每一个样例i,计算其应该属于的类
对于每一个类j,重新计算该类的质心
}
K-means算法存在的问题
K-means算法的优势就是简洁和效率,算法的时间复杂度是O(tkn),其中n是数据点的个数,t是循环迭代的次数,k是聚类中心的个数,k-means算法被认为相对于数据点的数目来说是线性的。
但是k-means算法同样存在许多问题:(参考至《Web 数据挖掘》一书 作者 Bing Liu)
算法只能应用于那些均值能够被定义的数据集上
用户需要事先指定聚类数目k
算法对异常值十分敏感
算法对初始种子的选取十分敏感,即那些被初选为初始聚类中心的数据点
k-means算法不适合于发现那些形状不是超维椭圆体(或超维球体)的聚类
缺点主要有三个:(参考至http://hi.baidu.com/cuijianyong73/item/d741cd8022070050e63d19b9)
① 在 K-means 算法中 K 是事先给定的,这个 K 值的选定是非常难以估计的。很多时候,事先并不知道给定的数据集应该分成多少个类别才最合适。这也是 K-means 算法的一个不足。有的算法是通过类的自动合并和分裂,得到较为合理的类型数目 K,例如 ISODATA 算法。关于 K-means 算法中聚类数目K 值的确定在文献[23]中,是根据方差分析理论,应用混合 F 统计量来确定最佳分类数,并应用了模糊划分熵来验证最佳分类数的正确性。在文献[24]中,使用了一种结合全协方差矩阵的 RPCL 算法,并逐步删除那些只包含少量训练数据的类。而文献[25]中使用的是一种称为次胜者受罚的竞争学习规则,来自动决定类的适当数目。它的思想是:对每个输入而言,不仅竞争获胜单元的权值被修正以适应输入值,而且对次胜单元采用惩罚的方法使之远离输入值。
② 在 K-means 算法中,首先需要根据初始聚类中心来确定一个初始划分,然后对初始划分进行优化。这个初始聚类中心的选择对聚类结果有较大的影响[26-29],一旦初始值选择的不好,可能无法得到有效的聚类结果,这也成为 K-means算法的一个主要问题。对于该问题的解决,许多算法采用遗传算法(GA),例如文献中采用遗传算法(GA)进行初始化,以内部聚类准则作为评价[30]指标。
③ 从 K-means 算法框架可以看出,该算法需要不断地进行样本分类调整,不断地计算调整后的新的聚类中心,因此当数据量非常大时,算法的时间开销是非常大的。所以需要对算法的时间复杂度进行分析、改进,提高算法应用范围。在文献[31,32]中从该算法的时间复杂度进行分析考虑,通过一定的相似性准则来去掉聚类中心的侯选集。而在文献[33]中,使用的 K-means 算法是对样本数据进行聚类,无论是初始点的选择还是一次迭代完成时对数据的调整,都是建立在随机选取的样本数据的基础之上,这样可以提高算法的收敛速度。
以上只是对k-means的简要陈述,详细的参考资料有《数据挖掘-概念与技术》作者:Jiawei Han和《Web 数据挖掘》作者 Bing Liu
下面是两个用C#实现的简单k-means算法(即随机选取初始类中心)
C#实现
代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/// <summary>
/// 简单的kmeans,计算二维空间的点,随机选择k个初始类中心
/// </summary>
namespace PracticeLog
{
public class Point
{
/// <summary>
/// X轴
/// </summary>
public int X
{
get;
set;
}
/// <summary>
/// Y轴
/// </summary>
public int Y
{
get;
set;
}
/// <summary>
/// point所属中心
/// </summary>
public int Focus
{
get;
set;
}
public Point()
{
}
}
/// <summary>
/// Kmeans
/// </summary>
class Kmeans
{
/// <summary>
/// 类中心的个数
/// </summary>
public int K
{
get;
set;
}
/// <summary>
/// 数据量(point点的个数)
/// </summary>
public int Num
{
get;
set;
}
/// <summary>
/// 迭代的次数
/// </summary>
public int Iterative
{
get;
set;
}
/// <summary>
/// 点集合
/// </summary>
public Point[] Points;
/// <summary>
/// 类中心点集合
/// </summary>
public Point[] Center;
/// <summary>
/// 构造函数,初始化相关参数
/// </summary>
/// <param name="k">类中心的个数</param>
/// <param name="num">点集的个数</param>
/// <param name="iterative">迭代的次数</param>
public Kmeans(int k, int num, int iterative)
{
this.K = k;
this.Num = num;
this.Iterative = iterative;
this.Points = new Point[this.Num];
this.Center = new Point[this.K];
}
/// <summary>
/// 初始化数据和类中心
/// </summary>
private void Init()
{
Random randXY = new Random();
//初始化数据,构造待聚类点集
for (int i = 0; i < this.Num; ++i)
{
//数据对象
Points[i] = new Point();
Points[i].X = randXY.Next();
Points[i].Y = randXY.Next();
//随机类中心
Random randFocus=new Random();
Points[i].Focus =randFocus.Next(0, this.K - 1);
//Console.WriteLine(Points[i].Focus);
}
//初始化类中心
for (int j = 0; j < this.K; ++j)
{
Center[j] = new Point();
Center[j].X = randXY.Next();
Center[j].Y = randXY.Next();
}
}
/// <summary>
/// 计算每个点与类中的距离,并重新分配点所属类中心
/// </summary>
private void Compute()
{
for (int i = 0; i < this.Num;++i )
{
double distance = -1;
for (int j = 0; j <this.K;++j )
{
double tmpDistance =EuclidDistance(Points[i], Center[j]);
if(tmpDistance<distance||j==0)
{
distance = tmpDistance;
Points[i].Focus = j;
}
}
}
}
/// <summary>
/// 计算二维空间中两点的欧式距离
/// </summary>
/// <param name="a">点a</param>
/// <param name="b">点b</param>
/// <returns></returns>
private double EuclidDistance(Point a, Point b)
{
double dou = 0;
dou=(a.X-b.X)*(a.X-b.X)+(a.Y-b.Y)*(a.Y-b.Y);
return Math.Sqrt(dou);
}
/// <summary>
/// 重新计算类中心
/// </summary>
private void UpdateCenter()
{
for (int i = 0; i < this.K;++i )
{
int x = 0;
int y = 0;
int count = 0;
for (int j = 0; j < this.Num;++j)
{
if (Points[j].Focus==i)
{
x += Points[j].X;
y += Points[j].Y;
count++;
}
}
//防止在二次分配类中心的时候出现没有点集的类中心出现
x = x / (count == 0 ? 1 :count);
y = y / (count == 0 ? 1 :count);
Center[i].X = x;
Center[i].Y = y;
}
}
/// <summary>
/// 打印计算完成的类中心
/// </summary>
private void Print()
{
foreach (Point p in Center)
{
Console.WriteLine(p.X +"," + p.Y);
}
}
public void Do()
{
this.Init();
while (this.Iterative > 0)
{
this.Compute();
this.UpdateCenter();
this.Iterative--;
}
this.Print();
}
}
}
代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PracticeLog
{
public class NPoint
{
/// <summary>
/// N维向量data
/// </summary>
public double[] data
{
get;
set;
}
/// <summary>
/// point所属中心
/// </summary>
public int Focus
{
get;
set;
}
public NPoint()
{
}
}
class KmeansNDim
{
/// <summary>
/// 类中心的个数
/// </summary>
public int K
{
get;
set;
}
/// <summary>
/// 数据量(point点的个数)
/// </summary>
public int Num
{
get;
set;
}
/// <summary>
/// 迭代的次数
/// </summary>
public int Iterative
{
get;
set;
}
/// <summary>
/// 数据的维数
/// </summary>
public int N
{
get;
set;
}
/// <summary>
/// 点集合
/// </summary>
public NPoint[] Points;
/// <summary>
/// 类中心点集合
/// </summary>
public NPoint[] Center;
/// <summary>
/// 构造函数,初始化相关参数
/// </summary>
/// <param name="k">类中心的个数</param>
/// <param name="num">点集的个数</param>
/// <param name="iterative">迭代的次数</param>
public KmeansNDim(int k, int num, int N, int iterative)
{
this.K = k;
this.Num = num;
this.N=N;
this.Iterative = iterative;
this.Points = new NPoint[this.Num];
this.Center = new NPoint[this.K];
}
/// <summary>
/// 初始化数据和类中心
/// </summary>
private void Init()
{
Random randData = new Random();
for (int i = 0; i < this.Num; ++i)
{
Points[i] = new NPoint();
Points[i].data = newdouble[this.N];
for (int j=0;j<this.N;++j)
{
Points[i].data[j]=randData.Next();d
}
Random randFocus=new Random();
Points[i].Focus=randFocus.Next(0,this.K-1);
}
for (int m=0;m<this.K;++m)
{
Center[m]=new NPoint();
Center[m].data = newdouble[this.N];
for (int n=0;n<this.N;++n)
{
Center[m].data[n]=randData.Next();
}
}
}
/// <summary>
/// 计算每个点与类中的距离,并重新分配点所属类中心
/// </summary>
private void Compute()
{
for (int i = 0; i < this.Num;++i )
{
double distance = -1;
for (int j = 0; j <this.K;++j )
{
double tmpDis =EuclidDistance(Points[i].data, Center[j].data);
if (tmpDis<distance||j==0)
{
distance = tmpDis;
Points[i].Focus = j;
}
}
}
}
/// <summary>
/// 计算二维空间中两点的欧式距离
/// </summary>
/// <param name="a">点A</param>
/// <param name="b">点B</param>
/// <returns></returns>
private double EuclidDistance(double[] pointA, double[] pointB)
{
int len = pointA.Length;
double dist = 0;
for (int i = 0; i < len;++i )
{
dist += ((pointA[i] -pointB[i]) * (pointA[i] - pointB[i]));
}
return Math.Sqrt(dist);
}
/// <summary>
/// 重新计算类中心
/// </summary>
private void UpdateCenter()
{
for (int i = 0; i < this.K;++i )
{
double[] tmp=newdouble[this.N];
int count = 0;
for (int j = 0; j <this.Num;++j )
{
if (Points[j].Focus==i)
{
for (int k=0; k <this.N;++k )
{
tmp[k] +=Points[j].data[k];
}
count++;
}
//防止在二次分配类中心的时候出现没有点集的类中心出现
count = (count == 0 ? 1 :count);
for (int m = 0; m <this.N; ++m)
{
Center[i].data[m] =tmp[m] / count;
}
}
}
}
/// <summary>
/// 打印计算完成的类中心
/// </summary>
private void Print()
{
foreach (NPoint p in Center)
{
for (int i = 0; i <this.N;++i )
{
Console.Write(p.data[i] +"_");
}
Console.WriteLine();
}
}
public void Do()
{
this.Init();
while (this.Iterative > 0)
{
this.Compute();
this.UpdateCenter();
this.Iterative--;
}
this.Print();
}
}
}
未完待续……
- 学习笔记——K-means(1) 简要介绍、算法优劣、简单k-means的实现
- k-means/k-means++算法的笔记及scala实现
- K-Means算法学习笔记
- 学习笔记- K-means算法
- K-means算法的介绍
- K-Means的简单实现
- K-means的简单实现
- K-means算法的实现
- k-means 简单实现
- K—means算法实现流程
- K-means算法实现
- K-means算法实现
- k-means算法学习
- 机器学习笔记——K-means
- 机器学习笔记——K-Means
- K-means学习笔记
- K-means学习笔记
- K-means学习笔记
- MFC中CTreeCtrl加载节点缓慢的分析和解决方法
- WWDC 上讲到的 Objective C / LLVM 改进
- UVa 11265 The Sultan's Problem
- 类似百度文库源码
- 再读《与大象共舞》
- 学习笔记——K-means(1) 简要介绍、算法优劣、简单k-means的实现
- Android 网络通信框架Volley简介(Google IO 2013)
- Windows服务编写原理及探讨
- opencv两视频叠加(控制台版)
- CSRF的攻击与防御
- Step By Step(Lua面向对象)
- Linux 终端访问 FTP 及 上传下载 文件 .
- warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=l
- 已知圆外一点,圆心和半径,求过圆外点的直线与圆的切点算法