如何计算朴素贝叶斯分类器中的证据?
How to calculate evidence in Naive Bayes classifier?
我在 Python 中写了一个简单的多项朴素贝叶斯分类器。该代码预测了 BBC news dataset 的正确标签,但是当我在分母中使用先验 P(X) 概率将分数输出为概率时,我得到了不正确的值(例如 > 1 表示概率)。下面附上我的代码:
整个过程是基于我从Wikipedia article中学到的关于朴素贝叶斯的这个公式:
- 所以,第一步是从文章中提取特征。为此,我使用 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 个特征。
- 下一步是计算每个标签的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 的词。
- 接下来,我计算 p(Ck),这是标签的先验概率。我简单地将一个类别的文章总数除以所有类别的文章总数:
labels_probs = [ len(data.index[data['category_id'] == i ]) / len(data) for i in range(5)]
- 这些是缩放项和常数项的函数(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) :
- 最终的朴素贝叶斯分类器如下所示:
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。我肯定在理论上遗漏了一些东西,因为我对概率论和数学了解不多。我将不胜感激任何帮助。谢谢。
感谢 Robert Dodier 我弄清楚了问题所在。在除以常数(证据)之前,请确保对分子日志取幂返回概率。另外,请确保在求和之前对证据项中的所有 类 求幂。
我在 Python 中写了一个简单的多项朴素贝叶斯分类器。该代码预测了 BBC news dataset 的正确标签,但是当我在分母中使用先验 P(X) 概率将分数输出为概率时,我得到了不正确的值(例如 > 1 表示概率)。下面附上我的代码:
整个过程是基于我从Wikipedia article中学到的关于朴素贝叶斯的这个公式:
- 所以,第一步是从文章中提取特征。为此,我使用 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 个特征。
- 下一步是计算每个标签的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 的词。
- 接下来,我计算 p(Ck),这是标签的先验概率。我简单地将一个类别的文章总数除以所有类别的文章总数:
labels_probs = [ len(data.index[data['category_id'] == i ]) / len(data) for i in range(5)]
- 这些是缩放项和常数项的函数(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) :
- 最终的朴素贝叶斯分类器如下所示:
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。我肯定在理论上遗漏了一些东西,因为我对概率论和数学了解不多。我将不胜感激任何帮助。谢谢。
感谢 Robert Dodier 我弄清楚了问题所在。在除以常数(证据)之前,请确保对分子日志取幂返回概率。另外,请确保在求和之前对证据项中的所有 类 求幂。