写mapreduce程序实现kmeans算法。我们的想法可能是

1. 次迭代后的质心

2. map里。计算每一个质心与样本之间的距离,得到与样本距离最短的质心,以这个质心作为key,样本作为value,输出

3. reduce里,输入的key是质心,value是其它的样本,这时又一次计算聚类中心,将聚类中心put到一个所有变量t中。

4. 在main里比較前一次的质心和本次的质心是否发生变化,假设变化,则继续迭代,否则退出。

本文的思路基本上是依照上面的步骤来做的,仅仅只是有几个问题须要解决

1. hadoop是不存在自己定义的全局变量的。所以上面定义一个全局变量存放质心的想法是实现不了的。所以一个替代的思路是将质心存放在文件里

2. 存放质心的文件在什么地方读取,假设在map中读取。那么能够肯定我们是不能用一个mapreduce实现一次迭代。所以我们选择在main函数里读取质心,然后将质心set到configuration中。configuration在map和reduce都是可读

3. 怎样比較质心是否发生变化,是在main里比較么,读取本次质心和上一次质心的文件然后进行比較。这样的方法是能够实现的,可是显得不够高富帅,这个时候我们用到了自己定义的counter,counter是全局变量,在map和reduce中可读可写,在上面的思路中,我们看到reduce是有上次迭代的质心和刚刚计算出来的质心的。所以直接在reduce中进行比較就全然能够。假设没发生变化,counter加1。仅仅要在main里比較获取counter的值即可了。

梳理一下,详细的过程例如以下

1. main函数读取质心文件

2. 将质心的字符串放到configuration中

3. 在mapper类重写setup方法,获取到configuration的质心内容。解析成二维数组的形式。代表质心

4. mapper类中的map方法读取样本文件,跟全部的质心比較。得出每一个样本跟哪个质心近期,然后输出<质心,样本>

5. reducer类中又一次计算质心,假设又一次计算出来的质心跟进来时的质心一致,那么自己定义的counter加1

6. main中获取counter的值,看是否等于质心,假设不相等,那么继续迭代,否在退出

详细的实现例如以下

1. pom依赖

这个要跟集群的一致。由于假设不一致在计算其它问题的时候没有问题。可是在使用counter的时候会出现故障

java.lang.IncompatibleClassChangeError: Found interface org.apache.hadoop.mapreduce.Counter, but class was expected

原因是:事实上从2.0開始。org.apache.hadoop.mapreduce.Counter从1.0版本号的class改为interface,能够看一下你导入的这个类是class还是interface,假设是class那么就是导包导入的不正确,须要改动

2.
样本

实例样本例如以下

1,1
2,2
3,3
-3,-3
-4,-4
-5,-5

3.
质心

这个质心是从样本中随机找的

1,1
2,2

4. 代码实现

首先定义一个Center类,这个类主要存放了质心的个数k,还有两个从hdfs上读取质心文件的方法,一个用来读取初始的质心。这个实在文件里,另一个是用来读取每次迭代后的质心目录,这个是在目录中的,代码例如以下

Center类

public class Center {

	protected static int k = 2;		//质心的个数

	/**
* 从初始的质心文件里载入质心,并返回字符串。质心之间用tab切割
* @param path
* @return
* @throws IOException
*/
public String loadInitCenter(Path path) throws IOException { StringBuffer sb = new StringBuffer(); Configuration conf = new Configuration();
FileSystem hdfs = FileSystem.get(conf);
FSDataInputStream dis = hdfs.open(path);
LineReader in = new LineReader(dis, conf);
Text line = new Text();
while(in.readLine(line) > 0) {
sb.append(line.toString().trim());
sb.append("\t");
} return sb.toString().trim();
} /**
* 从每次迭代的质心文件里读取质心,并返回字符串
* @param path
* @return
* @throws IOException
*/
public String loadCenter(Path path) throws IOException { StringBuffer sb = new StringBuffer(); Configuration conf = new Configuration();
FileSystem hdfs = FileSystem.get(conf);
FileStatus[] files = hdfs.listStatus(path); for(int i = 0; i < files.length; i++) { Path filePath = files[i].getPath();
if(!filePath.getName().contains("part")) continue;
FSDataInputStream dis = hdfs.open(filePath);
LineReader in = new LineReader(dis, conf);
Text line = new Text();
while(in.readLine(line) > 0) {
sb.append(line.toString().trim());
sb.append("\t");
}
} return sb.toString().trim();
}
}

KmeansMR类

public class KmeansMR {

	private static String FLAG = "KCLUSTER";

	public static class TokenizerMapper
extends Mapper<Object, Text, Text, Text>{ double[][] centers = new double[Center.k][];
String[] centerstrArray = null; @Override
public void setup(Context context) { //将放在context中的聚类中心转换为数组的形式。方便使用
String kmeansS = context.getConfiguration().get(FLAG);
centerstrArray = kmeansS.split("\t");
for(int i = 0; i < centerstrArray.length; i++) {
String[] segs = centerstrArray[i].split(",");
centers[i] = new double[segs.length];
for(int j = 0; j < segs.length; j++) {
centers[i][j] = Double.parseDouble(segs[j]);
}
}
} public void map(Object key, Text value, Context context
) throws IOException, InterruptedException { String line = value.toString();
String[] segs = line.split(",");
double[] sample = new double[segs.length];
for(int i = 0; i < segs.length; i++) {
sample[i] = Float.parseFloat(segs[i]);
}
//求得距离近期的质心
double min = Double.MAX_VALUE;
int index = 0;
for(int i = 0; i < centers.length; i++) {
double dis = distance(centers[i], sample);
if(dis < min) {
min = dis;
index = i;
}
} context.write(new Text(centerstrArray[index]), new Text(line));
}
} public static class IntSumReducer
extends Reducer<Text,Text,NullWritable,Text> { Counter counter = null; public void reduce(Text key, Iterable<Text> values,
Context context
) throws IOException, InterruptedException { double[] sum = new double[Center.k];
int size = 0;
//计算相应维度上值的加和。存放在sum数组中
for(Text text : values) {
String[] segs = text.toString().split(",");
for(int i = 0; i < segs.length; i++) {
sum[i] += Double.parseDouble(segs[i]);
}
size ++;
} //求sum数组中每一个维度的平均值。也就是新的质心
StringBuffer sb = new StringBuffer();
for(int i = 0; i < sum.length; i++) {
sum[i] /= size;
sb.append(sum[i]);
sb.append(",");
} /**推断新的质心跟老的质心是否是一样的*/
boolean flag = true;
String[] centerStrArray = key.toString().split(",");
for(int i = 0; i < centerStrArray.length; i++) {
if(Math.abs(Double.parseDouble(centerStrArray[i]) - sum[i]) > 0.00000000001) {
flag = false;
break;
}
}
//假设新的质心跟老的质心是一样的,那么相应的计数器加1
if(flag) {
counter = context.getCounter("myCounter", "kmenasCounter");
counter.increment(1l);
}
context.write(null, new Text(sb.toString()));
}
} public static void main(String[] args) throws Exception { Path kMeansPath = new Path("/dsap/middata/kmeans/kMeans"); //初始的质心文件
Path samplePath = new Path("/dsap/middata/kmeans/sample"); //样本文件
//载入聚类中心文件
Center center = new Center();
String centerString = center.loadInitCenter(kMeansPath); int index = 0; //迭代的次数
while(index < 5) { Configuration conf = new Configuration();
conf.set(FLAG, centerString); //将聚类中心的字符串放到configuration中 kMeansPath = new Path("/dsap/middata/kmeans/kMeans" + index); //本次迭代的输出路径。也是下一次质心的读取路径 /**推断输出路径是否存在。假设存在,则删除*/
FileSystem hdfs = FileSystem.get(conf);
if(hdfs.exists(kMeansPath)) hdfs.delete(kMeansPath); Job job = new Job(conf, "kmeans" + index);
job.setJarByClass(KmeansMR.class);
job.setMapperClass(TokenizerMapper.class);
job.setReducerClass(IntSumReducer.class);
job.setOutputKeyClass(NullWritable.class);
job.setOutputValueClass(Text.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
FileInputFormat.addInputPath(job, samplePath);
FileOutputFormat.setOutputPath(job, kMeansPath);
job.waitForCompletion(true); /**获取自己定义counter的大小,假设等于质心的大小。说明质心已经不会发生变化了,则程序停止迭代*/
long counter = job.getCounters().getGroup("myCounter").findCounter("kmenasCounter").getValue();
if(counter == Center.k) System.exit(0);
/**又一次载入质心*/
center = new Center();
centerString = center.loadCenter(kMeansPath); index ++;
}
System.exit(0);
} public static double distance(double[] a, double[] b) { if(a == null || b == null || a.length != b.length) return Double.MAX_VALUE;
double dis = 0;
for(int i = 0; i < a.length; i++) {
dis += Math.pow(a[i] - b[i], 2);
}
return Math.sqrt(dis);
}
}

5. 结果

产生了两个目录。各自是第一次、第二次迭代后的聚类中心

最后的聚类中心的内容例如以下

版权声明:本文博客原创文章。博客,未经同意,不得转载。

hadoop在实现kmeans算法——一个mapreduce实施的更多相关文章

  1. 利用Mahout实现在Hadoop上运行K-Means算法

    利用Mahout实现在Hadoop上运行K-Means算法 一.介绍Mahout Mahout是Apache下的开源机器学习软件包,目前实现的机器学习算法主要包含有协同过滤/推荐引擎,聚类和分类三个部 ...

  2. 一起学Hadoop——使用IDEA编写第一个MapReduce程序(Java和Python)

    上一篇我们学习了MapReduce的原理,今天我们使用代码来加深对MapReduce原理的理解. wordcount是Hadoop入门的经典例子,我们也不能免俗,也使用这个例子作为学习Hadoop的第 ...

  3. 在Hadoop上运行基于RMM中文分词算法的MapReduce程序

    原文:http://xiaoxia.org/2011/12/18/map-reduce-program-of-rmm-word-count-on-hadoop/ 在Hadoop上运行基于RMM中文分词 ...

  4. 腾讯公司数据分析岗位的hadoop工作 线性回归 k-means算法 朴素贝叶斯算法 SpringMVC组件 某公司的广告投放系统 KNN算法 社交网络模型 SpringMVC注解方式

    腾讯公司数据分析岗位的hadoop工作 线性回归 k-means算法 朴素贝叶斯算法 SpringMVC组件 某公司的广告投放系统 KNN算法 社交网络模型 SpringMVC注解方式 某移动公司实时 ...

  5. mahout运行测试与kmeans算法解析

    在使用mahout之前要安装并启动hadoop集群 将mahout的包上传至linux中并解压即可 mahout下载地址: 点击打开链接 mahout中的算法大致可以分为三大类: 聚类,协同过滤和分类 ...

  6. mahout运行测试与数据挖掘算法之聚类分析(一)kmeans算法解析

    在使用mahout之前要安装并启动hadoop集群 将mahout的包上传至linux中并解压即可 mahout下载地址: 点击打开链接 mahout中的算法大致可以分为三大类: 聚类,协同过滤和分类 ...

  7. mahout中KMeans算法

    本博文主要内容有   1.kmeans算法简介 2.kmeans执行过程  3.关于查看mahout中聚类结果的一些注意事项 4.kmeans算法图解      5.mahout的kmeans算法实现 ...

  8. [数据挖掘] - 聚类算法:K-means算法理解及SparkCore实现

    聚类算法是机器学习中的一大重要算法,也是我们掌握机器学习的必须算法,下面对聚类算法中的K-means算法做一个简单的描述: 一.概述 K-means算法属于聚类算法中的直接聚类算法.给定一个对象(或记 ...

  9. K-Means算法的10个有趣用例

    https://www.jianshu.com/p/162c9ec713cf 摘要: 让我们走进K-Means算法的“前世今生”以及和它有关的十个有趣的应用案例. K-means算法具有悠久的历史,并 ...

随机推荐

  1. MySqlClient访问tinyint字段返回布尔值

    原文 MySqlClient访问tinyint字段返回布尔值 症状: 使用MySqlClient访问tinyint       unsign 字段返回布尔值 true 和        false,但 ...

  2. const限定符的作用

    const限定符的作用:                                     1.定义const常量:const可以将一个对象变成一个常量,不可被修改,所以定义的 时候必须进行初始 ...

  3. CPU 球迷助威清理灰尘图形的全过程

    主机因为使用时间长的电源风扇,风扇轴承石油枯竭,导致拒绝或不转的风扇转速,热量使电源不能得到有效排除,往往会造成电脑死机,有几种方法来解决. 单省钱的办法例如以下: 1.把电源从主机上拆下,例如以下图 ...

  4. jsp中将后台传递过来的json格式的list数据绑定到下拉菜单select

    <span style="white-space:pre"> </span> <select><c:forEach var="f ...

  5. eclipse如何查看类之间的引用关系

    今天遇到这个问题:mark一点点: 在类名上单击右键.选择Reference->Workingspace快捷克债券Ctrl+Shift+G 版权声明:本文博客原创文章,博客,未经同意,不得转载.

  6. iotop 分析系统那些进程占用io资源

    iotop -b -o  -t  -qqq >> /tmp/iotop.log 1.直接yum安装,rh6的光盘里有包. yum install iotop   2.命令参数介绍   -o ...

  7. Java编码浅析(注意区分三个概念)(转)

    编码: (1)外部资源的字符集-----没有读入jvm中的数据都是外部资源 (2)jvm中数据的字符集-----都是unicode (1)和(2)之间发生交互时,如果不指定编码,则使用JVM平台默认字 ...

  8. 14.3.2.3 Consistent Nonlocking Reads 一致性非锁定读

    14.3.2.3 Consistent Nonlocking Reads 一致性非锁定读 一致性读 意味着 InnoDB 使用多版本来保护查询一个数据库在当前时间点的快照. 查询看到被事务做出的修改, ...

  9. Codeforces 475D CGCDSSQ 求序列中连续数字的GCD=K的对数

    题目链接:点击打开链接 #include <cstdio> #include <cstring> #include <algorithm> #include < ...

  10. mina的编码和解码以及断包的处理,发送自己定义协议,仿qq聊天,发送xml或json

    近期一段时间以来,mina非常火,和移动开发一样.异常的火爆.前面写了几篇移动开发的文章,都还不错.你们的鼓舞就是我最大的动力.好了,废话少说.我们来看下tcp通讯吧. tcp通讯对于java来说是非 ...