Twenty Newsgroups Classification实例任务(完结篇)

Twenty Newsgroups Classification实例任务跑的是哪个算法?就是bayesian,也就是我们说的贝叶斯,首先不管你是否了解贝叶斯算法(说实话,我真的不知道这个算法的原理),如果你看了这篇博客,至少应该了解如何对数据 进行处理,然后就可以分类了,不管它是啥算法,好,来开始。

接上篇系列blog: Twenty Newsgroups Classification实例任务之TrainNaiveBayesJob(一),其实贝叶斯算法从上篇博客才开始算是开始,之前的都是文本处理的内容,不看也可以,作用不大(下篇博客就说针对一般的直接数值型输入文件如何应用贝叶斯算法)。

首先来分析下原始数据:原始数据有20个文件夹,每个文件夹里面有n个子文件,经过一系列的转换每个子文件被用一组单词和单词出现的次数(次数也是经过转换的,好像叫做tfdif之类的,具体没咋了解)来表示,所以可以看做如下的形式:

directory1:{

direcctory1_file1:[word_a:3.2,word_b:2.2...]

directory1_file2:[word_a:3.2,word_b:2.2...]

}

directory2:{

direcctory2_file1:[word_a:3.2,word_b:2.2...]

directory2_file2:[word_a:3.2,word_b:2.2...]

}

...

TrainNaiveByaesJob的第一个任务其实就是把20个文件中各自所有文件整合到如下的形式(所谓的整合就是把对应单词的出现次数全部相加):

directory1:{

direcctory1_files:[word_a:6.4,word_b:4.4...]

}

directory2:{

direcctory2_files:[word_a:6.4,word_b:4.4...]

}

...

然后来分析index的问题,上次大概说了下意思,这里详细分析下:

TrainNaiveBayesJob里面的createLabelIndex方法里面的

SequenceFileDirIterable<Text, IntWritable> iterable =
              new SequenceFileDirIterable<Text, IntWritable>(getInputPath(), PathType.LIST, PathFilters.logsCRCFilter(), getConf());
代码好像只读了key的值,value的值它都没有管的,因为getInputPath()这个序列文件的key是Text的,但是value是VectorWritable的;姑且看做是没关的吧,然后读入每行输入到iterable中,然后看BayesUtils.writeLabelIndex:

Collection<String> seen = new HashSet<String>();
    int i = 0;
    try {
      for (Object label : labels) {
        String theLabel = ((Pair<?,?>) label).getFirst().toString().split("/")[1];
        if (!seen.contains(theLabel)) {
          writer.append(new Text(theLabel), new IntWritable(i++));
          seen.add(theLabel);
        }
      }
    } finally {
      Closeables.closeQuietly(writer);
    }
这里定义了一个set,也就是把所有行中唯一的key值,也就是标识写入文件,可以看到这里用了一个.split方法来获取标识,如果使用一般数字的输入文件那么就要改这里的代码了(具体下篇分析)。这块代码的意思就是标识和数字的映射而已。(index在第一个job前面,这里分析基本不分前后顺序了)

然后到第二个prepareJob。这个job主要看weightsMapper这个类文件,这里面定义了两个Vector,分别是:

private Vector weightsPerFeature;
  private Vector weightsPerLabel;
看这两个Vector是如何设置的:

weightsPerFeature.assign(instance, Functions.PLUS);
    weightsPerLabel.set(label, weightsPerLabel.get(label) + instance.zSum());
vectora.assign(vectorb,function.plus)的意思是啥?就是把vectora和vectorb相对应的值加起来,又这个是在map里面,所以等于是把全部的值都加起来了,对应于前面分析的数据形式,得到weightsPerFeature的值为:

directoryall:{

direcctoryall_files:[word_a:12.8,word_b:8.8...]

}

然后到了weightsPerLabel,首先要搞清楚instance.zSum()是干嘛的,这个是把所有instance的值全部加起来,好吧,那应该就可以猜到weightsPerLabel的值了,如下:

directory_1:{[word_all:10.8]}

directory_2:{[word_all:10.8]}

...

所以weightsPerFeature应该是一个一维的,size是单词个数的向量;weightsPerLabel是一个一维的,size是标识个数的向量;

cleanup输出这两个向量:

 ctx.write(new Text(TrainNaiveBayesJob.WEIGHTS_PER_FEATURE), new VectorWritable(weightsPerFeature));
      ctx.write(new Text(TrainNaiveBayesJob.WEIGHTS_PER_LABEL), new VectorWritable(weightsPerLabel));
   
对应参考最开始一篇博客相应的log信息可以看到这个job的输出是两条记录。

然后就到了第三个prepareJob了,什么?第三个?不是说有两个的么,的确是,看log信息的确是两个,但是源码里面有三个,这个是怎么回事?好吧,源码里面的job没有提交:

 /* TODO(robinanil): Enable this when thetanormalization works.
    succeeded = thetaSummer.waitForCompletion(true);
    if (!succeeded) {
      return -1;
    }*/
被注释掉了,说等到啥时候才开始起作用(应该是以后的版本了);

然后就得到模型了,然后就完了。额 ,完了?

好吧,还没。继续看下面:

NaiveBayesModel naiveBayesModel = BayesUtils.readModelFromDir(getTempPath(), getConf());
    naiveBayesModel.validate();
    naiveBayesModel.serialize(getOutputPath(), getConf());
这里首先得到模型,然后检查下模型,然后把模型写入文件;

后面两个就不看了,主要看下模型是如何得到的:

public static NaiveBayesModel readModelFromDir(Path base, Configuration conf) {

    float alphaI = conf.getFloat(ThetaMapper.ALPHA_I, 1.0f);

    // read feature sums and label sums
    Vector scoresPerLabel = null;
    Vector scoresPerFeature = null;
    for (Pair<Text,VectorWritable> record : new SequenceFileDirIterable<Text, VectorWritable>(
        new Path(base, TrainNaiveBayesJob.WEIGHTS), PathType.LIST, PathFilters.partFilter(), conf)) {
      String key = record.getFirst().toString();
      VectorWritable value = record.getSecond();
      if (key.equals(TrainNaiveBayesJob.WEIGHTS_PER_FEATURE)) {
        scoresPerFeature = value.get();
      } else if (key.equals(TrainNaiveBayesJob.WEIGHTS_PER_LABEL)) {
        scoresPerLabel = value.get();
      }
    }

    Preconditions.checkNotNull(scoresPerFeature);
    Preconditions.checkNotNull(scoresPerLabel);

    Matrix scoresPerLabelAndFeature = new SparseMatrix(scoresPerLabel.size(), scoresPerFeature.size());
    for (Pair<IntWritable,VectorWritable> entry : new SequenceFileDirIterable<IntWritable,VectorWritable>(
        new Path(base, TrainNaiveBayesJob.SUMMED_OBSERVATIONS), PathType.LIST, PathFilters.partFilter(), conf)) {
      scoresPerLabelAndFeature.assignRow(entry.getFirst().get(), entry.getSecond().get());
    }

    Vector perlabelThetaNormalizer = scoresPerLabel.like();
    /* for (Pair<Text,VectorWritable> entry : new SequenceFileDirIterable<Text,VectorWritable>(
        new Path(base, TrainNaiveBayesJob.THETAS), PathType.LIST, PathFilters.partFilter(), conf)) {
      if (entry.getFirst().toString().equals(TrainNaiveBayesJob.LABEL_THETA_NORMALIZER)) {
        perlabelThetaNormalizer = entry.getSecond().get();
      }
    } 

    Preconditions.checkNotNull(perlabelThetaNormalizer);
    */
    return new NaiveBayesModel(scoresPerLabelAndFeature, scoresPerFeature, scoresPerLabel, perlabelThetaNormalizer,
        alphaI);
  }
看readModelFromDir方法这里主要是把weightsPerLabel和weightsPerFeature全部读出来,同时把第一个job的输出用一个二维的向量表示:

即标识*单词个数这样维度的向量,使用scoresPerLabelAndFeature表示,然后就把这三个变量存入了model里面了(最后一行最后两个参数都是常量,可以不管)。

这样模型就建好了。这时就会产生疑问了?模型有了?如何用?

额,好吧,我直接说了:参考mahout bayesian 算法数据流,下载excel点击f2看里面的公式。这里简单的说明下:


(1)首先,原始数据是1到10行的一个向量集合,每个向量由a,b,c三个属性,最后面是它的标识,比如第10行(行号为10),属于第4类;所以标识有4个,属性(对应于前面的单词个数)有3个;

(2)计算weightsPerLabel(4*1向量),weightsPerFeature(1*3向量)分别由图中黄色格子标出,scoreFeatureAndLabel(4*3向量)由图中红色字体标出

(3)按照最上面的计算公式就可以计算出来第一行数据的label_1的参数值,然后计算label_2的参数值,一直到label_4的参数值,然后求出这些参数值的最大值所在的label就是这行数据应该被分入的标识了。

看到上面的10行数据有3行是被分错了。

这个,好吧,基本就是mahout中bayesian算法的实现了,不过说实话,贝叶斯算法就是这样的?

下篇,针对这样的数据应用贝叶斯算法:

0.2	0.3	0.4	1
0.32	0.43	0.45	1
0.23	0.33	0.54	1
2.4	2.5	2.6	2
2.3	2.2	2.1	2
5.4	7.2	7.2	3
5.6	7	6	3
5.8	7.1	6.3	3
6	6	5.4	3
11	12	13	4
敬请期待。。。


分享,成长,快乐

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值