决策树Partial Implementation源码的第二部分为:BuildForest,其源码所在位置为:MAHOUT_HOME/example/src/main/java/org/apache/mahout/classifier/df/mapreduce/BuildForest.java,现对其进行简要分析:
打开源码可以看到,BuildForest有如下四个步骤:
(1)创建一个DecisionTreeBuilder对象,并设置四个参数:
DecisionTreeBuilder treeBuilder = new DecisionTreeBuilder();
treeBuilder.setM(m);
treeBuilder.setComplemented(complemented);
treeBuilder.setMinSplitNum(minSplitNum);
treeBuilder.setMinVarianceProportion(minVarianceProportion)
(2)新建Builder接口,此处因为是并行参数(-p)所以使用PartialBuilder实现类:
Builder forestBuilder = new PartialBuilder(treeBuilder, dataPath, datasetPath, seed, getConf());
初始化的ParitalBuilder类的参数为:treeBuilder(DecisionTreeBuilder),这个参数只设置了四个参数,其他都未设置;
dataPath:输入训练数据的路径;datasetPath:对训练数据的描述文件的路径;seed:初始种子(暂时未深究);
getConf():各种配置参数(个人猜测);
(3)创建森林,设置树的个数:
DecisionForest forest = forestBuilder.build(nbTrees);
nbTrees表示要创建树的个数;
(4)把上面创建的森林写入HDFS:
DFUtils.storeWritable(getConf(), forestPath, forest);
上面四个步骤是BuildForest的四个主要操作,其他都是一些参数设置之类的辅助操作。对于上面四个步骤,其中第三歩,调用build方法最为复杂,现简要分析如下:
Builder是抽象类,定义了build非抽象方法,实现类PartialBuilder不用覆写此方法,而直接调用其父类(Builder)的build方法,Builder的build方法可分为如下几步:
<1>设置参数,包括seed,nbTrees,treeBuilder:
setRandomSeed(conf, seed);
setNbTrees(conf, nbTrees);
setTreeBuilder(conf, treeBuilder);
<2>把训练数据的描述文件放入内存中(这样理解是否?个人理解就是类似全局变量的作用):
DistributedCache.addCacheFile(datasetPath.toUri(), conf);
<3>新建Job任务,名字为‘decision forest builder’:
Job job = new Job(conf, "decision forest builder");
<4>对新建的job进行配置:
configureJob(job);
<5>提交任务,等待结果:
runJob(job){
return job.waitForCompletion(true);
}
<6>调用parseOutput方法得到森林(forest),删除输出文件,返回结果:
DecisionForest forest = parseOutput(job);
HadoopUtil.delete(conf, outputPath);
return forest;
这里为什么要删除outputPath呢?下面再说。
其中第<4>、<6>步在Builder中都是抽象方法,由其实现类进行覆写实现,在PartialBuilder中的configureJob()和parseOutput()如下:
cofigureJob():设置这个job的相关参数,Mapper为Step1Mapper,输出Key和Value类型分别为:TreeID、MapredOutput,输入文件为dataPath,输出文件为outputPath;
FileInputFormat.setInputPaths(job, getDataPath());
FileOutputFormat.setOutputPath(job, getOutputPath(conf));
job.setOutputKeyClass(TreeID.class);
job.setOutputValueClass(MapredOutput.class);
job.setMapperClass(Step1Mapper.class);
这里输出文件的路径设置为outputPath,所以在上面第<6>步中要删除这个文件;
parseOutput():其操作如下:
Node[] trees = new Node[numTrees];
processOutput(job, outputPath, keys, trees);
return new DecisionForest(Arrays.asList(trees));
processOutput():操作如下:
for (Pair<TreeID,MapredOutput> record : new SequenceFileIterable<TreeID, MapredOutput>(path, conf)) {
MapredOutput value = record.getSecond();
if (trees != null) {
trees[index] = value.getTree();
}
index++;
}
processOutput的操作也就是把输出文件的内容全部读入trees(Node[])中,然后以trees为参数建立一个DecisionForest返回;
DecisionForest返回过程如下:DecisionForest-->processOutput(PartialBuilder)-->parseOutput(PartialBuilder)-->build(PartialBuilder);
在build方法中的<3>、<4>、<5>步是整个BuilderForest的关键所在,所涉及到的类目前可以很明显的看到的有:TreeID(map输出Key类型)、MapredOutput(map输出Value类型)、Step1Mapper(job的Mapper操作);
<3><4><5>步下次再看。。
分享,快乐,成长
转载请注明出处:http://blog.csdn.net/fansy1990