Mahout随机森林算法源码分析(1)--Describe

来源:互联网 发布:techrules 知乎 编辑:程序博客网 时间:2024/06/04 18:20

Mahout版本:0.7,hadoop版本:1.0.4,jdk:1.7.0_25 64bit。

Mahout中实现决策树算法的有两个(quick start),分别是Partial Implementation和Breiman Example,可以点击链接到相应的网页查看其官方实例。其中Breiman Example是单机版的,而Partial Implementation是可以使用map-reduce模式的。

Partial Implementation可以分为三步:Describe、BuildForest、TestForest,共称为决策树三部曲。以前有写过相关的内容,今次重新写这个算法的分析,应该会有一些更加深入的认知。本篇介绍三部曲之一Describe。

Describe在mahout-examples-0.7-job.jar包中的\org\apache\mahout\classifier\df\tools 路径下,在myeclipse中打开此文件,可以看到该类的源码。直接运行该类(含有main函数,可以直接运行),可以看到该类的使用指南:

Usage:                                                                           [--path <path> --file <file> --descriptor <descriptor1> [<descriptor2> ...]    --regression --help]                                                            Options                                                                           --path (-p) path                                 Data path                      --file (-f) file                                 Path to generated descriptor                                                    file                           --descriptor (-d) descriptor [descriptor ...]    data descriptor                --regression (-r)                                Regression Problem             --help (-h) 

该类主要的作用是把原始文件的描述写入一个文件。进入main函数,可以看到代码刚开始都是参数的传递,使用Option类来进行参数解析。然后就直接到了runTool()方法,这个是主要的操作,看到这个函数的参数有:dataPath(原始数据的输入路径),descriptor(对原始文件的描述,list),descPath(描述文件生成的路径),regression(是否是回归问题,由于这里做的是非回归问题,所以这个参数可以暂时忽略)。

采用的测试数据:glass.data,测试类:

package test.breiman;import java.io.IOException;import java.util.Arrays;import org.apache.hadoop.conf.Configuration;import org.apache.hadoop.fs.Path;import org.apache.mahout.classifier.df.data.DescriptorException;import org.apache.mahout.classifier.df.tools.Describe;import org.apache.mahout.common.HadoopUtil;public class DescribeFollow {/** * @param 测试Describe * @throws DescriptorException  * @throws IOException  */public static void main(String[] args) throws IOException, DescriptorException {String[] arg=new String[]{"-p","hdfs://ubuntu:9000/user/breiman/input/glass.data","-f","hdfs://ubuntu:9000/user/breiman/glass.info2","-d","I","9","N","L"};//System.out.println(arg[Arrays.asList(arg).indexOf("-f")+1]);HadoopUtil.delete(new Configuration(), new Path(arg[Arrays.asList(arg).indexOf("-f")+1]));Describe.main(arg);}}
首先简单介绍下输入数据:

1,1.52101,13.64,4.49,1.10,71.78,0.06,8.75,0.00,0.00,12,1.51761,13.89,3.60,1.36,72.73,0.48,7.83,0.00,0.00,1
71,1.51574,14.86,3.67,1.74,71.87,0.16,7.36,0.00,0.12,272,1.51848,13.64,3.87,1.27,71.96,0.54,8.32,0.00,0.32,2
每一个样本都有11个维度,第一维度是样本的编号(从1开始),最后一维是样本的标签。中间9个维度是样本的属性,都是数值型的。所以--descriptor参数设置为[I,9,N,L],I表示为忽视,是ignore的缩写,N是Numerical的缩写,L表示Label。当然如果维度中有非数值型的属性,也是可以的用C表示(Categorical的缩写)。9表示九个都是N,如果属性是这样的[Ignore,Numerical,Numerical,Categorical,Numerical,Categorical,Categorical,Label],那么--descriptor参数就应该写为下面的方式:[I,2,N,C,N,2,C,L]。

在runTool()里面的第一行设置断点,可以看到形参中的descriptor是:[I, 9, N, L]。在runTool中一共进行了四个操作:

String descriptor = DescriptorUtils.generateDescriptor(description);Path fPath = validateOutput(filePath);Dataset dataset = generateDataset(descriptor, dataPath, regression);DFUtils.storeWritable(new Configuration(), fPath, dataset);
其中的validateOutput应该可以忽略的(主要是判断输出文件是否存在而已,在DescribeFollow的时候不管输出文件是否存在都把它删除了,所以这里肯定是不存在问题的了)。那么generateDescriptor方法是做什么用的呢?就是一个转换,debug直接进行下一步,可以看到descriptor的值为:[I N N N N N N N N N L],就等于是把数字表示的全部转为字符了。generateDataset方法对应的是DataLoader.generateDataset(descriptor, regression, fs, path)这个方法,进入DataLoader类里面的这个方法。这个方法内主要进行了三个操作:

Attribute[] attrs = DescriptorUtils.parseDescriptor(descriptor);if (parseString(attrs, valsets, line, regression)) {        size++;      }List<String>[] values = new List[attrs.length];    for (int i = 0; i < valsets.length; i++) {      if (valsets[i] != null) {        values[i] = Lists.newArrayList(valsets[i]);      }    }
第一步是把descriptor转换为全拼,如下:[IGNORED, NUMERICAL, NUMERICAL, NUMERICAL, NUMERICAL, NUMERICAL, NUMERICAL, NUMERICAL, NUMERICAL, NUMERICAL, LABEL];第二步采用parseString方法去遍历所有的输入文件,看输入文件是否满足descriptor的描述,是则把行数加1,即样本数加1(glass.data的数据,size为214,符合原始数据的样本数);第三步是把标识全部取出来放入values中,values中的值为:[null, null, null, null, null, null, null, null, null, null, [3, 2, 1, 7, 6, 5]]至于values中的最后一个表示标识的为什么不是[1,2,3,5,6,7],是因为在parseString方式中这里的set是HashSet,采用随机存放的方式。该方法返回:

return new Dataset(attrs, values, size, regression);
返回一个dataset,这个dataSet中有属性、标识、样本数。

debug方式下看到的dataset如下:



到最后一步,DFUtils.storeWritable(new Configuration(), fPath, dataset);直接把dataset写入了文件,看这个方法:

public static void storeWritable(Configuration conf, Path path, Writable writable) throws IOException
里面的最后一个形参是writable的,但是我们传入的是dataset,可以么?看dataset的定义就可以了,看到DataSet是实现了Writable接口的,所以,这个是没有问题的。



分享,成长,快乐

转载请注明blog地址:http://blog.csdn.net/fansy1990