热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

Mahout版本:0.7,hadoop版本:1.0.4,jdk:1.7.0_2564bit。今天到BuildForest的主要Mapper操作,前面也说到BuildForest主要的操作都在Mapp

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

今天到BuildForest的主要Mapper操作,前面也说到BuildForest主要的操作都在Mapper里面,而reducer是没有的。本篇介绍其Mapper,Step1Mapper。首先贴上其仿制代码,如下:

package mahout.fansy.partial;

import java.io.IOException;
import java.util.List;
import java.util.Random;

import mahout.fansy.utils.read.ReadText;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.filecache.DistributedCache;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.mahout.classifier.df.Bagging;
import org.apache.mahout.classifier.df.builder.DecisionTreeBuilder;
import org.apache.mahout.classifier.df.data.Data;
import org.apache.mahout.classifier.df.data.DataConverter;
import org.apache.mahout.classifier.df.data.Dataset;
import org.apache.mahout.classifier.df.data.Instance;
import org.apache.mahout.classifier.df.mapreduce.Builder;
import org.apache.mahout.classifier.df.mapreduce.MapredOutput;
import org.apache.mahout.classifier.df.mapreduce.partial.TreeID;
import org.apache.mahout.classifier.df.node.Node;
import org.apache.mahout.common.RandomUtils;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;

/**
* Step1Mapper的仿造代码
* @author fansy
*/
public class Step1MapperFollow {
private DataConverter converter;
private Random rng;
private int nbTrees;
private int firstTreeId;
private int partition;
private final List instances = Lists.newArrayList();
private Configuration conf;
private Path datasetPath ;
private Path input;
// private Path output;
private List values;
private Dataset dataset;
private DecisionTreeBuilder treeBuilder;
private int m; // selection

public static void main(String[] args) throws IOException, InterruptedException{
Step1MapperFollow s1m=new Step1MapperFollow();
s1m.init();
s1m.setup();
s1m.map();
s1m.cleanup();
}
/*
* 运行该类时首先要先运行该函数
*/
private void init() throws IOException{
datasetPath=new Path("hdfs://ubuntu:9000/user/breiman/glass.info");
input=new Path("hdfs://ubuntu:9000/user/breiman/input/glass.data");
// output=new Path("hdfs://ubuntu:9000/user/breiman/output-forest");


treeBuilder = new DecisionTreeBuilder();
treeBuilder.setM(m);
treeBuilder.setComplemented(true);
cOnf=new Configuration();
conf.set("mapred.job.tracker", "ubuntu:9001");
// 把dataset加入内存中
DistributedCache.addCacheFile(datasetPath.toUri(), conf);
dataset=Dataset.load(conf,datasetPath);
values=getData();
}
private List getData() throws IOException {

return ReadText.readText(input, conf);
}
/*
* 仿造setup函数
*/
public void setup() throws IOException{

//configure(Builder.getRandomSeed(conf), conf.getInt("mapred.task.partition", -1),
// Builder.getNumMaps(conf), Builder.getNbTrees(conf));
// conf.getInt("mapred.task.partition", -1)的值直接设置为0即可
// 参数设置参考上面
configure(Builder.getRandomSeed(conf), 0,
1, 10);
}


/*
* 仿造map函数
*/
protected void map() throws IOException {
//List values =ReadText.readText(input, conf);
for(Text value:values){
String[] v=value.toString().split(",");
if(v[10].equals("2")){
//System.out.println(v[10]);
}
instances.add(converter.convert(value.toString()));
}
}

/*
* 仿造cleanup函数
*/
protected void cleanup() throws IOException, InterruptedException {
// prepare the data

Data data = new Data(dataset, instances);
Bagging bagging = new Bagging(treeBuilder, data);

TreeID key = new TreeID();

for (int treeId = 0; treeId
Node tree = bagging.build(rng);

key.set(partition, firstTreeId + treeId);

// if (!isNoOutput()) {
MapredOutput emOut = new MapredOutput(tree);
System.out.println("key:"+key+"***value:"+emOut);
// context.write(key, emOut);
// }
}
}

protected void configure(Long seed, int partition, int numMapTasks, int numTrees) throws IOException {
cOnverter= new DataConverter(dataset);
// prepare random-numders generator
if (seed == null) {
rng = RandomUtils.getRandom();
} else {
rng = RandomUtils.getRandom(seed);
}

// mapper's partition
Preconditions.checkArgument(partition >= 0, "Wrong partition ID");
this.partition = partition;

// compute number of trees to build
nbTrees = nbTrees(numMapTasks, numTrees, partition);

// compute first tree id
firstTreeId = 0;
for (int p = 0; p firstTreeId += nbTrees(numMapTasks, numTrees, p);
}
}

public static int nbTrees(int numMaps, int numTrees, int partition) {
int nbTrees = numTrees / numMaps;
if (partition == 0) {
nbTrees += numTrees - nbTrees * numMaps;
}
return nbTrees;
}
}
(1)setup函数

这个函数其实应该包括init里面的所有东东,这里设定的主要包括;Random随机种子、nbTrees决策树的个数、dataset的路径、data的路径。把data读入到values集合里面、把dataset读到dataset变量,新建treeBuilder变量设定其相关属性值,新建converter变量。

(2)map函数

map函数就是遍历每行的输入,然后使用converter把读入的数据进行转换,然后添加到instances里面,首先看下instances变量吧。这个变量定义如下:List,这个是一个list,然后看到Instance类,Instance类里面就一个属性Vector和若干方法,可以看到其实Instance里面就是存储的Vector而已,不清楚搞多个Instance干嘛,直接Vector不好么?接下来看DataConverter,它有两个属性,一个是Pattern的用于分解string字符串的,另外一个是dataset,用于convert方法中相关值的设定。还有一个比较重要的方法convert方法,这个是用于把字符串转换为Vector(准确来说是Instance)的函数。在讲这个函数前,先来看下dataset吧:


假如我传入的字符串是:[1,1.52101,13.64,4.49,1.10,71.78,0.06,8.75,0.00,0.00,1],那么convert函数首先使用逗号把字符串解析到数组中,然后根据ignored的值把数组中对应的下标的值忽略,再次根据attributes的值进行匹配,如果是Numerical的话直接把值加入vector中,如果是categorical的话就按照values里面的数组进行匹配,比如如果是字符串“3”的话,那么就把其下标值加入vector中,比如上面的数据是1,那么加入字符串中的值就是2。可以通过debug方式查看添加这行输入后vector的值:


这里可以看到字符串1(这里一定要看做是字符串,而不是数字)的确是被转换为了2了,而且可以看到由于第7、8的值为0,所以这里就没有显示了。

(3)cleanup函数

看cleanup函数,刚开始新建了几个变量、Data、Bagging、TreeID,然后循环调用build函数建立树并输出每棵树,每棵树是由Node类带出的。所以这里的重点是build函数。

Bagging.build函数传入一个随机种子,然后返回一个Node,这个Node就是一个树了,这个Node可以往左、右继续添加Node。继续看这个函数的代码:

Arrays.fill(sampled, false);    Data bag = data.bagging(rng, sampled);        return treeBuilder.build(rng, bag);
看到这里首先对Data进行了一个.bagging(rng)的处理,然后把处理后的data传入了treeBuilder的build函数。一个个来看data.bagging是做什么处理的呢?

 public Data bagging(Random rng, boolean[] sampled) {    int datasize = size();    List bag = Lists.newArrayListWithCapacity(datasize);        for (int i = 0; i instaces是原始数据的list,可以看到bag每次添加了一个从instances中随机取出的一个vector值,然后进行返回,同时修改了sampled的值(这个值是说instances的哪个下标已经被选中了),所以返回的bag值里面肯定是有重复的,如下:



下面到了treeBuilder.build方法,这个方法被两个类覆写,分别是DecisionTreeBuilder、DefaultTreeBuilder,这里调用的是DecisionTreeBuilder的build方法。

刚开始是如下的代码:

if (selected == null) {      selected = new boolean[data.getDataset().nbAttributes()];      selected[data.getDataset().getLabelId()] = true; // never select the label    }    if (m == 0) {      // set default m      double e = data.getDataset().nbAttributes() - 1;      if (data.getDataset().isNumerical(data.getDataset().getLabelId())) {        // regression        m = (int) Math.ceil(e / 3.0);      } else {        // classification        m = (int) Math.ceil(Math.sqrt(e));      }    }
设定label的selected的值为true,其他属性值的selected被设置为false。然后设定m的值,由于m的值,前面没有设定,而这里是做分类问题的,所以设定m的值为所有属性值个数的平方根。这个m值是为了下面随机选择的属性值的个数。

下面的代码通过判断data.getDataset().isNumerical(data.getDataset().getLabelId())这个boolean值来进行判断是用回归还是分类思路来处理。这里的label肯定不是数值型的,所以进入分类处理的代码:

首先是两个判断:

 if (isIdentical(data)) {        return new Leaf(data.majorityLabel(rng));      }      if (data.identicalLabel()) {        return new Leaf(data.getDataset().getLabel(data.get(0)));      }
第一个判断是判断data是否全部都是一样的,第二个判断是判断data是否是空的;这里传入的data虽然有重复,但是不全是一样的,而且肯定不是为空,所以继续往下走。

int[] attributes = randomAttributes(rng, selected, m);
这行代码的主要意思是随机选择m个属性返回到attributes,比如这次debug得到的结果是:[8,2,6];然后到了下面的if (attributes == null || attributes.length == 0)这里跳过,下面if (igSplit == null) 对分类问题,这个赋值为:igSplit = new OptIgSplit();

代码继续走:

Split best = null;    for (int attr : attributes) {      Split split = igSplit.computeSplit(data, attr);      if (best == null || best.getIg() 首先看下Split这个类,有三个属性:int attr,double ig,double split;来看下computeSplit函数(OptIgSplitl里面的函数):

public Split computeSplit(Data data, int attr) {    if (data.getDataset().isNumerical(attr)) {      return numericalSplit(data, attr);    } else {      return categoricalSplit(data, attr);    }  }
又要进入函数,看numericalSplit函数:

Split numericalSplit(Data data, int attr) {    double[] values = sortedValues(data, attr);    initCounts(data, values);    computeFrequencies(data, attr, values);    int size = data.size();    double hy = entropy(countAll, size);    double invDataSize = 1.0 / size;    int best = -1;    double bestIg = -1.0;    // try each possible split value    for (int index = 0; index = values[index]      size = DataUtils.sum(countAll);      ig -= size * invDataSize * entropy(countAll, size);      if (ig > bestIg) {        bestIg = ig;        best = index;      }      DataUtils.add(countLess, counts[index]);      DataUtils.dec(countAll, counts[index]);    }    if (best == -1) {      throw new IllegalStateException("no best split found !");    }    return new Split(attr, bestIg, values[best]);  }

尼玛,好长呀。晚上回去再看。。。


分享,成长,快乐

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



推荐阅读
  • 标题: ... [详细]
  • 本文记录了在vue cli 3.x中移除console的一些采坑经验,通过使用uglifyjs-webpack-plugin插件,在vue.config.js中进行相关配置,包括设置minimizer、UglifyJsPlugin和compress等参数,最终成功移除了console。同时,还包括了一些可能出现的报错情况和解决方法。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • GreenDAO快速入门
    前言之前在自己做项目的时候,用到了GreenDAO数据库,其实对于数据库辅助工具库从OrmLite,到litePal再到GreenDAO,总是在不停的切换,但是没有真正去了解他们的 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • 本文介绍了如何清除Eclipse中SVN用户的设置。首先需要查看使用的SVN接口,然后根据接口类型找到相应的目录并删除相关文件。最后使用SVN更新或提交来应用更改。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
  • 本文讨论了如何使用Web.Config进行自定义配置节的配置转换。作者提到,他将msbuild设置为详细模式,但转换却忽略了带有替换转换的自定义部分的存在。 ... [详细]
  • GPT-3发布,动动手指就能自动生成代码的神器来了!
    近日,OpenAI发布了最新的NLP模型GPT-3,该模型在GitHub趋势榜上名列前茅。GPT-3使用的数据集容量达到45TB,参数个数高达1750亿,训练好的模型需要700G的硬盘空间来存储。一位开发者根据GPT-3模型上线了一个名为debuid的网站,用户只需用英语描述需求,前端代码就能自动生成。这个神奇的功能让许多程序员感到惊讶。去年,OpenAI在与世界冠军OG战队的表演赛中展示了他们的强化学习模型,在限定条件下以2:0完胜人类冠军。 ... [详细]
author-avatar
WYZ的小舟于SZ
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有