开发环境:Intellij IDEA14 、Maven3.2、JDK1.7、Hadoop2.6 、mahout0.10
源码下载及运行参考:https://github.com/fansy1990/randomforest_classify
设计思路:
Mahout 随机森林算法(Random Forest)利用建立好的模型(BuildForest的输出结果)以及描述文件(Describe的输出结果),来对没有标签的数据进行分类。受TestForest中的CMapper的启发,其主要代码如下:
[mw_shl_code=bash,true]String line = value.toString();
if (!line.isEmpty()) {
Instance instance = converter.convert(line);
double prediction = forest.classify(dataset, rng, instance);
lkey.set(dataset.getLabel(instance));
lvalue.set(Double.toString(prediction));
context.write(lkey, lvalue);
}[/mw_shl_code]
可以看到一行数据value,被赋值到line,然后通过line转换为Instance,之后就可以直接使用forest.classify函数来得到实际的类别了。当然,这里classify函数得到的是实际类别的下标,还需要进行一步转换,转换过程使用dataset.getLabelString(prediction)即可,这样就可以还原原始的类别了。
这里还有一个问题,TestForest里面的line里面是包含Label的,如果我们传入的line不包含label,那么通过converter.conver进行转换为Instance,肯定是有问题的,那怎么办呢?
这个问题可以通过自定义Converter来解决,具体如下:
[mw_shl_code=java,true]package util;
import com.google.common.base.Preconditions;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.mahout.classifier.df.data.Dataset;
import org.apache.mahout.classifier.df.data.Instance;
import org.apache.mahout.math.DenseVector;
/**
* Created by Fansy on 2015/11/2.
*/
public class DataConverter {
// private static final Pattern COMMA_SPACE = Pattern.compile("[, ]");
private String splitter;
private final Dataset dataset;
public DataConverter(Dataset dataset,String splitter) {
this.dataset = dataset;
this.splitter=splitter;
}
public Instance convert(String string) {
// all attributes (categorical, numerical, label), ignored
// get rid of label ,the data only contains (categorical, numerical),ignored
int nball = dataset.nbAttributes() + dataset.getIgnored().length-1;
// 把label列添加到vector中,方便直接调用forest的classify函数
String[] tokens = string.split(splitter);
Preconditions.checkArgument(tokens.length == nball,
"Wrong number of attributes in the string: " + tokens.length + ". Must be " + nball);
int nbattrs = dataset.nbAttributes();
DenseVector vector = new DenseVector(nbattrs);
int aId = 0;
for (int attr = 0; attr < nball;) {
if(dataset.getLabelId()==attr){// label 列所在下标
vector.set(aId++,0);// 对于label列直接赋值0
}
if (!ArrayUtils.contains(dataset.getIgnored(), attr)) {
String token = tokens[attr].trim();
if ("?".equals(token)) {
// missing value
return null;
}
if (dataset.isNumerical(aId)) {
vector.set(aId++, Double.parseDouble(token));
} else { // CATEGORICAL
vector.set(aId, dataset.valueOf(aId, token));
aId++;
}
attr++;
}
}
return new Instance(vector);
}
}
[/mw_shl_code]
这里在计算nball时,使用-1,即去掉Label这一个属性,这样我们的数据就可以通过Preconditions.checkArgument的验证了。
接着,在对line进行解析的过程中,要判断当前的id是否是Label(由于Label是在Describe的阶段指定的,所以这里还需要通过dataset.getLabelId来确定),如果是的话,那么就把vector的当前值赋值为0(当然这里赋值为任何的数值型都是可以的)。这样,等于是我们把传入的无label的数据人为的加入了一个任意指定的label,这样做的必要性是为了调用forest的classify函数。
分享,成长,快乐
脚踏实地,专注
转载请注明blog地址:http://blog.csdn.net/fansy1990
|