使用NumPy实现高斯朴素贝叶斯分类器:鸢尾花数据集全流程教程

使用NumPy实现高斯朴素贝叶斯分类器:鸢尾花数据集全流程教程

1. 高斯朴素贝叶斯算法原理

高斯朴素贝叶斯是基于贝叶斯定理和特征条件独立假设的分类算法。它假设每个特征在给定类别下服从高斯分布(正态分布),适用于连续型特征数据。

1.1 贝叶斯定理公式

朴素贝叶斯分类器的核心是贝叶斯定理: $$P(y|x_1,x_2,...,x_n) = \frac{P(y)\prod_{i=1}^n P(x_i|y)}{P(x_1,x_2,...,x_n)}$$

其中: - P(y|x1, x2, ..., xn) 是后验概率(给定特征下属于某类的概率) - P(y) 是先验概率(各类别的初始概率) - P(xi|y) 是似然概率(某类别下特征出现的概率) - 分母是证据因子,在实际比较中可以忽略

1.2 高斯概率密度函数

对于连续特征,使用高斯概率密度函数: $$P(x_i|y) = \frac{1}{\sqrt{2\pi\sigma_y^2}} \exp\left(-\frac{(x_i - \mu_y)^2}{2\sigma_y^2}\right)$$ 其中 μy 是特征在类别 y 下的均值,σy 是标准差。

2. 鸢尾花数据集介绍

鸢尾花数据集包含3类鸢尾花(山鸢尾、变色鸢尾、维吉尼亚鸢尾),每类50个样本,每个样本有4个特征: - 花萼长度(sepal length) - 花萼宽度(sepal width) - 花瓣长度(petal length) - 花瓣宽度(petal width)

3. 环境准备与数据加载

首先导入必要的库并加载数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

# 加载鸢尾花数据集
iris = load_iris()
X = iris.data # 特征数据
y = iris.target # 标签数据
feature_names = iris.feature_names # 特征名称
target_names = iris.target_names # 类别名称

print("数据集形状:", X.shape,y.shape)
print("特征名称:", feature_names)
print("类别名称:", target_names)
print("各类别样本数:", np.bincount(y))

4. 数据预处理与标准化

数据标准化是将特征数据缩放到均值为0,标准差为1的分布,这有助于提高高斯朴素贝叶斯的性能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 划分训练集和测试集(70%训练,30%测试)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y
)

train_mean = np.mean(X_train, axis=0)
train_std = np.std(X_train, axis=0)

def standardize_data(X,mean,std):
"""
对数据进行标准化处理
"""
X_standardized = (X - mean) / std # 标准化公式
return X_standardized

X_train_standardized = standardize_data(X_train, train_mean, train_std)
X_test_standardized = standardize_data(X_test, train_mean, train_std)

print("训练集形状:", X_train_standardized.shape)
print("测试集形状:", X_test_standardized.shape)

5. 高斯朴素贝叶斯分类器实现

下面是用NumPy从头实现高斯朴素贝叶斯分类器的完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
class GaussianNaiveBayes:
"""
高斯朴素贝叶斯分类器实现
"""

def __init__(self):
self.classes = None
self.priors = {} # 先验概率
self.means = {} # 每个类别下特征的均值
self.stds = {} # 每个类别下特征的标准差

def fit(self, X, y):
"""
训练模型:计算先验概率、均值和标准差
"""
self.classes = np.unique(y)

for c in self.classes:
# 获取当前类别的所有样本
X_c = X[y == c]

# 计算先验概率(该类样本数占总样本数的比例)
self.priors[c] = X_c.shape[0] / X.shape[0]

# 计算每个特征的均值和标准差
self.means[c] = np.mean(X_c, axis=0)
self.stds[c] = np.std(X_c, axis=0)

# 避免标准差为0导致除零错误(添加一个很小的值)
self.stds[c] = np.where(self.stds[c] == 0, 1e-9, self.stds[c])

def _gaussian_pdf(self, x, mean, std):
"""
计算高斯概率密度函数
"""
exponent = np.exp(-0.5 * ((x - mean) ** 2) / (std ** 2))
return (1 / (np.sqrt(2 * np.pi) * std)) * exponent

def _calculate_log_likelihood(self, x, c):
"""
计算对数似然概率(使用对数防止数值下溢)
"""
likelihood = 0
for i in range(len(x)):
# 使用高斯PDF计算每个特征的条件概率
prob = self._gaussian_pdf(x[i], self.means[c][i], self.stds[c][i])
# 使用对数相加代替概率相乘,避免数值下溢
likelihood += np.log(prob + 1e-9) # 添加小值避免log(0)
return likelihood

def predict_single(self, x):
"""
预测单个样本的类别
"""
best_class = None
max_log_posterior = -np.inf

for c in self.classes:
# 计算对数先验概率
log_prior = np.log(self.priors[c])
# 计算对数似然概率
log_likelihood = self._calculate_log_likelihood(x, c)
# 对数后验概率 = 对数先验 + 对数似然
log_posterior = log_prior + log_likelihood

if log_posterior > max_log_posterior:
max_log_posterior = log_posterior
best_class = c

return best_class

def predict(self, X):
"""
预测多个样本的类别
"""
predictions = []
for x in X:
predictions.append(self.predict_single(x))
return np.array(predictions)

def score(self, X, y):
"""
计算模型准确率
"""
y_pred = self.predict(X)
accuracy = np.sum(y_pred == y) / len(y)
return accuracy

6. 模型训练与评估

现在使用我们实现的分类器进行训练和预测:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 创建模型实例
gnb = GaussianNaiveBayes()

# 训练模型
gnb.fit(X_train_standardized, y_train)

# 在测试集上进行预测
y_pred = gnb.predict(X_test_standardized)

# 计算准确率
accuracy = gnb.score(X_test_standardized, y_test)
print(f"模型准确率: {accuracy:.4f}")

# 详细评估结果
from sklearn.metrics import classification_report, confusion_matrix

print("\n分类报告:")
print(classification_report(y_test, y_pred, target_names=target_names))

print("混淆矩阵:")
print(confusion_matrix(y_test, y_pred))

7. 特征条件概率矩阵可视化

为了更好地理解模型,我们可以可视化每个类别下特征的高斯分布:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def visualize_feature_distributions(model, feature_names, target_names):
"""
可视化每个特征在不同类别下的高斯分布
"""
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
axes = axes.ravel()

# 生成测试数据点(标准化后的范围)
x_range = np.linspace(-3, 3, 100)

for feature_idx in range(4):
for c in model.classes:
mean = model.means[c][feature_idx]
std = model.stds[c][feature_idx]

# 计算高斯分布
y_dist = (1 / (np.sqrt(2 * np.pi) * std)) * \
np.exp(-0.5 * ((x_range - mean) ** 2) / (std ** 2))

axes[feature_idx].plot(x_range, y_dist,
label=f'{target_names[c]}')

axes[feature_idx].set_title(f'{feature_names[feature_idx]}分布')
axes[feature_idx].set_xlabel('标准化值')
axes[feature_idx].set_ylabel('概率密度')
axes[feature_idx].legend()

plt.tight_layout()
plt.show()

# 调用可视化函数
visualize_feature_distributions(gnb, feature_names, target_names)

8. 关键知识点总结

8.1 数值稳定性处理

在实际实现中,我们需要注意数值稳定性: - 避免除零错误:为标准差添加一个小值(如1e-9) - 防止数值下溢:使用对数概率代替原始概率相乘 - 拉普拉斯平滑:对于概率计算添加平滑项

8.2 算法优缺点

优点: - 简单易懂,实现容易 - 训练和预测速度快 - 对小规模数据表现良好 - 对缺失数据不敏感

缺点: - 特征独立性假设在现实中往往不成立 - 对输入数据的分布假设比较严格 - 当特征相关性较强时性能可能下降

8.3 应用场景

高斯朴素贝叶斯特别适用于: - 特征为连续值的分类问题 - 需要快速原型验证的场景 - 数据维度较高的文本分类(配合TF-IDF等特征提取) - 实时预测系统

9. 进一步学习建议

  1. 尝试不同的数据集:如帕金森数据集,比较不同数据集上的表现
  2. 实现其他变种:如多项式朴素贝叶斯(用于离散特征)和伯努利朴素贝叶斯
  3. 参数调优:研究平滑参数对模型性能的影响
  4. 与其他算法对比:比较与逻辑回归、SVM等算法的性能差异

通过本教程,您应该已经掌握了使用NumPy实现高斯朴素贝叶斯分类器的完整流程,包括数据预处理、模型实现、训练预测和结果评估。这种从零开始的实现方式有助于深入理解算法原理和实际应用中的各种细节考虑。