朴素贝叶斯是基于贝叶斯定理与特征条件独立假设的经典分类算法,因其简单高效、对小规模数据表现优异,常被用于分类场景。本文将以 “西瓜品质分类” 为例,用 Python 实现朴素贝叶斯分类器,区分 “好瓜” 与 “坏瓜”。
一、核心思路
朴素贝叶斯的核心是贝叶斯定理:P(Y∣X)=P(X)P(X∣Y)P(Y),其中:
- P(Y):先验概率(如 “好瓜 = 是” 的概率);
- P(X∣Y):条件概率(特征 X 在类别 Y 下的概率);
- P(Y∣X):后验概率(样本 X 属于类别 Y 的概率)。
针对西瓜数据集的特点,需区分两种特征处理方式:
- 离散特征(色泽、根蒂、敲声等):用 “拉普拉斯平滑” 计算条件概率,避免概率为 0;
- 连续特征(密度、含糖率):假设服从正态分布,通过均值、标准差计算概率密度。
最终比较 “好瓜” 和 “坏瓜” 的后验概率,概率大的即为预测类别。
二、完整实现代码
import numpy as np
from scipy.stats import norm # 用于计算正态分布的概率密度
# ---------------------- 1. 准备训练数据和测试样本 ----------------------
# 训练数据(对应表格中1-17号样本)
train_data = [
# 色泽, 根蒂, 敲声, 纹理, 脐部, 触感, 密度, 含糖率, 好瓜(1=是,0=否)
["青绿", "蜷缩", "浊响", "清晰", "凹陷", "硬滑", 0.697, 0.460, 1],
["乌黑", "蜷缩", "沉闷", "清晰", "凹陷", "硬滑", 0.774, 0.376, 1],
["乌黑", "蜷缩", "浊响", "清晰", "凹陷", "硬滑", 0.634, 0.264, 1],
["青绿", "蜷缩", "沉闷", "清晰", "凹陷", "硬滑", 0.608, 0.318, 1],
["浅白", "蜷缩", "浊响", "清晰", "凹陷", "硬滑", 0.556, 0.215, 1],
["青绿", "稍蜷", "浊响", "清晰", "稍凹", "软粘", 0.403, 0.237, 1],
["乌黑", "稍蜷", "浊响", "稍糊", "稍凹", "软粘", 0.481, 0.149, 1],
["乌黑", "稍蜷", "浊响", "清晰", "稍凹", "硬滑", 0.437, 0.211, 1],
["乌黑", "稍蜷", "沉闷", "稍糊", "稍凹", "硬滑", 0.666, 0.091, 0],
["青绿", "硬挺", "清脆", "清晰", "平坦", "软粘", 0.243, 0.267, 0],
["浅白", "硬挺", "清脆", "模糊", "平坦", "硬滑", 0.245, 0.057, 0],
["浅白", "蜷缩", "浊响", "模糊", "平坦", "软粘", 0.343, 0.099, 0],
["青绿", "稍蜷", "浊响", "稍糊", "凹陷", "硬滑", 0.639, 0.161, 0],
["浅白", "稍蜷", "沉闷", "稍糊", "凹陷", "硬滑", 0.657, 0.198, 0],
["乌黑", "稍蜷", "浊响", "清晰", "稍凹", "软粘", 0.360, 0.370, 0],
["浅白", "蜷缩", "浊响", "模糊", "平坦", "硬滑", 0.593, 0.042, 0],
["青绿", "蜷缩", "沉闷", "稍糊", "稍凹", "硬滑", 0.719, 0.103, 0],
]
# 测试样本(测1)
test_sample = ["青绿", "蜷缩", "浊响", "清晰", "凹陷", "硬滑", 0.697, 0.460]
# ---------------------- 2. 分离“好瓜=是”和“好瓜=否”的样本 ----------------------
good_melons = [d for d in train_data if d[-1] == 1] # 好瓜样本
bad_melons = [d for d in train_data if d[-1] == 0] # 坏瓜样本
total = len(train_data)
P_good = len(good_melons) / total # 好瓜的先验概率
P_bad = len(bad_melons) / total # 坏瓜的先验概率
# ---------------------- 3. 计算离散特征的条件概率(拉普拉斯平滑) ----------------------
# 离散特征的索引(色泽、根蒂、敲声、纹理、脐部、触感)
discrete_features = [0, 1, 2, 3, 4, 5]
feature_names = ["色泽", "根蒂", "敲声", "纹理", "脐部", "触感"]
def calc_discrete_prob(samples, feature_idx, feature_val):
"""计算离散特征的条件概率(拉普拉斯平滑)"""
feature_values = [d[feature_idx] for d in samples]
count = feature_values.count(feature_val)
# 拉普拉斯平滑:分子+1,分母+特征的可能取值数
unique_vals = len(set(feature_values))
return (count + 1) / (len(samples) + unique_vals)
# ---------------------- 4. 计算连续特征的正态分布参数(均值、标准差) ----------------------
def calc_normal_params(samples, feature_idx):
"""计算连续特征的均值和标准差"""
values = [d[feature_idx] for d in samples]
mean = np.mean(values)
std = np.std(values, ddof=1) # 样本标准差(ddof=1)
return mean, std
# 好瓜的密度、含糖率的正态分布参数
good_density_mean, good_density_std = calc_normal_params(good_melons, 6)
good_sugar_mean, good_sugar_std = calc_normal_params(good_melons, 7)
# 坏瓜的密度、含糖率的正态分布参数
bad_density_mean, bad_density_std = calc_normal_params(bad_melons, 6)
bad_sugar_mean, bad_sugar_std = calc_normal_params(bad_melons, 7)
# ---------------------- 5. 计算测试样本的后验概率 ----------------------
# 计算“好瓜=是”的后验概率
P_good_given_test = P_good
for idx in discrete_features:
val = test_sample[idx]
P_good_given_test *= calc_discrete_prob(good_melons, idx, val)
# 乘以连续特征的概率密度
P_good_given_test *= norm.pdf(test_sample[6], good_density_mean, good_density_std)
P_good_given_test *= norm.pdf(test_sample[7], good_sugar_mean, good_sugar_std)
# 计算“好瓜=否”的后验概率
P_bad_given_test = P_bad
for idx in discrete_features:
val = test_sample[idx]
P_bad_given_test *= calc_discrete_prob(bad_melons, idx, val)
# 乘以连续特征的概率密度
P_bad_given_test *= norm.pdf(test_sample[6], bad_density_mean, bad_density_std)
P_bad_given_test *= norm.pdf(test_sample[7], bad_sugar_mean, bad_sugar_std)
# ---------------------- 6. 输出预测结果 ----------------------
print(f"测试样本属于好瓜的后验概率:{P_good_given_test:.6f}")
print(f"测试样本属于坏瓜的后验概率:{P_bad_given_test:.6f}")
prediction = "是" if P_good_given_test > P_bad_given_test else "否"
print(f"预测结果:该西瓜是好瓜吗?{prediction}")
三、关键步骤解析
1. 数据预处理
将西瓜数据集整理为结构化列表,分离 “好瓜” 和 “坏瓜” 样本,为后续概率计算做准备。
2. 先验概率计算
直接通过 “好瓜 / 坏瓜样本数 ÷ 总样本数” 得到先验概率,反映数据集里 “好瓜”“坏瓜” 的基础分布。
3. 离散特征处理
对色泽、根蒂等离散特征,采用拉普拉斯平滑:
- 解决 “某个特征值未出现在训练集中,导致条件概率为 0” 的问题;
- 公式:
(N 为特征的唯一取值数)。
4. 连续特征处理
密度、含糖率是连续值,假设其服从正态分布:
- 计算训练集中 “好瓜 / 坏瓜” 下该特征的均值、标准差;
- 用正态分布概率密度函数
计算测试样本的概率密度。
5. 后验概率计算与预测
结合先验概率和所有特征的条件概率(离散 + 连续),得到测试样本属于 “好瓜”“坏瓜” 的后验概率,取概率更大的类别作为预测结果。
四、运行结果与分析
测试样本属于好瓜的后验概率:0.037281
测试样本属于坏瓜的后验概率:0.000078
预测结果:该西瓜是好瓜吗?是
测试样本与训练集中 “好瓜” 样本完全一致,模型正确预测为 “好瓜”,结果符合预期。
五、常见问题解决
1. 缺少 scipy 模块
代码中scipy.stats.norm用于正态分布计算,若报错No module named 'scipy',执行安装命令:
pip install scipy
2. 环境不一致问题
若已安装 scipy 仍报错,大概率是 VS Code/PyCharm 使用的 Python 解释器与安装 scipy 的环境不一致:
- VS Code:Ctrl+Shift+P → 输入 “Python: Select Interpreter” → 选择安装了 scipy 的环境。
六、拓展与优化
- 批量预测:封装预测逻辑为函数,遍历多个测试样本实现批量分类;
- 特征扩展:增加更多西瓜特征(如大小、重量),提升模型泛化能力;
- 模型对比:与决策树、逻辑回归等算法对比,验证朴素贝叶斯的效果。
总结
本文通过西瓜品质分类案例,完整实现了 “离散特征 + 连续特征” 混合场景下的朴素贝叶斯分类器。核心要点是:离散特征用拉普拉斯平滑避免概率为 0,连续特征假设正态分布计算概率密度,最终通过后验概率比较完成分类。朴素贝叶斯虽简单,但在特征独立假设成立的场景下,能以极低的计算成本实现较好的分类效果,是入门机器学习分类算法的绝佳选择。

1282

被折叠的 条评论
为什么被折叠?



