注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

anqiang专栏

不要问细节是怎么搞的,源码说明一切

 
 
 

日志

 
 

Weak 学习七 (朴素贝叶斯之多项式贝叶斯分类算法)  

2009-07-15 09:56:26|  分类: Weka 学习系列 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

NaiveBayesMultinomial字面上的意思,多项式贝叶斯分类算法。这种方法是在A Comparison of Event Models for Naive Bayes Text Classification这篇论文中提到的。它是一个专门用于文本处理的分类器(可以这样说,它专门处理经过WVTool处理过后的文本向量空间形式表达的样本集)。因为从

/**

   * Returns default capabilities of the classifier.

   *

   * @return      the capabilities of this classifier

   */

  public Capabilities getCapabilities() {

    Capabilities result = super.getCapabilities();

 

    // attributes

    result.enable(Capability.NUMERIC_ATTRIBUTES);

 

    // class

    result.enable(Capability.NOMINAL_CLASS);

    result.enable(Capability.MISSING_CLASS_VALUES);

   

    return result;

  }

这个函数里,我们可以看到它只能处理数值型属性和离散型类别。

在这种分类器中,它底层还是将各个属性当做离散属性来处理,只不过没有属性的权重不同而已,可以用TFTFIDF等对这些离散属性进行加权(这是WVTool要做的事情了)。

整个贝叶斯方法的思想是:

假定文本中各个属性的相互独立的,这样我们在学习的过程中可以得到各个属性与类别的先验概率;在分类时,我们利用学习得到的先验概率来推导到出,在某个属性集合的情况下,测试文档属于某个类别的后验概率。

现在我们分别对学习部分和分类部分的代码进行分析:

学习部分:

/**

   * Generates the classifier.

   *

   * @param instances set of instances serving as training data

   * @throws Exception if the classifier has not been generated successfully

   */

  public void buildClassifier(Instances instances) throws Exception

  {

    // can classifier handle the data?

      /*

       * 前面是一些必要的处理工作

       */

    getCapabilities().testWithFail(instances);

 

    // remove instances with missing class

    instances = new Instances(instances);

    instances.deleteWithMissingClass();

   

    m_headerInfo = new Instances(instances, 0);

    m_numClasses = instances.numClasses();

    m_numAttributes = instances.numAttributes();

    m_probOfWordGivenClass = new double[m_numClasses][];

   

    /*

      initialising the matrix of word counts

      NOTE: Laplace estimator introduced in case a word that does not appear for a class in the

      training set does so for the test set

    */

    for(int c = 0; c<m_numClasses; c++)

      {

    m_probOfWordGivenClass[c] = new double[m_numAttributes];

    for(int att = 0; att<m_numAttributes; att++)

      {

        m_probOfWordGivenClass[c][att] = 1;

      }

      }

   

    //enumerate through the instances

    Instance instance;

    int classIndex;

    double numOccurences;

    double[] docsPerClass = new double[m_numClasses];

    double[] wordsPerClass = new double[m_numClasses];

   

    java.util.Enumeration enumInsts = instances.enumerateInstances();

    while (enumInsts.hasMoreElements())

      {

    instance = (Instance) enumInsts.nextElement();

    classIndex = (int)instance.value(instance.classIndex());

    /*

     * 每个类别的文档数量数组

     */

    docsPerClass[classIndex] += instance.weight();

      

    for(int a = 0; a<instance.numValues(); a++)

      if(instance.index(a) != instance.classIndex())

        {

          if(!instance.isMissing(a))

       {

          /*

           * 用当前属性的权重乘于当前样本的权重,将它们累加起来得到在当前属性出现时属于某个类别的先验概率

           */

         numOccurences = instance.valueSparse(a) * instance.weight();

         if(numOccurences < 0)

           throw new Exception("Numeric attribute values must all be greater or equal to zero.");

         wordsPerClass[classIndex] += numOccurences;

         m_probOfWordGivenClass[classIndex][instance.index(a)] += numOccurences;

       }

        }

      }

   

    /*

      normalising probOfWordGivenClass values

      and saving each value as the log of each value

      对得到的先验概率进行归一化处理

    */

    for(int c = 0; c<m_numClasses; c++)

      for(int v = 0; v<m_numAttributes; v++)

    m_probOfWordGivenClass[c][v] = Math.log(m_probOfWordGivenClass[c][v] / (wordsPerClass[c] + m_numAttributes - 1));

   

    /*

      calculating Pr(H)

      NOTE: Laplace estimator introduced in case a class does not get mentioned in the set of

      training instances

       使用了拉普拉斯修正

    */

    final double numDocs = instances.sumOfWeights() + m_numClasses;

    m_probOfClass = new double[m_numClasses];

   

    /*

     * 得到类别分布概率的先验概率

     */

    for(int h=0; h<m_numClasses; h++)

      m_probOfClass[h] = (double)(docsPerClass[h] + 1)/numDocs;

  }

整个学习的过程就是要得到,属性空间上各个属性相对每个类别的先验概率以及类别分布的先验概率。

 

分类过程:

/**

   * Calculates the class membership probabilities for the given test

   * instance.

   *

   * @param instance the instance to be classified

   * @return predicted class probability distribution

   * @throws Exception if there is a problem generating the prediction

   */

  public double [] distributionForInstance(Instance instance) throws Exception

  {

      /*

       * 总的思想是通过先验概率得到当前样本的类别分布的后验概率

       */

    double[] probOfClassGivenDoc = new double[m_numClasses];

   

    //calculate the array of log(Pr[D|C])

    double[] logDocGivenClass = new double[m_numClasses];

    for(int h = 0; h<m_numClasses; h++)

      logDocGivenClass[h] = probOfDocGivenClass(instance, h);

   

    double max = logDocGivenClass[Utils.maxIndex(logDocGivenClass)];

    double probOfDoc = 0.0;

   

    /*

     * 这一步为什么要这样做 Math.exp()),我还不太明白。

     * 现在这些属性先验概率的乘积与类别分布的先验概率的相乘,得到当前样本集属性某个类别的后验概率

     */

    for(int i = 0; i<m_numClasses; i++)

      {

    probOfClassGivenDoc[i] = Math.exp(logDocGivenClass[i] - max) * m_probOfClass[i];

    probOfDoc += probOfClassGivenDoc[i];

      }

   

    Utils.normalize(probOfClassGivenDoc,probOfDoc);

   

    return probOfClassGivenDoc;

  }

   

  /**

   * log(N!) + (for all the words)(log(Pi^ni) - log(ni!))

   * 

   *  where

   *      N is the total number of words

   *      Pi is the probability of obtaining word i

   *      ni is the number of times the word at index i occurs in the document

   *

   * @param inst       The instance to be classified

   * @param classIndex The index of the class we are calculating the probability with respect to

   *

   * @return The log of the probability of the document occuring given the class

   */

   

  /*

   * 通过当前样本的属性集得到当前样本属于各个类别的后验概率

   */

  private double probOfDocGivenClass(Instance inst, int classIndex)

  {

    double answer = 0;

    //double totalWords = 0; //no need as we are not calculating the factorial at all.

   

    double freqOfWordInDoc;  //should be double

    for(int i = 0; i<inst.numValues(); i++)

      if(inst.index(i) != inst.classIndex())

    {

      freqOfWordInDoc = inst.valueSparse(i);

      //totalWords += freqOfWordInDoc;

      answer += (freqOfWordInDoc * m_probOfWordGivenClass[classIndex][inst.index(i)]

            ); //- lnFactorial(freqOfWordInDoc));

    }

   

    //answer += lnFactorial(totalWords);//The factorial terms don't make

    //any difference to the classifier's

    //accuracy, so not needed.

   

    return answer;

  }

分类过程实际上是通过学习到的属性集合的先验概率,得到当前样本属性集合属于各个类别的后验概率。

其实整个过程是比较简单的,它的原理NaiveBayes是相同的。虽然朴素贝叶斯基于的假设是不符合现实情况的,文本中的属性一般是有上下文语意关联的,它们并不是相互独立的。尽管是这样,朴素贝叶斯方法在文本分类中表现还是很不错的。

  评论这张
 
阅读(2679)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017