一句话答案:多项式朴素贝叶斯是处理词频计数数据的王者,专为文本分类而生。10行代码即可实现新闻主题分类,准确率超90%!
如果你在搜索:
那么,这篇文章就是为你写的——从词频统计到概率计算,一步不跳。
朴素贝叶斯家族有三大主力成员,选择哪个取决于你的数据形态:
算法类型 | 适用数据 | 核心假设 | 典型场景 |
|---|---|---|---|
多项式朴素贝叶斯 | 离散计数(如词频) | 特征服从多项式分布 | 新闻分类、情感分析、垃圾邮件过滤 |
伯努利朴素贝叶斯 | 二值特征(0/1) | 特征是布尔值 | 短文本、关键词存在性判断 |
高斯朴素贝叶斯 | 连续数值 | 特征服从正态分布 | 身高、温度、成绩等连续数据 |
💡 核心思想:对于每个类别,每个词都有一个独立的概率。新文档的类别,由其所有词的频率加权决定。
我们要计算:

根据贝叶斯定理和独立性假设:

其中:

为避免零概率,使用拉普拉斯平滑(Laplace Smoothing):

⚠️ 注意:分母是该类别的总词数(不是文档数),因为多项式模型关注的是生成过程中的词频。
新闻ID | 内容(分词后) | 类别 |
|---|---|---|
1 | ["科技", "人工智能", "发展", "迅速"] | 科技 (0) |
2 | ["科技", "公司", "发布", "新产品"] | 科技 (0) |
3 | ["体育", "比赛", "精彩", "激烈"] | 体育 (1) |
目标:预测新文档
["人工智能", "比赛"]属于哪个类别?
["科技", "人工智能", "发展", "迅速", "公司", "发布", "新产品", "体育", "比赛", "精彩", "激烈"] → N = 11
["人工智能", "比赛"] 的后验概率新文档词频:人工智能:1, 比赛:1


✅ 结论:0.00372 > 0.00297 → 判定为 科技 (0)
尽管“比赛”是体育词,但“人工智能”的权重更高(在科技类中概率更大),且科技类先验概率也更高,最终被判为科技新闻。
from sklearn.naive_bayes import MultinomialNB
from sklearn.feature_extraction.text import CountVectorizer
# 数据
texts = [
"科技 人工智能 发展 迅速",
"科技 公司 发布 新产品",
"体育 比赛 精彩 激烈"
]
labels = [0, 0, 1] # 0=科技, 1=体育
# 向量化(词频计数)
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(texts)
# 训练
clf = MultinomialNB(alpha=1.0) # alpha=1 即拉普拉斯平滑
clf.fit(X, labels)
# 预测
new_text = vectorizer.transform(["人工智能 比赛"])
pred = clf.predict(new_text)
print("预测结果:", "科技" if pred[0] == 0 else "体育")
# 输出: 科技import numpy as np
from collections import defaultdict
class MultinomialNB:
def __init__(self, alpha=1.0):
self.alpha = alpha
def fit(self, docs, labels):
# 构建词汇表
vocab = set(word for doc in docs for word in doc)
self.vocab = {word: i for i, word in enumerate(vocab)}
self.n_vocab = len(vocab)
# 统计
class_counts = defaultdict(int)
word_counts = defaultdict(lambda: np.zeros(self.n_vocab))
total_words = defaultdict(int)
for doc, label in zip(docs, labels):
class_counts[label] += 1
for word in doc:
if word in self.vocab:
word_counts[label][self.vocab[word]] += 1
total_words[label] += 1
# 先验概率
total_docs = len(labels)
self.priors = {label: count / total_docs for label, count in class_counts.items()}
# 条件概率(带平滑)
self.cond_prob = {}
for label in class_counts:
smoothed_counts = word_counts[label] + self.alpha
smoothed_total = total_words[label] + self.alpha * self.n_vocab
self.cond_prob[label] = smoothed_counts / smoothed_total
def predict(self, docs):
predictions = []
for doc in docs:
scores = {}
for label in self.priors:
log_score = np.log(self.priors[label])
for word in doc:
if word in self.vocab:
idx = self.vocab[word]
log_score += np.log(self.cond_prob[label][idx])
scores[label] = log_score
predictions.append(max(scores, key=scores.get))
return predictions
# 使用
docs = [["科技","人工智能","发展","迅速"], ["科技","公司","发布","新产品"], ["体育","比赛","精彩","激烈"]]
nb = MultinomialNB(alpha=1.0)
nb.fit(docs, [0,0,1])
print(nb.predict()) # 输出: [0]import java.util.*;
public class MultinomialNaiveBayes {
private Map<String, Integer> vocab = new HashMap<>();
private Map<Integer, Double> priors = new HashMap<>();
private Map<Integer, double[]> condProb = new HashMap<>();
private int vocabSize;
private double alpha;
public MultinomialNaiveBayes(double alpha) {
this.alpha = alpha;
}
public void fit(List<List<String>> docs, List<Integer> labels) {
// 构建词汇表
Set<String> wordSet = new HashSet<>();
for (List<String> doc : docs) {
wordSet.addAll(doc);
}
int idx = 0;
for (String word : wordSet) {
vocab.put(word, idx++);
}
vocabSize = vocab.size();
// 统计类别文档数
Map<Integer, Integer> classDocCount = new HashMap<>();
for (int label : labels) {
classDocCount.put(label, classDocCount.getOrDefault(label, 0) + 1);
}
// 统计词频和总词数
Map<Integer, int[]> wordFreq = new HashMap<>();
Map<Integer, Integer> totalWords = new HashMap<>();
for (int label : classDocCount.keySet()) {
wordFreq.put(label, new int[vocabSize]);
totalWords.put(label, 0);
}
for (int i = 0; i < docs.size(); i++) {
int label = labels.get(i);
for (String word : docs.get(i)) {
if (vocab.containsKey(word)) {
int wIdx = vocab.get(word);
wordFreq.get(label)[wIdx]++;
totalWords.put(label, totalWords.get(label) + 1);
}
}
}
// 先验概率
int totalDocs = labels.size();
for (int label : classDocCount.keySet()) {
priors.put(label, (double) classDocCount.get(label) / totalDocs);
}
// 条件概率(拉普拉斯平滑)
for (int label : classDocCount.keySet()) {
double[] prob = new double[vocabSize];
int totalWordCount = totalWords.get(label);
for (int i = 0; i < vocabSize; i++) {
prob[i] = (wordFreq.get(label)[i] + alpha) / (totalWordCount + alpha * vocabSize);
}
condProb.put(label, prob);
}
}
public int predict(List<String> doc) {
Map<Integer, Double> scores = new HashMap<>();
for (int label : priors.keySet()) {
double logScore = Math.log(priors.get(label));
for (String word : doc) {
if (vocab.containsKey(word)) {
int wIdx = vocab.get(word);
logScore += Math.log(condProb.get(label)[wIdx]);
}
}
scores.put(label, logScore);
}
return Collections.max(scores.entrySet(), Map.Entry.comparingByValue()).getKey();
}
// 测试
public static void main(String[] args) {
List<List<String>> docs = Arrays.asList(
Arrays.asList("科技", "人工智能", "发展", "迅速"),
Arrays.asList("科技", "公司", "发布", "新产品"),
Arrays.asList("体育", "比赛", "精彩", "激烈")
);
List<Integer> labels = Arrays.asList(0, 0, 1);
MultinomialNaiveBayes nb = new MultinomialNaiveBayes(1.0);
nb.fit(docs, labels);
List<String> testDoc = Arrays.asList("人工智能", "比赛");
int prediction = nb.predict(testDoc);
System.out.println("预测结果: " + (prediction == 0 ? "科技" : "体育"));
// 输出: 科技
}
}优点 | 缺点 |
|---|---|
✅ 天然适配文本词频数据 | ❌ 假设词之间独立(忽略语序和上下文) |
✅ 训练和预测极快 | ❌ 对罕见词敏感(需平滑) |
✅ 小样本下表现优异 | ❌ 无法处理未登录词(OOV) |
✅ 概率输出可解释 | ❌ 词频可能被长文档主导(可配合TF-IDF) |
本系列将持续更新以下算法,每篇均包含:
即将发布:
多项式朴素贝叶斯用最朴素的独立假设,驾驭了最复杂的语言世界。它不理解语法,却能读懂情感;它不分析逻辑,却能分辨主题。
记住:在NLP的浩瀚海洋中,有时简单统计就是最强大的罗盘。
现在,你已经能:
关键词:机器学习、多项式朴素贝叶斯、Multinomial Naive Bayes、文本分类、词频、拉普拉斯平滑、新闻分类、情感分析、Python 多项式朴素贝叶斯、Java 多项式朴素贝叶斯、手动计算
相关链接
无论你是想写代码调用 API 的开发者,设计 AI 产品的 PM,评估技术路线的管理者,还是单纯好奇智能本质的思考者,这里都有值得你驻足的内容。 不追 hype,只讲逻辑;不谈玄学,专注可复现的认知。 让我们一起,在这场百年一遇的智能革命中,看得更清,走得更稳 https://cloud.tencent.com/developer/column/107314
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。