其实如果只是要获得输入数据那么,可以使用mahout官网提供的方法在得到了序列的*.txt文件后直接把mahout-distribution-0.7.zip解压拷贝到虚拟机,(在/etc/profile里面配置下hadoop_home变量)然后找到mahout_home/bin目录,执行 chmod +x mahout ,然后分别执行
./mahout seqdirectory -i <input> -o <output>
./mahout seq2sparse -i <output>/chunk-0 -o <output-in>上面的<input>、<output>对应于自己的输入和输出;我所使用的数据并不是 Reuters dataset的全部数据,而只是前三个:reut2-000.sgm、 reut2-001.sgm、reut2-002.sgm,这样的数据在经过ExtractReuters 后生成了3000个文件,然后经过seqdirectory合并成了一个2.41M的数据文件。seq2sparse有7个job,每个job负责自己的内容,这个暂时不加分析;最终的结果在<output-in>/tfidf-vectors里面,即为输入数据;
有了输入数据就可以直接跑程序了,首先不管程序是什么样子的,先跑出结果再说:
package mahout.test.canopy; import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.mahout.clustering.canopy.CanopyDriver; import org.apache.mahout.common.distance.DistanceMeasure; import org.apache.mahout.common.distance.EuclideanDistanceMeasure; public class CanopyTest { public static void main(String[] args) throws ClassNotFoundException, IOException, InterruptedException { Configuration conf =new Configuration(); conf.set("mapred.job.tracker", "192.168.128.138:9001"); Path input=new Path("hdfs://hadoop:9000/user/hadoop/output/canopyvec/tfidf-vectors"); Path output=new Path("hdfs://hadoop:9000/user/hadoop/output/canopy-output"); DistanceMeasure measure=new EuclideanDistanceMeasure(); CanopyDriver.buildClusters(conf, input, output, measure, 33.1, 22.1, 3, false); System.out.println("job is done."); } }最开始的时候我的t1、t2设置为3.1、2.1结果map出来的结果数是为零(这个暂时也不知道是代表什么意思),后来改为上面的结果可以看到map输出了509条记录,reduece输出3条记录(符合参数clusterFileter的设定值:3),最后的输出为:canopy-output/clusters-0-final/part-r-00000。
为了便于后面的观察,所以不使用上面的数据,而使用自己造的数据,造数据之前首先要知道输入数据的格式,那么使用下面的代码看下输入数据是什么:
package mahout.test.utils; import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.Mapper; import org.apache.hadoop.mapreduce.lib.input.SequenceFileInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; import org.apache.hadoop.util.ToolRunner; import org.apache.mahout.common.AbstractJob; import org.apache.mahout.math.Vector; import org.apache.mahout.math.VectorWritable; /** * 读取序列文件,输入Key:Text,Value:VectorWritable * @author fansy * @version 2013/7/21 11:32 * */ public class ReadTextVectorWritable extends AbstractJob { @Override public int run(String[] arg0) throws Exception { if(arg0.length!=6||(!"-i".equals(arg0[0])||(!"-o".equals(arg0[2]))||(!"-jt".equals(arg0[4])))){ System.err.println("参数不正确!"); System.out.println("Usage:"); System.out.println("-i <input> -o <output> -jt <jobtracker:port>"); System.exit(-1); } Configuration conf=new Configuration(); conf.set("mapred.job.tracker", arg0[5]); Job job=new Job(conf); job.setJobName("readTextVector with Input:"+arg0[1]); job.setJarByClass(ReadTextVectorWritable.class); job.setInputFormatClass(SequenceFileInputFormat.class); job.setMapperClass(RM.class); job.setMapOutputKeyClass(Text.class); job.setMapOutputValueClass(Text.class); job.setNumReduceTasks(0); SequenceFileInputFormat.addInputPath(job, new Path(arg0[1])); FileOutputFormat.setOutputPath(job, new Path(arg0[3])); return job.waitForCompletion(true)? 0:-1; } public static class RM extends Mapper<Text,VectorWritable,Text,Text>{ public void map(Text key,VectorWritable value,Context context)throws InterruptedException,IOException{ Vector vector=value.get(); context.write(key, new Text(vector.asFormatString())); } } public static void main(String[] args) throws Exception{ ToolRunner.run(new Configuration(), new ReadTextVectorWritable(), args); } }然后查看hdfs上面的输入数据,如下:
1 {1:3.45,2:4.67,3:2.34} 2 {1:4.65,3:4.62,3:4.34} 3 {1:5.95,5:4.67,3:2.24}第一个代表一个样本号,冒号前面代表维度,冒号后面代表相应维度的大小。猜测:对于reuters数据,应该是把所有的单词全部排序,然后按照一定的规则进行编码(可以是从零到n的编码,所以冒号前面的数字就代表某一个单词),后面的数值应该是该单词在文件中的重要性(?这个应该是有一个pagerank的算法之类的);
有了上面的想法后就可以直接构造一定的输入数据(自己构造少量数据就可以对算法有很清晰的认识),然后把canopy算法的代码改为java代码(不使用hadoop就可以跑的代码),然后就可以进行调试分析其算法逻辑了。
(未完待续。。。)
分享,快乐,成长
转载请注明出处:http://blog.csdn.net/fansy1990