共计 1925 个字符,预计需要花费 5 分钟才能阅读完成。
朴素贝叶斯算法在文本分类中表现优异,尤其适合处理小样本数据和高维特征。它的计算效率极高,且对缺失数据不敏感,这些特点使其成为垃圾邮件过滤等场景的理想选择。下面我们就用 C# 一步步实现这个经典算法。

文本预处理
-
分词处理
对于英文文本,我们可以直接用空格分割,但中文需要额外分词库:// 使用 Jieba.NET 中文分词 var segmenter = new JiebaSegmenter(); var words = segmenter.Cut("您的信用卡账单已生成"); -
停用词过滤
常见无意义词会降低模型效果:var stopWords = new HashSet<string>{"的", "和", "是"}; words = words.Where(w => !stopWords.Contains(w)); -
词干提取
英文需要统一单词形式(如 running→run):var stemmer = new PorterStemmer(); string stemmed = stemmer.StemWord("running");
核心算法实现
我们先定义一个朴素贝叶斯类框架:
public class NaiveBayesClassifier
{private Dictionary<string, int> spamWordCounts = new();
private Dictionary<string, int> hamWordCounts = new();
private int spamTotal, hamTotal;
// 添加训练样本
public void AddTrainingSample(string text, bool isSpam) {/*...*/}
// 进行分类预测
public bool Classify(string text) {/*...*/}
}
关键步骤拆解
-
先验概率计算
// P(Spam) = 垃圾邮件总数 / 总邮件数 double pSpam = (double)spamTotal / (spamTotal + hamTotal); -
条件概率计算
使用拉普拉斯平滑避免零概率问题:// P(word|Spam) = (词在垃圾邮件中出现次数 + 1) / (垃圾邮件总词数 + 唯一词数) double pWordGivenSpam = (spamWordCounts.GetValueOrDefault(word, 0) + 1) / (double)(spamTotal + vocabularySize); -
联合概率计算
对文本中所有词的概率取对数相加,防止数值下溢:// 使用 log 转换乘法为加法 double spamScore = Math.Log(pSpam); foreach(var word in words) spamScore += Math.Log(GetWordProbability(word, true));
性能优化技巧
- 内存优化
- 使用哈希值存储长词
-
定期清理低频词(出现次数 <3)
-
多线程训练
Parallel.ForEach(trainingData, item => {lock (lockObj) {classifier.AddTrainingSample(item.Text, item.IsSpam); } });
端到端示例
加载公开数据集并训练:
var dataset = File.ReadLines("SMSSpamCollection")
.Select(line => line.Split('\t'))
.Select(parts => new {IsSpam = parts[0] == "spam",
Text = parts[1]
});
var nb = new NaiveBayesClassifier();
foreach(var item in dataset)
nb.AddTrainingSample(item.Text, item.IsSpam);
// 测试
Console.WriteLine(nb.Classify("WIN FREE IPHONE")); // true
Console.WriteLine(nb.Classify("Meeting at 3pm")); // false
生产环境建议
- 特征选择
- 使用卡方检验选取 Top 500 最具区分度的词
-
去除 URL、特殊符号等干扰项
-
处理数值下溢
- 始终使用对数概率计算
-
设置最小概率阈值(如 1e-10)
-
模型持久化
// 使用 ProtoBuf.NET 序列化 Serializer.SerializeToFile("model.bin", classifier);
思考与改进
- 一词多义问题可以通过:
- 引入词性标注
-
结合上下文窗口(n-gram)
-
增量训练建议:
- 定期用新数据微调模型
- 实现模型版本控制
通过这个实战项目,我们发现朴素贝叶斯虽然简单,但在文本分类任务中表现惊人。建议读者尝试调整特征选择策略,或者将该分类器扩展到其他语言场景。
正文完
