hadoop学习--K-Means(聚类算法)

本例子介绍使用hadoop做聚类分析。通过mapreduce实现KMeans算法。

1、KMeans算法介绍:

k-means 算法接受参数 k ;然后将事先输入的n个数据对象划分为 k个聚类以便使得所获得的聚类满足:同一聚类中的对象相似度较高;而不同聚类中的对象相似度较小。聚类相似度是利用各聚类中对象的均值所获得一个“中心对象”(引力中心)来进行计算的。
K-means算法是最为经典的基于划分的聚类方法,是十大经典数据挖掘算法之一。K-means算法的基本思想是:以空间中k个点为中心进行聚类,对最靠近他们的对象归类。通过迭代的方法,逐次更新各聚类中心的值,直至得到最好的聚类结果。
假设要把样本集分为c个类别,算法描述如下:
(1)适当选择c个类的初始中心;
(2)在第k次迭代中,对任意一个样本,求其到c各中心的距离,将该样本归到距离最短的中心所在的类;
(3)利用均值等方法更新该类的中心值;
(4)对于所有的c个 聚类中心,如果利用(2)(3)的 迭代法更新后,值保持不变,则迭代结束,否则继续迭代。
该 算法的最大优势在于简洁和快速。算法的关键在于初始中心的选择和距离公式。
这里的测试数据使用坐标数据,找到各个坐标对应的聚类中心,数据格式如下:
(1,1)
(2,2)
(99,99)
(100,100)
(101,101)
2、实现过程

step1:随机选取K个点,作为初始中心;

step2:对于任意一个坐标,求其到各个中心点的距离,并将其归类到最短的中心所在类中;

step3:累加类中的各个点坐标求均值,作为新的中心点;

step4:如果中心点保持不变,则结束。否则继续step2。

3、代码:

[java] view plain copy
  1. import java.io.IOException;  
  2. import java.util.StringTokenizer;  
  3. import java.io.ByteArrayInputStream;    
  4. import java.io.ByteArrayOutputStream;  
  5. import java.net.URI;  
  6.   
  7. import org.apache.hadoop.fs.FSDataInputStream;  
  8. import org.apache.hadoop.conf.Configuration;  
  9. import org.apache.hadoop.fs.Path;  
  10. import org.apache.hadoop.io.Text;  
  11. import org.apache.hadoop.fs.FileSystem;  
  12. import org.apache.hadoop.io.IOUtils;  
  13. import org.apache.hadoop.mapreduce.Job;  
  14. import org.apache.hadoop.mapreduce.Mapper;  
  15. import org.apache.hadoop.mapreduce.Reducer;  
  16. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;  
  17. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;  
  18. import org.apache.hadoop.util.GenericOptionsParser;  
  19. import org.apache.hadoop.io.IntWritable;  
  20. import org.apache.hadoop.io.LongWritable;  
  21.   
  22. public class KMeans {  
  23.     public static String[] centerlist;  
  24.     public static int k = 0;//K 个数  
  25.     public static class MapClass   
  26.         extends Mapper<LongWritable, Text, Text, Text> {  
  27.           
  28.         private final static IntWritable one = new IntWritable(1);  
  29.         private Text word = new Text();   
  30.         public void map(LongWritable key, Text value,  
  31.                         Context context ) throws IOException,  
  32.                         InterruptedException {  
  33.             StringTokenizer itr = new StringTokenizer(value.toString());    
  34.         while(itr.hasMoreTokens())    
  35. <span style="white-space:pre">  </span>    {    
  36.         String outValue = new String(itr.nextToken());  
  37.     
  38.         String[] list = outValue.replace("(""").replace(")""").split(",");    
  39.         String[] c = centerlist[0].replace("(""").replace(")""").split(",");    
  40.         float min = 0;    
  41.         int pos = 0;    
  42.         for(int i=0;i<list.length;i++)    
  43.         {    
  44.             min += (float) Math.pow((Float.parseFloat(list[i]) - Float.parseFloat(c[i])),2);    
  45.         }    
  46.         for(int i=0;i<centerlist.length;i++)    
  47.         {    
  48.              String[] centerStrings = centerlist[i].replace("(""").replace(")""").split(",");    
  49.              float distance = 0;    
  50.              for(int j=0;j<list.length;j++)    
  51.                  distance += (float) Math.pow((Float.parseFloat(list[j]) - Float.parseFloat(centerStrings[j])),2);    
  52.              if(min>distance)    
  53.              {    
  54.                         min=distance;    
  55.                         pos=i;    
  56.                     }    
  57.                 }    
  58.                 context.write(new Text(centerlist[pos]), new Text(outValue));    
  59.             }    
  60.         }  
  61.     }  
  62.       
  63.     public static class Reduce extends Reducer<Text, Text, Text, Text> {  
  64.           
  65.         public void reduce(Text key, Iterable<Text> values,  
  66.                            Context context) throws IOException,InterruptedException {  
  67.                              
  68.             String outVal = "";    
  69.             int count=0;    
  70.             String center="";    
  71.             int length = key.toString().replace("(""").replace(")""").replace(":""").split(",").length;    
  72.             float[] ave = new float[Float.SIZE*length];    
  73.             for(int i=0;i<length;i++)    
  74.                 ave[i]=0;     
  75.             for(Text val:values)    
  76.             {    
  77.                 outVal += val.toString()+" ";    
  78.                 String[] tmp = val.toString().replace("(""").replace(")""").split(",");    
  79.                 for(int i=0;i<tmp.length;i++)    
  80.                     ave[i] += Float.parseFloat(tmp[i]);    
  81.                 count ++;    
  82.             }    
  83.             for(int i=0;i<length;i++)    
  84.             {    
  85.                 ave[i]=ave[i]/count;    
  86.                 if(i==0)    
  87.                     center += "("+ave[i]+",";    
  88.                 else {    
  89.                     if(i==length-1)    
  90.                         center += ave[i]+")";    
  91.                     else {    
  92.                         center += ave[i]+",";    
  93.                     }    
  94.                 }    
  95.             }    
  96.             System.out.println(center);    
  97.             context.write(key, new Text(outVal+center));  
  98.         }  
  99.     }  
  100.     public static void CenterInitial(String strInPath,String[] strcen) throws IOException{  
  101.         String[] list;    
  102.         String inpath = strInPath;   
  103.         Configuration conf = new Configuration(); //读取hadoop文件系统的配置    
  104.         conf.set("hadoop.job.ugi""hadoop,hadoop");     
  105.         FileSystem fs = FileSystem.get(URI.create(inpath),conf); //FileSystem是用户操作HDFS的核心类,它获得URI对应的HDFS文件系统     
  106.         FSDataInputStream in = null;     
  107.         ByteArrayOutputStream out = new ByteArrayOutputStream();    
  108.         try{     
  109.              
  110.             in = fs.open( new Path(inpath) );     
  111.             IOUtils.copyBytes(in,out,50,false);  //用Hadoop的IOUtils工具方法来让这个文件的指定字节复制到标准输出流上     
  112.             list = out.toString().split("\n");    
  113.         } finally {     
  114.             IOUtils.closeStream(in);  
  115.             out.close();    
  116.         }  
  117.         for(int i = 0;i < strcen.length;i++)  
  118.         {  
  119.             strcen[i] = list[i];  
  120.         }  
  121.           
  122.     }  
  123.     public static float NewCenter(String strOutPath) throws IOException{  
  124.         String[] list;  
  125.         float should = Integer.MIN_VALUE ;  
  126.         Configuration conf = new Configuration(); //读取hadoop文件系统的配置    
  127.         conf.set("hadoop.job.ugi""hadoop,hadoop");     
  128.         FileSystem fs = FileSystem.get(URI.create(strOutPath + "/part-r-00000"),conf); //FileSystem是用户操作HDFS的核心类,它获得URI对应的HDFS文件系统     
  129.         FSDataInputStream in = null;     
  130.         ByteArrayOutputStream out = new ByteArrayOutputStream();    
  131.         try{     
  132.              
  133.             in = fs.open( new Path(strOutPath + "/part-r-00000") );     
  134.             IOUtils.copyBytes(in,out,50,false);  //用Hadoop的IOUtils工具方法来让这个文件的指定字节复制到标准输出流上     
  135.             list = out.toString().split("\n");    
  136.         } finally {     
  137.             IOUtils.closeStream(in);  
  138.             out.close();    
  139.         }  
  140.           
  141.         for(int i = 0;i < k;i++){  
  142.             String[] l = list[i].replace("\t"" ").split(" ");  
  143.             String[] oldcenter = l[0].replace("(""").replace(")""").split(","); //原先的中心点  
  144.             String[] finalcenter = l[l.length-1].replace("(""").replace(")""").split(",");//新的中心点  
  145.             centerlist[i] = l[l.length-1];      //保存最新的中心点  
  146.             float tmp = 0;  
  147.             for(int j = 0; j< oldcenter.length;j++){  
  148.                 tmp+=Math.pow(Float.parseFloat(oldcenter[j]) - Float.parseFloat(finalcenter[j]),2 );  
  149.             }  
  150.             if(should <= tmp)  
  151.                 should = tmp;  
  152.         }  
  153.         System.out.println("New Center...");  
  154.         return should;  
  155.           
  156.     }  
  157.     public static void main(String[] args) throws Exception {   
  158.   
  159.         Configuration conf = new Configuration();  
  160.         conf.set("fs.default.name""hdfs://localhost:9000");  
  161.         int times = 0;  
  162.         String[] otherArgs = new GenericOptionsParser(conf,args).getRemainingArgs();  
  163.         if(otherArgs.length != 3){  
  164.             System.err.println("Usage: KMeans K <in> <out>");  
  165.             System.exit(2);  
  166.         }  
  167.         k = Integer.parseInt(args[0]);  
  168.         String[] strcen = new String[k];  
  169.         centerlist = strcen;  
  170.         CenterInitial(args[1], centerlist);     //初始化中心  
  171.         double s = 0;  
  172.         double shold = 0.0001;  
  173.         do{  
  174.             Job job = new Job(conf, "KMeans");  
  175.             job.setJarByClass(KMeans.class);  
  176.             job.setMapperClass(MapClass.class);  
  177.             job.setReducerClass(Reduce.class);  
  178.               
  179.             job.setOutputKeyClass(Text.class);  
  180.             job.setOutputValueClass(Text.class);  
  181.               
  182.             FileSystem fs = FileSystem.get(conf);       //每次循环,都删除输出目录    
  183.             fs.delete(new Path(args[2]),true);  
  184.             FileInputFormat.setInputPaths(job, new Path(otherArgs[1]));  
  185.             FileOutputFormat.setOutputPath(job, new Path(otherArgs[2]));  
  186.             if(job.waitForCompletion(true))    
  187.             {    
  188.                 s = NewCenter(args[2]);   
  189.                 times++;  
  190.             }  
  191.         }while(s> shold);  
  192.         System.out.println("Iterator: " + times);   //迭代次数,即重复计算中心点的次数  
  193.         //System.exit(job.waitForCompletion(true) ? 0 : 1);  
  194.     }  
  195. }  
4、代码分析

CenterInitial函数:初始化中心点,这里以输入文件的前k个点作为初始中心;例如k=2,则(1,1) (2,2)就是初始中心;

map:计算每个点到各个中心的距离,并将其归类;

第一计算后,map的输出:

(1,1)<span style="white-space:pre">	</span>(1,1)
(2,2)<span style="white-space:pre">	</span>(2,2) (99,99) (100,100) (101,101)

reduce:计算每个聚类中新的中心点,并输出到part-r-00000;

第一次reduce之后:

(1,1)<span style="white-space: pre;">	</span>(1,1) (1,1)
(2,2)<span style="white-space: pre;">	</span>(2,2) (99,99) (100,100) (101,101) (75.5,75.5)

Newcenter函数:根据reduce计算出的新的中心点与原来的中心作比较,这里的比较也就是2点之间距离的计算,如果距离很小,基本可以判定聚类中心不再变化
该函数把reduce的结果中新的中心点(第一次计算时,把(1,1) (75.5,75.5) )存到centerlist中。作为下次map归类的根据。

这里的centerlist是KMeans的String变量数组,相当于全局变量。。。如果中心点比较多,数据量比较大的话,也可以把这个结果写到hdfs文件中,每次都从文件中读取。

这里的main函数是一个循环,每次map reduce 结束之前,都要删除上一次输出的结果。

5、运行参数

2 input.txt output

这里的 2 表示 2个聚类中心。

运行结果:

(1.5,1.5)<span style="white-space:pre">	</span>(1,1) (2,2) (1.5,1.5)
(100.0,100.0)<span style="white-space:pre">	</span>(99,99) (100,100) (101,101) (100.0,100.0)

源代码:https://github.com/y521263/Hadoop_in_Action

参考资料:

http://blog.csdn.net/shizhixin/article/details/8968977

http://cloudcomputing.ruc.edu.cn/

你可能感兴趣的:(hadoop学习--K-Means(聚类算法))