
朴素贝叶斯就像一位经验丰富的裁判,通过观察选手的特征表现(如速度、力量)快速判断其属于哪支队伍(类别)。它的核心假设是:所有特征相互独立(“朴素”一词的由来)。尽管现实世界中特征可能相关,但这种简化能让计算大幅加速。
数学本质:基于贝叶斯定理
P(类别∣特征)=P(特征∣类别)P(类别)P(特征)P(类别∣特征)=P(特征)P(特征∣类别)P(类别)
实际计算中只需比较分子大小,因为分母对所有类别相同。
import java.util.*;
public class NaiveBayes {
// 存储每个类别的词频统计
private Map<String, Map<String, Integer>> classWordCounts = new HashMap<>();
private Map<String, Integer> classDocCounts = new HashMap<>();
private Set<String> vocabulary = new HashSet<>();
// 训练模型
public void train(List<String> documents, List<String> labels) {
for (int i = 0; i < documents.size(); i++) {
String doc = documents.get(i);
String label = labels.get(i);
classDocCounts.put(label, classDocCounts.getOrDefault(label, 0) + 1);
Map<String, Integer> wordCounts = classWordCounts.getOrDefault(label, new HashMap<>());
for (String word : doc.split(" ")) {
wordCounts.put(word, wordCounts.getOrDefault(word, 0) + 1);
vocabulary.add(word);
}
classWordCounts.put(label, wordCounts);
}
}
// 预测类别
public String predict(String document) {
double maxProb = Double.NEGATIVE_INFINITY;
String bestLabel = "";
for (String label : classDocCounts.keySet()) {
double logProb = Math.log((double)classDocCounts.get(label) / classDocCounts.values().stream().mapToInt(i->i).sum());
for (String word : document.split(" ")) {
int count = classWordCounts.get(label).getOrDefault(word, 0);
logProb += Math.log((count + 1.0) / (classWordCounts.get(label).values().stream().mapToInt(i->i).sum() + vocabulary.size()));
}
if (logProb > maxProb) {
maxProb = logProb;
bestLabel = label;
}
}
return bestLabel;
}
public static void main(String[] args) {
NaiveBayes model = new NaiveBayes();
List<String> docs = Arrays.asList(
"free money urgent win", "hello meeting tomorrow",
"lottery prize claim", "project update schedule"
);
List<String> labels = Arrays.asList("spam", "ham", "spam", "ham");
model.train(docs, labels);
System.out.println(model.predict("free lottery")); // 输出"spam"
}
}指标 | 数值 | 说明 |
|---|---|---|
训练时间复杂度 | O(n*d) | n=样本数,d=平均特征数 |
预测时间复杂度 | O(c*d) | c=类别数,d=特征数 |
空间复杂度 | O(c*d + v) | v=词表大小,d=特征维度,c=类别数 |
注:使用对数概率避免浮点数下溢,通过拉普拉斯平滑处理未登录词。
新手入门:
成手进阶:
处理连续特征:实现高斯朴素贝叶斯
// 高斯概率密度函数
private double gaussianProb(double x, double mean, double std) {
return (1/(std*Math.sqrt(2*Math.PI))) * Math.exp(-(x-mean)*(x-mean)/(2*std*std));
}突破独立性假设:
分布式优化:
朴素贝叶斯的优雅在于用简单假设实现高效分类。虽然其“独立性假设”看似天真,但在文本分类等场景中表现出色——如同一位经验丰富的裁判,虽不深究特征间复杂关系,却能快速做出准确判断。正如其在垃圾邮件过滤中的成功应用所示,有时简单的模型反而是工程实践的最佳选择。