数据挖掘笔记-聚类-KMeans-原理与简单实现

KMeans
K-means(k均值)算法是很典型的基于距离的聚类算法,采用距离作为相似性的评价指标,即认为两个对象的距离越近,其相似度就越大。该算法认为簇是由距离靠近的对象组成的,因此把得到紧凑且独立的簇作为最终目标。
基本算法:
1.选择K个点作为初始质心。
2.Repeat
3.     将每个点指派到最近的质心,形成K个簇。
4.     重新计算每个簇的质心。
Until 质心不再发生变化。
1.初始点的选择策略
随机选取K个点、均匀抽样选取K个点、最大最小法选取、Canopy算法选取。
2.指派点到最近的质心
对欧式空间的点可以使用欧氏距离。
对文档采用余弦相似度。
可以使用曼哈顿距离,jaccard度量来降低计算度。
3.质心和目标函数
邻近性度量采用欧氏距离时,使用误差的平方和(SSE)来作为度量聚类质量的目标函数。使其最小化来得到最优解。
邻近性度量采用余弦相似度时,使用凝聚度来作为度量聚类质量的目标函数。使其最大化来得到最优解。
4.  算法停止条件   
计算准则函数,原中心点与新中心点距离小于或等于一定阀值等等
设置最大迭代次数
K-MEANS算法的缺点:产生聚类的大小相差不会很大,对于脏数据很敏感。
KMedoids

K中心点算法(K-medoids)提出了新的质点选取方式,而不是简单像k-means算法采用均值计算法。在K中心点算法中,每次迭代后的质点都是从聚类的样本点中选取,而选取的标准就是当该样本点成为新的质点后能提高类簇的聚类质量,使得类簇更紧凑。该算法使用绝对误差标准来定义一个类簇的紧凑程度。

如果某样本点成为质点后,绝对误差能小于原质点所造成的绝对误差,那么K中心点算法认为该样本点是可以取代原质点的,在一次迭代重计算类簇质点的时候,我们选择绝对误差最小的那个样本点成为新的质点。较好的解决了对离群点/噪声数据的敏感,但时间复杂度上升至O(k(m-k)^2)。计算量显然要比KMeans要大,一般只适合小数据量。

二分KMeans

二分KMeans是对基本KMeans的直接扩充,它基于一种简单想法:为了得到K个簇,将所有点集合分裂成两个簇,从这些簇中选取一个继续分裂,直到产生K个簇。

二分k均值(bisecting k-means)算法的主要思想是:首先将所有点作为一个簇,然后将该簇一分为二。之后选择能最大程度降低聚类代价函数(也就是误差平方和)的簇划分为两个簇。以此进行下去,直到簇的数目等于用户给定的数目k为止。

       以上隐含着一个原则是:因为聚类的误差平方和能够衡量聚类性能,该值越小表示数据点月接近于它们的质心,聚类效果就越好。所以我们就需要对误差平方和最大的簇进行再一次的划分,因为误差平方和越大,表示该簇聚类越不好,越有可能是多个簇被当成一个簇了,所以我们首先需要对这个簇进行划分。

二分k均值算法的伪代码如下:

将所有数据点看成一个簇

当簇数目小于k时

       对每一个簇

              计算总误差

              在给定的簇上面进行k-均值聚类(k=2)

              计算将该簇一分为二后的总误差

       选择使得误差最小的那个簇进行划分操作​​​​​​​

下面用Java来简单实现算法,考虑简单,点只用了二维。

public class KMeansCluster extends AbstractCluster {
	
	public static final double THRESHOLD = 1.0;
	
	public List<Point> initData() {
		List<Point> points = new ArrayList<Point>();
		InputStream in = null;
		BufferedReader br = null;
		try {
			in = KMeansCluster.class.getClassLoader().getResourceAsStream("kmeans1.txt");
			br = new BufferedReader(new InputStreamReader(in));
			String line = br.readLine();
			while (null != line && !"".equals(line)) {
				StringTokenizer tokenizer = new StringTokenizer(line);
				double x = Double.parseDouble(tokenizer.nextToken());
				double y = Double.parseDouble(tokenizer.nextToken());
				points.add(new Point(x , y));
				line = br.readLine();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			IOUtils.closeQuietly(in);
			IOUtils.closeQuietly(br);
		}
		return points;
	}
	
	//随机生成中心点,并生成初始的K个聚类
	public List<PointCluster> genInitCluster(List<Point> points, int k) {
		List<PointCluster> clusters = new ArrayList<PointCluster>();
		Random random = new Random();
		for (int i = 0, len = points.size(); i < k; i++) {
			PointCluster cluster = new PointCluster();
			Point center = points.get(random.nextInt(len));
			cluster.setCenter(center);
			cluster.getPoints().add(center);
			clusters.add(cluster);
		}
		return clusters;
	}
	
	//将点归入到聚类中
	public void handleCluster(List<Point> points, List<PointCluster> clusters) {
		for (Point point : points) {
			PointCluster minCluster = null;
			double minDistance = Integer.MAX_VALUE;
			for (PointCluster cluster : clusters) {
				Point center = cluster.getCenter();
				double distance = euclideanDistance(point, center);
//				double distance = manhattanDistance(point, center);
				if (distance < minDistance) {
					minDistance = distance;
					minCluster = cluster;
				}
			}
			if (null != minCluster) {
				minCluster.getPoints().add(point);
			}
		}
		//终止条件定义为原中心点与新中心点距离小于一定阀值
		//当然也可以定义为原中心点等于新中心点
		boolean flag = true;
		for (PointCluster cluster : clusters) {
			Point center = cluster.getCenter();
			System.out.println("center: " + center);
			Point newCenter = cluster.computeMeansCenter();
			System.out.println("new center: " + newCenter);
//			if (!center.equals(newCenter)) {
			double distance = euclideanDistance(center, newCenter);
			System.out.println("distaince: " + distance);
			if (distance > THRESHOLD) {
				flag = false;
				cluster.setCenter(newCenter);
			}
		}
		if (!flag) {
			for (PointCluster cluster : clusters) {
				cluster.getPoints().clear();
			}
			handleCluster(points, clusters);
		}
	}
	
	public List<PointCluster> cluster(List<Point> points, int k) {
		List<PointCluster> clusters = genInitCluster(points, k);
		handleCluster(points, clusters);
		return clusters;
	}
	
	public void build() {
		List<Point> points = initData();
		List<PointCluster> clusters = cluster(points, 4);
		printClusters(clusters);
	}

	public static void main(String[] args) {
		KMeansCluster builder = new KMeansCluster();
		builder.build();
	}
}
KMediodsCluster
public class KMediodsCluster extends AbstractCluster {
	
	public static final double THRESHOLD = 2.0;
	
	public List<Point> initData() {
		List<Point> points = new ArrayList<Point>();
		InputStream in = null;
		BufferedReader br = null;
		try {
			in = KMediodsCluster.class.getClassLoader().getResourceAsStream("kmeans1.txt");
			br = new BufferedReader(new InputStreamReader(in));
			String line = br.readLine();
			while (null != line && !"".equals(line)) {
				StringTokenizer tokenizer = new StringTokenizer(line);
				double x = Double.parseDouble(tokenizer.nextToken());
				double y = Double.parseDouble(tokenizer.nextToken());
				points.add(new Point(x , y));
				line = br.readLine();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			IOUtils.closeQuietly(in);
			IOUtils.closeQuietly(br);
		}
		return points;
	}
	
	//随机生成中心点,并生成初始的K个聚类
	public List<PointCluster> genInitCluster(List<Point> points, int k) {
		List<PointCluster> clusters = new ArrayList<PointCluster>();
		Random random = new Random();
		for (int i = 0, len = points.size(); i < k; i++) {
			PointCluster cluster = new PointCluster();
			Point center = points.get(random.nextInt(len));
			cluster.setCenter(center);
			cluster.getPoints().add(center);
			clusters.add(cluster);
		}
		return clusters;
	}
	
	//将点归入到聚类中
	public void handleCluster(List<Point> points, List<PointCluster> clusters) {
		for (Point point : points) {
			PointCluster minCluster = null;
			double minDistance = Integer.MAX_VALUE;
			for (PointCluster cluster : clusters) {
				Point center = cluster.getCenter();
				double distance = euclideanDistance(point, center);
//				double distance = manhattanDistance(point, center);
				if (distance < minDistance) {
					minDistance = distance;
					minCluster = cluster;
				}
			}
			if (null != minCluster) {
				minCluster.getPoints().add(point);
			}
		}
		//终止条件定义为原中心点与新中心点距离小于一定阀值
		//当然也可以定义为原中心点等于新中心点
		boolean flag = true;
		for (PointCluster cluster : clusters) {
			Point center = cluster.getCenter();
			System.out.println("center: " + center);
			Point newCenter = cluster.computeMediodsCenter();
			System.out.println("new center: " + newCenter);
//			if (!center.equals(newCenter)) {
			double distance = euclideanDistance(center, newCenter);
			System.out.println("distaince: " + distance);
			if (distance > THRESHOLD) {
				flag = false;
				cluster.setCenter(newCenter);
			}
		}
		if (!flag) {
			for (PointCluster cluster : clusters) {
				cluster.getPoints().clear();
			}
			handleCluster(points, clusters);
		}
	}
	
	public List<PointCluster> cluster(List<Point> points, int k) {
		List<PointCluster> clusters = genInitCluster(points, k);
		handleCluster(points, clusters);
		return clusters;
	}
	
	public void build() {
		List<Point> points = initData();
		List<PointCluster> clusters = cluster(points, 4);
		printClusters(clusters);
	}
	
	public static void main(String[] args) {
		KMediodsCluster builder = new KMediodsCluster();
		builder.build();
	}
}
PointCluster
public class PointCluster {

	private Point center = null;

	private List<Point> points = null;

	public Point getCenter() {
		return center;
	}

	public void setCenter(Point center) {
		this.center = center;
	}

	public List<Point> getPoints() {
		if (null == points) {
			points = new ArrayList<Point>();
		}
		return points;
	}

	public void setPoints(List<Point> points) {
		this.points = points;
	}
	
	public Point computeMeansCenter() {
		int len = getPoints().size();
		double a = 0.0, b = 0.0;
		for (Point point : getPoints()) {
			a += point.getX();
			b += point.getY();
		}
		return new Point(a / len, b / len);
	}
	
	public Point computeMediodsCenter() {
		Point targetPoint = null;
		double distance = Integer.MAX_VALUE;
		for (Point point : getPoints()) {
			double d = 0.0;
			for (Point temp : getPoints()) {
				d += manhattanDistance(point, temp);
			}
			if (d < distance) {
				distance = d;
				targetPoint = point;
			}
		}
		return targetPoint;
	}
	
	public double computeSSE() {
		double result = 0.0;
		for (Point point : getPoints()) {
			result += euclideanDistance(point, center);
		}
		return result;
	}
	
	//计算两点之间的曼哈顿距离
	protected double manhattanDistance(Point a, Point b) {
		return Math.abs(a.getX() - b.getX()) + Math.abs(a.getY() - b.getY());
	}
	
	//计算两点之间的欧氏距离
	protected double euclideanDistance(Point a, Point b) {
		double sum =  Math.pow(a.getX() - b.getX(), 2) + Math.pow(a.getY() - b.getY(), 2);
		return Math.sqrt(sum);
	}
}
代码托管:https://github.com/fighting-one-piece/repository-datamining.git

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【摘要】 目前,对于聚类问题的研究普遍存在于社会生活中的各个领域,如模式识别、图像处理、机器学习和统计学等。关于对生活中各种各样的数据的聚类分类问题已经成为众多学者的研究热题之一。聚类和分类的区别在于,聚类没有任何先验知识可循,要通过数据自身的特点,将数据自动的划分到不同的类别中。聚类的基本形式定义为“在已给的数据集合中寻找数据点集的同类集合。每一个集合叫做一个类,并确定了一个区域,在区域中对象的密度高于其他区域中的密度。”聚类有很多种,其中最简单的形式便是划分式聚类,划分式聚类试图将给定的数据集合分割成不相交的子集,使具体的聚类准则是最优的。实际中应用最广泛的准则是聚类误差平方和准则,即对于每一个点都计算它到相应的聚类中心点的平方距离,并对数据集合上的所有点的距离进行求和。一种最流行的基于最小聚类误差平和的聚类是K-均值。然而,K-均值是一个局部搜索的算,它存在一些严重的不足,比如K值需要预先确定、聚类结果的好坏依赖于初始点的选取。为了解决这些问题,这个领域的研究者开发了很多其他的一些技术,试图基于全局最优化的方来解决聚类问题(比如模拟退火算、遗传算等)。然而这些技术并没有得到广泛的认可,在许多实际应用中应用最多的还是反复利用K-均值。K-均值是一种基于划分的聚类,它通过不断的迭代来进行聚类,当算收敛到一个结束条件时就终止迭代过程,输出聚类结果。由于其算思想简便,又容易实现对大规模数据的聚类,因此K-均值已成为一种最常用的聚类之一K-均值能找到关于聚类误差的局部的最优解,是一个能应用在许多聚类问题上的快速迭代算。它是一种以点为基础的聚类,以随机选取的初始点为聚类中心,迭代地改变聚类中心来使聚类误差最小化。这种方最主要的不足就是对于初始聚类中心点位置的选取敏感。因此,为了得到近似最优解,初始聚类中心的位置必须安排的有差异。本文就K-均值聚类聚类结果依赖于初始中心,而且经常收敛于局部最优解,而非全局最优解,以及聚类类别数K需要事先给定这两大缺憾展开研究。提出了分别解决这两个问题的算各一个首先,本文将Hae-Sang等人的快速K-中心点算确定初始中心点的思想应用于Aristidis Likas的全局K-均值聚类中下一个簇的初始中心选择上,提出一种改进的全局K-均值聚类,试图寻找一个周围样本点分布比较密集,且距离现有簇的中心都较远的样本点,将其作为下一个簇的最佳初始中心。通过对UCI机器学习数据库数据及人工随机模拟数据的测试,证明本文算与Aristidis Likas的全局K-均值聚类和快速全局K-均值聚类比,在不影响聚类误差平方和的前提下,聚类时间更短,具有更好的性能。同时,本文介绍了自组织特征映射网络(Self-Organizing Feature Map, SOFM)的相关内容,SOFM网络是将多维数据映射到低维规则网格中,可以有效的进行大规模的数据挖掘,其特点是速度快,但是分类的精度不高。而K-均值聚类,是一种通过不断迭代调整聚类质心的算,其特点是精度高,主要用于中小数据集的分类,但是聚类速度比较慢。因此,本文在分析了基于自组织特征映射网络聚类的学习过程,权系数自组织过程中邻域函数,以及学习步长的一般取值问题后,给出了基于自组织特征映射网络聚类实现的具体算,将自组织特征网络与K-均值聚类相结合,提出了一种基于自组织映射网络的聚类,来实现对输入模式进行聚类,实现K-均值聚类聚类类别数的自动确定。同时通过实验进行仿真实现,证明该算的有效性。 还原 【Abstract】 Clustering is a fundamental problem that frequently arises in a great variety of fields such as pattern recognition, image processing, machine learning and statistics. In general, clustering is defined as the problem of finding homogeneous groups of samples in a given data set. Each of these groups is called a cluster and can be defined as a region in which the density of exemplars is locally higher than in other regions.The simplest form of clustering is partition clustering w

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值