IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [原]Mahout随机森林算法--分类无标签数据

    fansy1990发表于 2015-11-02 23:27:16
    love 0

    开发环境:Intellij IDEA14 、Maven3.2、JDK1.7、Hadoop2.6 、mahout0.10

    源码下载及运行参考:https://github.com/fansy1990/randomforest_classify 

    设计思路:

    Mahout 随机森林算法(Random Forest)利用建立好的模型(BuildForest的输出结果)以及描述文件(Describe的输出结果),来对没有标签的数据进行分类。受TestForest中的CMapper的启发,其主要代码如下:

    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);
          }

    可以看到一行数据value,被赋值到line,然后通过line转换为Instance,之后就可以直接使用forest.classify函数来得到实际的类别了。当然,这里classify函数得到的是实际类别的下标,还需要进行一步转换,转换过程使用dataset.getLabelString(prediction)即可,这样就可以还原原始的类别了。

    这里还有一个问题,TestForest里面的line里面是包含Label的,如果我们传入的line不包含label,那么通过converter.conver进行转换为Instance,肯定是有问题的,那怎么办呢?

    这个问题可以通过自定义Converter来解决,具体如下:

    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);
        }
    }
    
    这里在计算nball时,使用-1,即去掉Label这一个属性,这样我们的数据就可以通过Preconditions.checkArgument的验证了。

    接着,在对line进行解析的过程中,要判断当前的id是否是Label(由于Label是在Describe的阶段指定的,所以这里还需要通过dataset.getLabelId来确定),如果是的话,那么就把vector的当前值赋值为0(当然这里赋值为任何的数值型都是可以的)。这样,等于是我们把传入的无label的数据人为的加入了一个任意指定的label,这样做的必要性是为了调用forest的classify函数。


    分享,成长,快乐

    脚踏实地,专注

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





沪ICP备19023445号-2号
友情链接