多项式Naive Bayes分类器概率计算异常问题求助
嘿,我来帮你捋清楚这个问题!你遇到的概率大于1的异常情况,核心是对数空间的运算逻辑搞错了,同时对证据项P(X)的计算方式也有偏差。咱们一步步拆解问题,再修正代码:
核心问题分析
首先回忆多项式朴素贝叶斯的核心公式:
$$P(C_k|X) = \frac{P(X|C_k)P(C_k)}{P(X)}$$
其中 $P(X) = \sum_k P(X|C_k)P(C_k)$,是所有类别下联合概率的总和。
因为直接计算乘积容易出现数值下溢,我们通常会转成对数空间计算:
$$\log(P(C_k|X)) = \log(P(X|C_k)) + \log(P(C_k)) - \log(P(X))$$
你的代码犯了两个关键错误:
- 在对数空间里用除法替代了减法($\log(a/b) = \log(a)-\log(b)$,不是$\log(a)/\log(b)$)
- 计算$\log(P(X))$时,错误地把各个类别的对数项直接相加(这相当于原始空间的乘积,而不是求和)
代码修正步骤
1. 先修正类别专属的单词概率计算
你之前的count_word_probability是计算整个数据集的单词概率,而多项式朴素贝叶斯需要按类别单独计算每个类别下的单词条件概率:
def count_word_probability_per_class(features, labels, num_classes): V_size = features.shape[1] alpha = 1 # Laplace平滑系数 word_probs = [] for cl in range(num_classes): # 取出当前类别的所有样本特征 class_features = features[labels == cl] # 该类别下每个单词的总出现次数 total_word_counts = np.sum(class_features, axis=0) # 该类别下的总单词数 total_words = np.sum(total_word_counts) # 拉普拉斯平滑后的条件概率 p(xi|Ck) probs = (alpha + total_word_counts) / ((V_size * alpha) + total_words) word_probs.append(probs) return word_probs
2. 修正证据项P(X)的计算逻辑
原来的nb_constant函数错误地将对数项相加,正确的做法是先把对数项转成原始空间求和,再取对数:
import 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) # 计算每个类别的 log(P(X|Ck)*P(Ck)) log_joint_terms = [ np.log(s_term) + np.sum(article * np.log(word_probs[i])) + np.log(labels_probs[i]) for i in range(len(word_probs)) ] # 计算 log(P(X)) = log( sum(exp(log_joint_terms)) ) log_evidence = np.log(np.sum(np.exp(log_joint_terms))) return log_evidence
3. 修正朴素贝叶斯分类器的概率计算
把对数空间的除法改成减法,再转换回原始概率:
def naive_bayes(article, label_probs, words_probs): s_term = scaling_term(article) log_evidence = nb_constant(article, label_probs, words_probs) log_class_probs = [] for cl in range(len(label_probs)): # 计算当前类别的联合对数概率 log(P(X|Ck)*P(Ck)) log_joint = np.log(s_term) + np.sum(article * np.log(words_probs[cl])) + np.log(label_probs[cl]) # 转换为后验对数概率 log(P(Ck|X)) = log_joint - log_evidence log_class_prob = log_joint - log_evidence log_class_probs.append(log_class_prob) # 转换为原始概率 class_probs = np.exp(np.array(log_class_probs)) # 浮点精度修正:确保概率和为1 class_probs = class_probs / np.sum(class_probs) return class_probs
额外说明
为什么不除以常数项时能正确预测?因为分类只需要比较各个类别$P(X|C_k)P(C_k)$的相对大小,$P(X)$对所有类别都是相同的,不影响排序,所以分类结果正确,但概率值是无意义的(趋近于0是因为数值下溢)。
内容的提问来源于stack exchange,提问作者kirgol
相关产品推荐
相关产品推荐

