news 2026/7/6 15:06:50

朴素贝叶斯分类器 Python 实现:从零构建 2 个核心函数与拉普拉斯平滑

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
朴素贝叶斯分类器 Python 实现:从零构建 2 个核心函数与拉普拉斯平滑

朴素贝叶斯分类器 Python 实现:从零构建核心函数与拉普拉斯平滑实战

1. 朴素贝叶斯算法原理精要

朴素贝叶斯分类器基于贝叶斯定理构建,其核心公式为:

P(y|x) = P(x|y) * P(y) / P(x)

其中:

  • P(y|x)是给定特征x时类别y的后验概率
  • P(x|y)是似然概率(类别y中特征x出现的概率)
  • P(y)是类别y的先验概率
  • P(x)是特征x的边缘概率

关键假设:所有特征条件独立,即:

P(x1,x2,...,xn|y) = P(x1|y)*P(x2|y)*...*P(xn|y)

这种独立性假设虽然"朴素",但在实际应用中往往表现惊人地好,特别是在文本分类等场景。

2. 核心函数实现:从零构建分类器

2.1 初始化与数据结构设计

我们首先定义NaiveBayesClassifier类的基本结构:

import numpy as np class NaiveBayesClassifier: def __init__(self): self.label_prob = {} # 存储类别的先验概率 P(y) self.condition_prob = {} # 存储条件概率 P(x|y) def fit(self, X, y): """训练模型,计算先验概率和条件概率""" pass def predict(self, X): """预测新样本的类别""" pass

2.2 fit函数实现:训练过程

fit函数需要完成两个核心计算:

def fit(self, X, y): # 计算类别的先验概率 unique_labels, counts = np.unique(y, return_counts=True) total_samples = len(y) self.label_prob = {label: count/total_samples for label, count in zip(unique_labels, counts)} # 计算条件概率 P(x|y) for label in unique_labels: # 获取当前类别的所有样本 label_samples = X[y == label] self.condition_prob[label] = {} for feature_idx in range(X.shape[1]): # 遍历每个特征 feature_values = label_samples[:, feature_idx] unique_values, value_counts = np.unique(feature_values, return_counts=True) # 计算当前特征在当前类别下的概率分布 self.condition_prob[label][feature_idx] = { value: count/len(label_samples) for value, count in zip(unique_values, value_counts) }

2.3 predict函数实现:预测过程

预测时需要计算每个类别的后验概率并选择最大值:

def predict(self, X): predictions = [] for sample in X: max_prob = -1 predicted_label = None # 遍历所有可能的类别 for label in self.label_prob: # 初始化当前类别的概率为先验概率 current_prob = self.label_prob[label] # 计算所有特征的条件概率乘积 for feature_idx, feature_value in enumerate(sample): # 如果特征值在训练集中未出现过,跳过该特征 if feature_value in self.condition_prob[label][feature_idx]: current_prob *= self.condition_prob[label][feature_idx][feature_value] # 选择概率最大的类别 if current_prob > max_prob: max_prob = current_prob predicted_label = label predictions.append(predicted_label) return np.array(predictions)

3. 零概率问题与拉普拉斯平滑

3.1 零概率问题分析

当测试集中出现训练集未出现过的特征值时,会导致条件概率为0,进而使整个后验概率为0。例如:

训练集中"购买金额=1000"在"类别=欺诈"中从未出现 则 P(购买金额=1000|欺诈) = 0 导致 P(欺诈|特征) = 0

3.2 拉普拉斯平滑实现

拉普拉斯平滑通过在分子加1,分母加类别数来避免零概率:

def fit_with_smoothing(self, X, y, alpha=1): # 计算带平滑的类先验概率 unique_labels, counts = np.unique(y, return_counts=True) total_samples = len(y) self.label_prob = { label: (count + alpha) / (total_samples + alpha * len(unique_labels)) for label, count in zip(unique_labels, counts) } # 计算带平滑的条件概率 for label in unique_labels: label_samples = X[y == label] self.condition_prob[label] = {} for feature_idx in range(X.shape[1]): feature_values = label_samples[:, feature_idx] unique_values = np.unique(np.concatenate([feature_values, np.unique(X[:, feature_idx])])) value_counts = {value: 0 for value in unique_values} for value in feature_values: value_counts[value] += 1 # 应用拉普拉斯平滑 total_count = len(label_samples) num_unique_values = len(unique_values) self.condition_prob[label][feature_idx] = { value: (count + alpha) / (total_count + alpha * num_unique_values) for value, count in value_counts.items() }

3.3 平滑效果对比

场景传统方法概率平滑后概率
出现过的特征值count/N(count+1)/(N+V)
未出现过的特征值01/(N+V)
未出现过的类别01/(总样本数+K)

其中:

  • N:类别中的样本数
  • V:特征的可能取值数
  • K:类别总数
  • count:特征值出现次数

4. 实战应用:新闻分类示例

4.1 数据准备与预处理

from sklearn.datasets import fetch_20newsgroups from sklearn.feature_extraction.text import CountVectorizer # 加载数据 categories = ['sci.space', 'rec.sport.baseball', 'talk.politics.mideast'] newsgroups_train = fetch_20newsgroups(subset='train', categories=categories) newsgroups_test = fetch_20newsgroups(subset='test', categories=categories) # 文本向量化 vectorizer = CountVectorizer(stop_words='english', max_features=1000) X_train = vectorizer.fit_transform(newsgroups_train.data).toarray() y_train = newsgroups_train.target X_test = vectorizer.transform(newsgroups_test.data).toarray() y_test = newsgroups_test.target

4.2 模型训练与评估

# 初始化并训练模型 nb = NaiveBayesClassifier() nb.fit_with_smoothing(X_train, y_train) # 预测并评估 from sklearn.metrics import accuracy_score y_pred = nb.predict(X_test) print(f"准确率: {accuracy_score(y_test, y_pred):.2f}")

4.3 性能优化技巧

  1. 特征选择:使用TF-IDF替代词频统计

    from sklearn.feature_extraction.text import TfidfVectorizer tfidf = TfidfVectorizer(stop_words='english', max_features=1000)
  2. 对数概率计算:防止数值下溢

    # 在predict函数中使用对数概率 log_prob = np.log(self.label_prob[label]) for feature_idx, feature_value in enumerate(sample): if feature_value in self.condition_prob[label][feature_idx]: log_prob += np.log(self.condition_prob[label][feature_idx][feature_value])
  3. 处理连续特征:高斯朴素贝叶斯

    # 计算均值和方差 self.means = {label: np.mean(X[y == label], axis=0) for label in unique_labels} self.vars = {label: np.var(X[y == label], axis=0) for label in unique_labels} # 高斯概率密度计算 def gaussian_prob(self, x, mean, var): exponent = np.exp(-((x - mean)**2) / (2 * var)) return (1 / np.sqrt(2 * np.pi * var)) * exponent

5. 算法对比与工程实践

5.1 不同朴素贝叶斯变体对比

类型特征假设适用场景Scikit-learn实现类
高斯连续值,正态分布数值型数据GaussianNB
多项式离散值,多项式分布文本分类(词频)MultinomialNB
伯努利二元特征文本分类(出现/不出现)BernoulliNB

5.2 实际应用中的注意事项

  1. 特征相关性处理

    • 使用特征选择降低相关性
    • 考虑半朴素贝叶斯(放松独立性假设)
  2. 类别不平衡处理

    # 在fit方法中调整类先验 class_weights = {0: 0.7, 1: 0.3} # 根据业务需求调整 self.label_prob = {label: weight for label, weight in class_weights.items()}
  3. 大数据集处理

    • 增量学习(partial_fit方法)
    • 分布式实现(如Spark MLlib)

5.3 性能优化实战代码

from sklearn.naive_bayes import MultinomialNB from sklearn.pipeline import Pipeline from sklearn.feature_extraction.text import TfidfTransformer # 构建优化后的文本分类管道 text_clf = Pipeline([ ('vect', CountVectorizer(stop_words='english', max_features=10000)), ('tfidf', TfidfTransformer()), ('clf', MultinomialNB(alpha=0.1)), ]) # 训练和评估 text_clf.fit(newsgroups_train.data, newsgroups_train.target) predicted = text_clf.predict(newsgroups_test.data) print(f"优化后准确率: {np.mean(predicted == newsgroups_test.target):.2f}")

朴素贝叶斯分类器虽然理论基础简单,但在实际应用中通过合理的特征工程和参数调整,往往能获得与复杂模型相媲美的性能。特别是在文本分类、垃圾邮件过滤等场景中,它仍然是首选的基准模型之一。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/6 15:02:09

Java开发日常:掌握这几个 Linux 核心操作就够了

为什么需要学习Linux系统的使用? 因为在真实工作中,很多中间件以及我们的应用服务,通常都是部署在 Linux 服务器 上,而不是 Windows 系统。 所以,Linux 的常见操作是开发人员必须掌握的基础能力。 另外,Linux 的使用方式和 Windows 也不太一样。 Windows 更多是通过图形…

作者头像 李华
网站建设 2026/7/6 14:58:45

红队测试插件开发_redteam-plugin-development

以下为本文档的中文说明该技能用于开发Promptfoo红队测试插件,帮助安全团队对AI应用进行对抗性测试。它支持自定义测试策略、注入攻击模拟、输出评估和报告生成。Promptfoo是一个AI安全评估框架,此技能扩展其红队测试能力,适用于需要系统性评…

作者头像 李华
网站建设 2026/7/6 14:58:07

代码分析工具_agent-code-analyzer

以下为本文档的中文说明该技能用于执行代码分析任务,涵盖静态代码分析、依赖检查、复杂度度量、安全漏洞扫描和代码质量评估。它帮助开发团队自动发现代码中的潜在问题。适用于CI/CD流水线中的代码质量门禁和开发过程中的代码审查辅助,提升整体代码质量。…

作者头像 李华
网站建设 2026/7/6 14:57:17

5个理由告诉你为什么Notepad--应该是你的首选跨平台文本编辑器

5个理由告诉你为什么Notepad--应该是你的首选跨平台文本编辑器 【免费下载链接】notepad-- 一个支持windows/linux/mac的文本编辑器,目标是做中国人自己的编辑器,来自中国。 项目地址: https://gitcode.com/GitHub_Trending/no/notepad-- 你是否曾…

作者头像 李华
网站建设 2026/7/6 14:49:14

题解:学而思编程 数字卡片

【题目来源】 学而思编程:数字卡片 【题目描述】 小猴收集了 nnn 张数字卡片,每一种数字卡片小猴恰好只有一张且都在 1∼n1\sim n1∼n 的范围内。 因为小猴加的猫咪贪玩,打翻了装着数字卡片的盒子,经过千辛万苦小猴终于把卡片…

作者头像 李华
网站建设 2026/7/6 14:48:47

Gazebo仿真PX4+3D激光雷达的搭建

Gazebo仿真无人机3D激光雷达的搭建 系统要求:Ubuntu22、24及以上版本。 以PX4开源工程为基础,先参考官方文档 https://docs.px4.io/main/en/sim_gazebo_gz/ 搭建基本的Gazebo无人机仿真,注意安装Gazebo而非老版本Gazebo classic。跑通make …

作者头像 李华