【问题标题】:How to calculate evidence in Naive Bayes classifier?如何在朴素贝叶斯分类器中计算证据?
【发布时间】:2020-06-12 16:54:39
【问题描述】:

我用 Python 编写了一个简单的多项式朴素贝叶斯分类器。代码预测BBC news dataset 的正确标签,但是当我在分母中使用先验 P(X) 概率将分数作为概率输出时,我得到不正确的值(例如概率 > 1)。下面附上我的代码:

整个过程都是基于我从Wikipedia article那里学到的关于朴素贝叶斯的这个公式:

  1. 因此,第一步是从文章中提取特征。为此,我使用 Sklearn 的计数矢量化器。它计算词汇表中所有单词的出现次数:
from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer(stop_words='english', min_df=5, ngram_range=(1,1) )
features = vectorizer.fit_transform(data.news).toarray()
print(features.shape)
(2225, 9138)

因此,我为数据集中的每篇文章获得了 9138 个特征。

  1. 下一步是计算每个标签的 p(xi | Ck)。它由多项分布公式给出:

我计算 pki 如下:

def count_word_probability(features):
  V_size = features.shape[1]
  alpha = 1
  total_counts_for_each_word = np.sum(features,axis=0)
  total_count_of_words = np.sum(total_counts_for_each_word)
  probs = (alpha + total_counts_for_each_word) / ( (V_size * alpha) + total_count_of_words)
  return probs

基本上,此函数的作用是计算所有带有特定标签的文章(例如商业)中每个单词的总频率,然后除以带有该标签的所有文章中的总单词数。它还应用拉普拉斯平滑 (alpha = 1 ) 来解释频率为 0 的单词。

  1. 接下来,我计算 p(Ck),即标签的先验概率。我只是将一个类别的文章总数除以所有类别的文章总数:
labels_probs = [ len(data.index[data['category_id'] == i ]) / len(data) for i in range(5)]
  1. 这些函数分别用于缩放项和常数项 (P(x)):
import math as math
from scipy.special import factorial

def scaling_term(doc):
  term = math.factorial(np.sum(doc)) / np.prod(factorial(doc))
  return term 

上面的缩放函数将文章中单词总和的阶乘除以阶乘的乘积。

def nb_constant (article, labels_probs, word_probs):
  s_term = scaling_term(article)
  evidence = [ np.log(s_term)  + np.sum(article * np.log(word_probs[i])) + np.log(labels_probs[i])  for i in range(len(word_probs))]
  evidence = np.sum(evidence)
  return evidence

所以,上面的最后一个函数计算分母(先验概率 P(x)。它将所有文章类别的 P(x|Ck) 相加:

  1. 最终的朴素贝叶斯分类器如下所示:
def naive_bayes(article, label_probs, words_probs):
  class_probs = []
  s_term = scaling_term(article)
  constant_term = nb_constant(article, label_probs, words_probs)
  for cl in range(len(label_probs)):
    class_prob =  ( np.log(s_term) + np.sum(article * np.log(words_probs[cl])) + np.log(label_probs[cl]) )  / constant_term
    class_probs.append(class_prob)
  class_probs = np.exp(np.array(class_probs))
  return class_probs

如果没有常数项,此函数会为我提供给它的任何自定义文本输出正确的标签。但所有班级的分数都是统一的,接近于零。当我除以常数项以获得总和为零的真实概率值时,我会得到奇怪的结果,例如所有类的概率为 1.25。我肯定在理论上遗漏了一些东西,因为我对概率论和数学知之甚少。我将不胜感激任何帮助。谢谢。

【问题讨论】:

  • 好吧,如果最终的每类概率之和不等于 1,则意味着你计算的归一化因子不正确,因为根据定义 1/Z 是导致每类的因子概率总和为 1。归一化应该如下所示:Z = k 上非归一化概率的总和,然后归一化概率 = 非归一化 / Z。在我看来,你走在正确的轨道上,坚持下去,我认为你可以弄清楚。
  • @RobertDodier 嗨,罗伯特!感谢您的答复。你能稍微解释一下这个公式吗?什么是 k 上的非归一化概率,什么是归一化概率?我认为我应该使用与分子中相同的公式 - P(Ck) * p(x|Ck) 但对所有类进行总结。
  • 在我看来,您正在使用对数将乘法变为加法,这没关系,但您必须小心。您必须应用 1/Z 从 log(p) 转换回 p 之后,即在获取 exp 之后。关于计算 Z,最简单和最可靠的方法是对要规范化的数组求和,将元素按原样相加,然后将每个元素除以总和。我的建议是不要试图重现相同的公式并对公式求和——只需构造数组,然后对数组中的数字求和。希望这会有所帮助!
  • @RobertDodier 非常感谢您!有效。在取每个类的指数后,我不得不对 Z 个类进行总结。那是第一个错误。第二个错误是在取分子的指数后我必须除以 Z。你能解释为什么这个命令吗?那是因为如果分子和分母的对数分开取,我就不能划分对数吗?或者它可以与减法一起使用吗?对数(分子)-对数(分母)?
  • 另外,如果你愿意,你可以发布你对问题的答案,指出一些对数规则以及使用这个公式时如何小心?

标签: python machine-learning statistics data-science naivebayes


【解决方案1】:

感谢Robert Dodier 我找到了问题所在。在除以常数(证据)之前,请确保将分子日志取回概率。此外,请确保在总结之前对证据项中的所有类进行取幂。

【讨论】:

    猜你喜欢
    • 2014-09-18
    • 2017-01-10
    • 2015-10-09
    • 2015-09-24
    • 2015-08-27
    • 2018-02-06
    • 2012-07-02
    • 2010-10-19
    相关资源
    最近更新 更多