数据挖掘/机器学习算法--直接聚类算法(k-means)
来源:互联网 发布:js 获取signature 编辑:程序博客网 时间:2024/06/18 00:35
大家好!我是钱大鑫!本篇博客我主要跟大家探讨一下经典聚类算法——k-means。还是那句话,学习算法不是一件可以瞬间学会的事情,大家慢慢读,慢慢领悟,内容绝大部分为会设计到一些数学知识,算法嘛,正常的!读完记得自己实践一下,好了,废话不多说,我们直接来看k-means算法。
1. 算法基本介绍
首先我们来理解一下什么是
下面,我们就来具体的探讨一下
2. 算法描述及伪代码
在
聚类算法通常是基于“紧密度”或者“相似度”等概念来对点集进行分组的。具体到
专业一点说,就是
讲了这么多了,我们来具体看一看
k-means算法输入:数据集D,聚簇数k输出:聚簇代表集合C,聚簇成员向量m/*初始化聚簇代表C*/从数据集D中随机挑选k个数据点使用这k个数据点构成初识聚簇代表集合Crepeat /*再分数据*/ 将D中的每个数据点重新分配至与之最近的聚簇均值 更新m(mi表示D中第i个点的聚簇标识) /*重定均值*/ 更新C(cj表示第j个聚簇均值)until 目标函数收敛
以上就是
步骤1:再分数据。将每个数据点分配到当前与之最近的那个聚簇中心,同时打破了上次迭代确定的归属关系。
步骤2:重定均值。重新确定每一个聚簇代表,即计算所有分配给该聚簇代表的数据中心(如算数平均值)。
算法收敛有两种表现形式:1. 目标函数值不断缩小至一定值并且不再发生明显变化;2. 聚簇代表集合
到这里,我们可以考虑一下它的算法复杂度。注意到每一次迭代都需要
当然,这样的
前面提到,
1. 直接比较具有不同聚簇数量的聚类效果;
2. 寻找出最优的
所以,如果实现无法知道理想的
除了上面提到的这些局限,还有一个
讲了这么多理论,下面我们讲个实例。
3.
Java实现
1. 数据获取类
package kmeans;/** 1. 数据获取类 2. @param <b>data</b> <i>in double[length][dim]</i><br/>length个instance的坐标,第i(0~length-1)个instance为data[i] 3. @param <b>length</b> <i>in</i> instance个数 4. @param <b>dim</b> <i>in</i> instance维数 5. @param <b>label</b> <i>out int[length]</i><br/>聚类后,instance所属的聚类标号(0~k-1) 6. @param <b>centers</b> <i>in out double[k][dim]</i><br/>k个聚类中心点的坐标,第i(0~k-1)个中心点为centers[i] 7. */public class kmeans_data { public double[][] data; public int length; public int dim; public int[] labels; public double[][] centers; public int[] centerCounts; public kmeans_data(double[][] data, int length, int dim) { this.data = data; this.length = length; this.dim = dim; }}
2. 参数初始化类
package kmeans;/** 1. 2. 参数初始化类 3. */public class kmeans_param { public static final int CENTER_ORDER = 0; public static final int CENTER_RANDOM = 1; public static final int MAX_ATTEMPTS = 4000; public static final double MIN_CRITERIA = 1.0; public double criteria = MIN_CRITERIA; //阈值 public int attempts = MAX_ATTEMPTS; //尝试次数 public int initCenterMehtod = CENTER_ORDER; //初始化聚类中心点方式 public boolean isDisplay = true; //是否直接显示结果}
3. 结果类
package kmeans;/** * * 结果类 * */public class kmeans_result { public int attempts; // 退出迭代时的尝试次数 public double criteriaBreakCondition; // 退出迭代时的最大距离(小于阈值) public int k; // 聚类数}
4. KMeans计算类
package kmeans;import java.util.Arrays;import java.util.Collections;import java.util.LinkedList;import java.util.List;import java.util.Random;/** 1. 2. KMeans计算类 3. */public class kmeans { /** * double[][] 元素全置0 * * @param matrix * double[][] * @param highDim * int * @param lowDim * int * double[highDim][lowDim] */ private static void setDouble2Zero(double[][] matrix, int highDim, int lowDim) { for (int i = 0; i < highDim; i++) { for (int j = 0; j < lowDim; j++) { matrix[i][j] = 0; } } } /** * 拷贝源二维矩阵元素到目标二维矩阵。 foreach (dests[highDim][lowDim] = sources[highDim][lowDim]); * * @param dests * double[][] * @param sources * double[][] * @param highDim * int * @param lowDim * int */ private static void copyCenters(double[][] dests, double[][] sources, int highDim, int lowDim) { for (int i = 0; i < highDim; i++) { for (int j = 0; j < lowDim; j++) { dests[i][j] = sources[i][j]; } } } /** * 更新聚类中心坐标 * * @param k * int 分类个数 * @param data * kmeans_data */ private static void updateCenters(int k, kmeans_data data) { double[][] centers = data.centers; setDouble2Zero(centers, k, data.dim); int[] labels = data.labels; int[] centerCounts = data.centerCounts; for (int i = 0; i < data.dim; i++) { for (int j = 0; j < data.length; j++) { centers[labels[j]][i] += data.data[j][i]; } } for (int i = 0; i < k; i++) { for (int j = 0; j < data.dim; j++) { centers[i][j] = centers[i][j] / centerCounts[i]; } } } /** * 计算两点的欧几里得距离 * * @param pa * double[] * @param pb * double[] * @param dim * int 维数 * @return double 距离 */ public static double dist(double[] pa, double[] pb, int dim) { double rv = 0; for (int i = 0; i < dim; i++) { double temp = pa[i] - pb[i]; temp = temp * temp; rv += temp; } return Math.sqrt(rv); } /** * 做Kmeans运算 * * @param k * int 聚类个数 * @param data * kmeans_data kmeans数据类 * @param param * kmeans_param kmeans参数类 * @return kmeans_result kmeans运行信息类 */ public static kmeans_result doKmeans(int k, kmeans_data data, kmeans_param param) { // 预处理 double[][] centers = new double[k][data.dim]; // 聚类中心点集 data.centers = centers; int[] centerCounts = new int[k]; // 各聚类的包含点个数 data.centerCounts = centerCounts; Arrays.fill(centerCounts, 0); int[] labels = new int[data.length]; // 各个点所属聚类标号 data.labels = labels; double[][] oldCenters = new double[k][data.dim]; // 临时缓存旧的聚类中心坐标 // 初始化聚类中心(随机或者依序选择data内的k个不重复点) if (param.initCenterMehtod == kmeans_param.CENTER_RANDOM) { // 随机选取k个初始聚类中心 Random rn = new Random(); List<Integer> seeds = new LinkedList<Integer>(); while (seeds.size() < k) { int randomInt = rn.nextInt(data.length); if (!seeds.contains(randomInt)) { seeds.add(randomInt); } } Collections.sort(seeds); for (int i = 0; i < k; i++) { int m = seeds.remove(0); for (int j = 0; j < data.dim; j++) { centers[i][j] = data.data[m][j]; } } } else { // 选取前k个点位初始聚类中心 for (int i = 0; i < k; i++) { for (int j = 0; j < data.dim; j++) { centers[i][j] = data.data[i][j]; } } } // 第一轮迭代 for (int i = 0; i < data.length; i++) { double minDist = dist(data.data[i], centers[0], data.dim); int label = 0; for (int j = 1; j < k; j++) { double tempDist = dist(data.data[i], centers[j], data.dim); if (tempDist < minDist) { minDist = tempDist; label = j; } } labels[i] = label; centerCounts[label]++; } updateCenters(k, data); copyCenters(oldCenters, centers, k, data.dim); // 迭代预处理 int maxAttempts = param.attempts > 0 ? param.attempts : kmeans_param.MAX_ATTEMPTS; int attempts = 1; double criteria = param.criteria > 0 ? param.criteria : kmeans_param.MIN_CRITERIA; double criteriaBreakCondition = 0; boolean[] flags = new boolean[k]; // 标记哪些中心被修改过 // 迭代 iterate: while (attempts < maxAttempts) { // 迭代次数不超过最大值,最大中心改变量不超过阈值 for (int i = 0; i < k; i++) { // 初始化中心点“是否被修改过”标记 flags[i] = false; } for (int i = 0; i < data.length; i++) { // 遍历data内所有点 double minDist = dist(data.data[i], centers[0], data.dim); int label = 0; for (int j = 1; j < k; j++) { double tempDist = dist(data.data[i], centers[j], data.dim); if (tempDist < minDist) { minDist = tempDist; label = j; } } if (label != labels[i]) { // 如果当前点被聚类到新的类别则做更新 int oldLabel = labels[i]; labels[i] = label; centerCounts[oldLabel]--; centerCounts[label]++; flags[oldLabel] = true; flags[label] = true; } } updateCenters(k, data); attempts++; // 计算被修改过的中心点最大修改量是否超过阈值 double maxDist = 0; for (int i = 0; i < k; i++) { if (flags[i]) { double tempDist = dist(centers[i], oldCenters[i], data.dim); if (maxDist < tempDist) { maxDist = tempDist; } for (int j = 0; j < data.dim; j++) { // 更新oldCenter oldCenters[i][j] = centers[i][j]; } } } if (maxDist < criteria) { criteriaBreakCondition = maxDist; break iterate; } } // 输出信息 kmeans_result rvInfo = new kmeans_result(); rvInfo.attempts = attempts; rvInfo.criteriaBreakCondition = criteriaBreakCondition; if (param.isDisplay) { System.out.println("k=" + k); System.out.println("attempts=" + attempts); System.out.println("criteriaBreakCondition=" + criteriaBreakCondition); System.out.println("The number of each classes are: "); for (int i = 0; i < k; i++) { System.out.print(centerCounts[i] + " "); } System.out.print("\n\n"); } return rvInfo; }}
5. KMeans主运行类
package com.dataMiningAlgorithms.kmeans;import kmeans.kmeans;import kmeans.kmeans_data;import kmeans.kmeans_param;/** 1. 2. KMeans主运行类 3. */public class KMeans { public static void main(String[] args) { //测试数据,四个二维的点 double[][] points = {{0, 0}, {4, 10}, {1, 1}, {5, 8}}; //初始化数据结构 kmeans_data data = new kmeans_data(points, 4, 2); //初始化参数结构 kmeans_param param = new kmeans_param(); //设置聚类中心点的初始化模式为随机模式 param.initCenterMehtod = kmeans_param.CENTER_RANDOM; //做kmeans计算,设置k为2 kmeans.doKmeans(2, data, param); //查看每个点的所属聚类标号 System.out.print("The labels of points is: "); for (int lable : data.labels) { System.out.print(lable + " "); } }}
6. 程序输出结果
k=2attempts=3criteriaBreakCondition=0.0The number of each classes are: 2 2 The labels of points is: 0 1 0 1
以上是Java对二维数据点简单
MatLab实现
在MatLab实现
1. MatLab主运行脚本
clear all;close all;clc;%第一类数据%均值mu1=[0 0 0]; %协方差S1=[0.3 0 0;0 0.35 0;0 0 0.3]; %产生高斯分布数据data1=mvnrnd(mu1,S1,100); %%第二类数据mu2=[1.25 1.25 1.25];S2=[0.3 0 0;0 0.35 0;0 0 0.3];data2=mvnrnd(mu2,S2,100);%第三个类数据mu3=[-1.25 1.25 -1.25];S3=[0.3 0 0;0 0.35 0;0 0 0.3];data3=mvnrnd(mu3,S3,100);%显示数据plot3(data1(:,1),data1(:,2),data1(:,3),'+');hold on;plot3(data2(:,1),data2(:,2),data2(:,3),'r+');plot3(data3(:,1),data3(:,2),data3(:,3),'g+');grid on;%三类数据合成一个不带标号的数据类%这里的data是不带标号的data=[data1;data2;data3];%k-means聚类%最后产生带标号的数据,标号在所有数据的最后,意思就是数据再加一维度[u re]=KMeans(data,3);[m n]=size(re);%最后显示聚类后的数据figure;hold on;for i=1:m if re(i,4)==1 plot3(re(i,1),re(i,2),re(i,3),'ro'); elseif re(i,4)==2 plot3(re(i,1),re(i,2),re(i,3),'go'); else plot3(re(i,1),re(i,2),re(i,3),'bo'); endendgrid on;
2. Kmeans脚本
%N是数据一共分多少类%data是输入的不带分类标号的数据%u是每一类的中心%re是返回的带分类标号的数据function [u re]=KMeans(data,N) [m n]=size(data); %m是数据个数,n是数据维数 ma=zeros(n); %每一维最大的数 mi=zeros(n); %每一维最小的数 u=zeros(N,n); %随机初始化,最终迭代到每一类的中心位置 for i=1:n ma(i)=max(data(:,i)); %每一维最大的数 mi(i)=min(data(:,i)); %每一维最小的数 for j=1:N u(j,i)=ma(i)+(mi(i)-ma(i))*rand(); %随机初始化,不过还是在每一维[min max]中初始化好些 end end while 1 pre_u=u; %上一次求得的中心位置 for i=1:N tmp{i}=[]; % 公式一中的x(i)-uj,为公式一实现做准备 for j=1:m tmp{i}=[tmp{i};data(j,:)-u(i,:)]; end end quan=zeros(m,N); %目标函数的实现 for i=1:m c=[]; for j=1:N c=[c norm(tmp{j}(i,:))]; end [junk index]=min(c); quan(i,index)=norm(tmp{index}(i,:)); end %距离公式的实现 for i=1:N for j=1:n u(i,j)=sum(quan(:,i).*data(:,j))/sum(quan(:,i)); end end if norm(pre_u-u)<0.1 %不断迭代直到位置不再变化 break; end end re=[]; for i=1:m tmp=[]; for j=1:N tmp=[tmp norm(data(i,:)-u(j,:))]; end [junk index]=min(tmp); re=[re;data(i,:) index]; endend
3. 实验结果
初始数据在三维空间中的状态显示:
4. 算法扩展
我们顺便也讲讲
一般的数据分析岗位都涉及到大数据集的分析建模,因此让
我们还是主要说说
柔性
在标准
学习算法一般情况下大致分为两种:有监督学习和无监督学习。简单地说,有监督学习需要使用类别标签,而无监督学习不用类别标签,
对半监督
5. 总结
最后总结一下吧,
6. 参考文献
[1] P. S. Bradley, K. P. Bennett, and A. Demiriz. "Constrained k-means clustering", Technical Report MSR-TR-2000-65, 2000.[2] E. Forgey. "Cluster analysis of multivariate data: Efficiency vs. interpretability of classification", Biometrics, 21, pp. 758, 1965.[3] H. P. Friedman and J. Rubin. "Onsome invariant criteria for grouping data", Journal of American Statistical Association, 62, pp. 1159-1178, 1967.[4] T. Kanungo, et al. "A local search approximation algorithm for k-means clustering", Computational Geometry: Theory and Application, 28(2004), pp. 89-112, 2004.
- 数据挖掘/机器学习算法--直接聚类算法(k-means)
- 数据挖掘十大经典算法学习之K均值(K-means)聚类算法
- 【数据挖掘】k-means聚类算法
- 数据挖掘 K-Means++聚类算法
- 数据挖掘算法学习(一)K-Means算法
- 数据挖掘算法学习(一)K-Means算法
- 机器学习:k-Means聚类算法
- 机器学习-K-means聚类算法
- 数据挖掘-K-means算法
- 数据挖掘算法-k-means
- 数据挖掘k-means聚类算法JAVA模拟
- 数据挖掘之聚类算法K-Means总结
- 数据挖掘之聚类算法K-Means总结_0
- 数据挖掘之聚类算法K-Means总结
- 机器学习算法 - k-means Clustering K均值聚类
- 机器学习--k均值聚类(k-means)算法
- 视觉机器学习算法之一k-means算法聚类
- 机器学习算法-k-means聚类算法
- truncate 和 delete 区别总结
- 一步步搭建视频直播系统,基于LFLiveKit+ijkplayer+rtmp(iOS端)
- 求两个数的最大公约数 C语言
- 5个方法有效优化调查问卷
- 指定&和@的页面统一显示的指令
- 数据挖掘/机器学习算法--直接聚类算法(k-means)
- C++使用protobuffer的一些坑
- 不懂win10重装win7?0基础教你自己动手重装电脑系统!
- Builder模式--合理分工,自由扩展(创建型模式04)
- vs内存泄露检测方法
- powerdesigner安装,破解文件
- HTML+CSS基础入门-第五天(HTML-超链接)
- PWM模式的库函数介绍
- 分布式存储 Minio