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

朴素贝叶斯分类器 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): 预测新样本的类别 pass2.2 fit函数实现训练过程fit函数需要完成两个核心计算def fit(self, X, y): # 计算类别的先验概率 unique_labels, counts np.unique(y, return_countsTrue) 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_countsTrue) # 计算当前特征在当前类别下的概率分布 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(欺诈|特征) 03.2 拉普拉斯平滑实现拉普拉斯平滑通过在分子加1分母加类别数来避免零概率def fit_with_smoothing(self, X, y, alpha1): # 计算带平滑的类先验概率 unique_labels, counts np.unique(y, return_countsTrue) 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(count1)/(NV)未出现过的特征值01/(NV)未出现过的类别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(subsettrain, categoriescategories) newsgroups_test fetch_20newsgroups(subsettest, categoriescategories) # 文本向量化 vectorizer CountVectorizer(stop_wordsenglish, max_features1000) 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.target4.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 性能优化技巧特征选择使用TF-IDF替代词频统计from sklearn.feature_extraction.text import TfidfVectorizer tfidf TfidfVectorizer(stop_wordsenglish, max_features1000)对数概率计算防止数值下溢# 在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])处理连续特征高斯朴素贝叶斯# 计算均值和方差 self.means {label: np.mean(X[y label], axis0) for label in unique_labels} self.vars {label: np.var(X[y label], axis0) 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)) * exponent5. 算法对比与工程实践5.1 不同朴素贝叶斯变体对比类型特征假设适用场景Scikit-learn实现类高斯连续值正态分布数值型数据GaussianNB多项式离散值多项式分布文本分类词频MultinomialNB伯努利二元特征文本分类出现/不出现BernoulliNB5.2 实际应用中的注意事项特征相关性处理使用特征选择降低相关性考虑半朴素贝叶斯放松独立性假设类别不平衡处理# 在fit方法中调整类先验 class_weights {0: 0.7, 1: 0.3} # 根据业务需求调整 self.label_prob {label: weight for label, weight in class_weights.items()}大数据集处理增量学习partial_fit方法分布式实现如Spark MLlib5.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_wordsenglish, max_features10000)), (tfidf, TfidfTransformer()), (clf, MultinomialNB(alpha0.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})朴素贝叶斯分类器虽然理论基础简单但在实际应用中通过合理的特征工程和参数调整往往能获得与复杂模型相媲美的性能。特别是在文本分类、垃圾邮件过滤等场景中它仍然是首选的基准模型之一。