zoukankan      html  css  js  c++  java
  • Mahout Bayes分类

    Mahout Bayes分类器是按照《Tackling the Poor Assumptions of Naive Bayes Text Classiers》论文写出来了,具体查看论文

    实现包括三部分:The Trainer(训练器)、The Model(模型)、The Classifier(分类器)

    1、训练

    首先,要对输入数据进行预处理,转化成Bayes M/R job读入数据要求的格式,即训练器输入的数据是KeyValueTextInputFormat格式,第一个字符是类标签,剩余的是特征属性(即单词)。以20个新闻的例子来说,从官网上下载的原始数据是一个分类目录,下面每个文件夹名就是类标签,里面是属于此类的一些文档(一个文件是一篇文章)。mahout通过org.apache.mahout.classifier.bayes.PrepareTwentyNewsgroups对数据进行预处理,完成的工作是对于原始的一个分类目录下面的所有文件,依次遍历,并将目录名作为类别名,就这样完成了inputDir-->outputFile的转变。如果要处理html文件的话,那么就需要在BayesFileFormatter调用html clean,extract body text的过程,生成cleaned text。

    PrepareTwentyNewsgroups调用org.apache.mahout.classifier.BayesFileFormatter和org.apache.lucene.analysis.standard.StandardAnalyzer进行分析处理,将数据转换成了:类标签作为一个文件的名字,文件里每一个行(一个回车算一行)是包含在此类中的一篇文章(格式是:类标签 一篇文章中的所有单词)。

    BayesFileFormatter的作用是把一个目录里的所有文件转换成一个文件,label   content其中的文件内容全部经过tokenize,词之间加入空格,存入content。BayesFileFormatter.collapse的作用是将一个目录的所有文件加上label处理成MapReduce所需要的按行处理的文件格式,变成1个文件。

    其次,调用org.apache.mahout.classifier.bayes.TrainClassifier进行训练,结果生成newsmodel。

    这个类会根据命令行参数调用两个训练器:trainCNaiveBayes和trainNaiveBayes,其中trainCNaiveBayes函数调用CBayesDriver类;trainNaiveBayes调用BayesDriver类。

    这里只分析org.apache.mahout.classifier.bayes.mapreduce.bayes.BayesDriver类,它实现了BayesJob接口,在runJob函数里调用4个map/reduce作业类:

    第一个:BayesFeatureDriver负责Read the features in each document normalized by length of each document

    第二个:BayesTfIdfDriver负责Calculate the TfIdf for each word in each label

    第三个:BayesWeightSummerDriver负责alculate the Sums of weights for each label, for each feature

    第四个:BayesThetaNormalizerDriver负责:Calculate the normalization factor Sigma_W_ij for each complement class

    以20-news的例子分别分析这四个类:

    ⑴BayesFeatureDriver

    所在包:package org.apache.mahout.classifier.bayes.mapreduce.common;

    输入格式:KeyValueTextInputFormat.class

    输出格式:BayesFeatureOutputFormat.class

    输出key类型:StringTuple.class

    输出value类型:DoubleWritable.class

    Map:BayesFeatureMapper.class

    Reduce:BayesFeatureReducer.class

    注意:BayesFeatureDriver可以独立运行,默认的输入和输出:(没有试过,不知道可不可以独立运行,里面有main函数,应该是可以的,不过本人水平有限,目前不会)

    input=new Path("/home/drew/mahout/bayes/20news-input");

    output=new Path("/home/drew/mahout/bayes/20-news-features");

    p=new BayesParameters(1) gramsize默认为1

    BayesFeatureOutputFormat继承了MultipleOutputFormat,定义了产生的四个文件路径及名字,文件的格式还是SequenceFileOutputFormat.:

    $OUTPUT/trainer-wordFreq

    $OUTPUT/trainer-termDocCount

    $OUTPUT/trainer-featureCount

    $OUTPUT/trainer-docCount

    BayesFeatureMapper的输出为:

    一行一个map,根据数据处理的格式即一篇文章一个map,以下的label指类标签,token是属性即单词,dKJ是某token在本篇文章中出现的次数,∑dKJ2是本篇文章中所有token出现次数的平方和,以下及后面的表格是觉得看着清楚自己画的,输出时只是里面的内容,例如:_WT,label,token空格value的值

    key

    value

    _WT,label,token

    Log[(1.0+dKJ)/(∑dKJ2)1/2]即为某词在一个文档中的TF值

    通俗点就是:Log[(1.0+某属性在本篇文章中出现的次数)/(本篇文章中所有属性出现次数的平方和)1/2]

    _DF,label,token

    1.0

    _FC,token

    1.0

    _LC,label

    1.0

    BayesFeatureReducer的输出为:

    相同的key放在一个reduce里执行合并

    key

    value

    输出

    _WT,label,token

    ∑Log[(1.0+dKJ)/(∑dKJ2)1/2]即某类中某属性的TF值

    trainer-wordFreq

    _DF,label,token

    某label中出现某token的文档数

    trainer-termDocCount

    _FC,token

    所有训练集文章中出现某token的文档数

    trainer-featureCount

    _LC,label

    某label下的文档数

    trainer-docCount

    _FT,token

    与_FC的value一样

    没输出且只在mhaout-0.4里出现这部分计算,0.3里没有

    ⑵BayesTfIdfDriver

    输入格式:SequenceFileInputFormat.class

    输出格式:BayesTfIdfOutPutFormat.class

    输出key类型:StringTuple.class

    输出value类型:DoubleWritable.class

    输入路径:就是第一个map/reduce生成的trainer-wordFreq、trainer-termDocCount、trainer-featureCount文件

    输出:trainer-tfIdf文件

    Map:BayesTfIdfMapper.class

    Reduce:BayesTfIdfReducer.class

    根据BayesFeatureReducer的输出文件计算TF-IDF值,但是只调用了以上的trainer-wordFreq、trainer-termDocCount、trainer-featureCount三个文件,计算完毕后会删除这些中间文件(目前还没搞懂在哪删除的,只看见bayesDriver里面有把),并生成两个文件trainer-tfIdf和trainer-vocabCount(BayesTfIdfOutPutFormat里有)。

    BayesTfIdfMapper的输出为:

    TF值是调用trainer-wordFreq中的value

    Idf=某类下的文档数/某类下出现该token的文档数

    其中某类下出现该token的文档数是调用trainer-termDocCount中的value

    key

    value

    _WT,label,token

    TF值

    _WT,label,token

    logidf

    _FS

    1.0

    ②BayesTfIdfReducer的输出为:

    key

    value

    输出

    _WT,label,token

    TF×logidf

    trainer-tfidf

    _FS

    属性总数

    trainer-VocabCount

    ⑶BayesWeightSummerDriver

    输入文件格式:SequenceFileInputFormat.class

    输出文件格式:BayesWeightSummerOutputFormat.class

    输出key:StringTuple.class

    输出value:DoubleWritable.class

    输入路径:是第二个map/reduce生成的trainer-tfIdf文件

    输出:trainer-weights文件

    Map:BayesWeightSummerMapper.class

    Reduce:BayesWeightSummerReducer.class

    这里只调用了第二个map/reduce生成的trainer-tfIdf,没有调trainer-VocabCount

    BayesWeightSummerMapper的输出为:

    key

    value

    _SJ,token

    TFIdf值

    _SK,label

    TFIdf值

    _SJSK

    TFIdf值

    ②BayesWeightSummerReducer的输出为:

    key

    value

    输出

    _SJ,token

    某属性的全部TFIdf总和

    Sigma_j

    _SK,label

    某类下的所有属性的TFIdf总和

    Sigma_k

    _SJSK

    所有的TFIdf值

    Sigma_kSigma_j

    BayesThetaNormalizerDriver

    输入文件格式:SequenceFileInputFormat.class

    输出文件格式:SequenceFileOutputFormat.class

    输出key:StringTuple.class

    输出value:DoubleWritable.class

    输入路径: 第二个map/reduce生成的trainer-tfIdf/下的trainer-tfIdf和trainer-VocabCount,以及trainer-weights/的Sigma_k和Sigma_kSigma_j

    输出:trainer-thetaNormalizer文件

    BayesWeightSummerMapper的输出为:

    Log里的分子中TFIdf是某类下某属性的TFIdf值,分母中VocabCount是属性总数

    key

    value

    _LTN,label

    Log[(TFIdf+1.0)/(sigma_k+VocabCount)]

    ②BayesWeightSummerReducer的输出为:

    将Map的结果合并

    key

    value

    _LTN,label

    ΣLog[(TFIdf+1.0)/(sigma_k+VocabCount)]

    2、模型

    以上训练部分的四个job执行完毕后,整个bayes模型就建立完毕了,总共生成并保存三个目录文件:

    trainer-tfIdf

    trainer-weights

    trainer-thetaNormalizer

    我们可以将模型从分布式上Sequence文件导成本地的txt文件进行查看。

    3、测试

    调用类:TestClassifier

    所在包:package org.apache.mahout.classifier.bayes;

    根据命令行参数会选择顺序执行还是并行map/reduce执行,这里只分析并行map/reduce,执行时会调用BayesClassifierDriver类

    分析BayesClassifierDriver类

    先runjob,runjob中先运行BayesClassifierMapper再是BayesClassifierReducer,job执行完毕后会调用混合矩阵:ConfusionMatrix函数显示结果

    BayesClassifierMapper

    首先,运行configure:algorithm=new BayesAlgorithm()和datastore=new InMemoryDatastore(params),datastore时InMemoryDatastore(params)方法将模型装入到datastore中即装入Sigma_j、Sigma_k、Sigma_kSigma_j、thetaNormalizer、weight=TfIdf、alpha_i=1.0);classifier=new classifierContext(algorithm,datastore),classifier.initialize(),即初始化classifier,初始化classifier是datastore.initialize()和algorithm.initialize(this.datastore)。

    datastore的初始化:

    调用SequenceFileModelReader的loadModel方法(五个Load方法):

    ①loadFeatureWeights(装入的是Sigma_j)生成hashmap Sigma_j{0,weight 1,weight …}其中0、1…等是属性的标号,weight是Sigma_j的value。

    ②loadLabelWeights(装入的是Sigma_k)生成hashmap Sigma_k{0,weight 1,weight …}其中0、1…等是label即类标签的标号,weight是Sigma_k的value。

    ③loadSumWeight(装入的是Sigma_kSigma_j)使datastore的成员变量Sigma_jSigma_k=value(训练得到的所有tfidf总和)。

    ④loadThetaNormalizer(装入的是ThetaNormalizer)生成hashmap thetaNormalizerPerlabel{0,weight 1,weight …}其中weight是传进来的value,使datastore的成员变量thetaNormalizer=Max(1.0 |weight|)。

    ⑤loadWeightMatrix(装入的是weight即tfidf)生成weightMatrix是SparseMatrix,其中行是属性的标号,列是label的标号,行列交叉的地方是tfidf。

    algorithm的初始化:

    调用datastore.getKeys,getKeys返回labeldicionary.Keys即返回一个集合,里面放的是所有的label。

    其次,运行map:开始分类classifier.classifyDocument(),classifyDocument()调用algorithm.classifyDocument。new result{unkonwm,0} categories=”label weight”即所有的label集合;开始循环:针对每一个类进行循环,调用documenWeight:先计算文档中每个词的次数(frequency),生成一个Map叫wordlist,针对wordlist的each pair计算:∑[frequency×featureweight(datastore,label,word)]。其中featureweight共四个,都调用datastore.getWeight,以下分别分析:

    ①double result = 调用datastore.getWeight,稀疏矩阵的getQuick,取出矩阵的Tfidf值;

    ②double vocabCount =属性总数;

    ③double sumLableWeight =Sigma_k的值;

    ④double numerator =result + 1.0;

    ⑤double denominator = sumLableWeight + vocabCount;

    ⑥double weight =log(numerator/denominator)也就是=log[(Tfidf+1.0)/(Sigma_k+属性个数)];

    返回的是result = -weight;

    所以说,documenWeight返回的值是测试文档属于某类的概率的大小,即所有属性的在某类下的frequency×result之和与在其他类下的和值进行比较,最大值的,取出它的label,文档就属于此类。

    key=_CT 正确label 分类label  value=1.0

    BayesClassifierReducer

    只是合并map的结果。

    key=_CT 正确label 分类label  value=正确分类的文档数

    根据对以上∑(frequency×result)进行分析,参照贝叶斯多项式模型,frequency是对weight中取对数时转移到前面的,即log(numerator/denominator)frequency = frequency×log(numerator/denominator),weight是条件概率,即log[(numerator/denominator)frequency

    ×(numerator/denominator)frequency …] = ∑log(numerator/denominator)frequency因为按贝叶斯原理来说,后验概率=先验概率×条件概率,据我理解,此处为什么没有乘先验概率,可能是因为所用的20个新闻的数据每类中的文档数大致一样,先验概率几乎一样,所以没必要乘(个人猜测)。

    ConfusionMatrix函数显示结果

    key=正确label  value={key=分类label value=值}


    本文转载自:http://blog.163.com/jiayouweijiewj@126/blog/static/17123217720113115027394/



  • 相关阅读:
    398. Random Pick Index
    382. Linked List Random Node
    645. Set Mismatch
    174. Dungeon Game
    264. Ugly Number II
    115. Distinct Subsequences
    372. Super Pow
    LeetCode 242 有效的字母异位词
    LeetCode 78 子集
    LeetCode 404 左叶子之和
  • 原文地址:https://www.cnblogs.com/cl1024cl/p/6205103.html
Copyright © 2011-2022 走看看