机器学习——贝叶斯分类
一、核心基石:贝叶斯定理的本质解读
贝叶斯分类的所有逻辑都源于贝叶斯定理,这一定理的核心价值在于“利用新信息更新对原有事件的概率判断”,而非凭空预测。在理解定理前,我们先明确三个关键概率概念(用生活化案例辅助理解):
先验概率 P(A):无任何额外信息时,事件A发生的概率。比如“随机抽1个成年人,患有糖尿病”的概率,仅基于整体人群的患病比例,不涉及任何体检数据。
似然概率 P(B|A):已知事件A发生的前提下,事件B发生的概率。比如“已知某人患有糖尿病(A),其空腹血糖超标(B)”的概率。
后验概率 P(A|B):已知事件B发生的前提下,事件A发生的概率。这是我们最终想求的概率,比如“已知某人空腹血糖超标(B),其患有糖尿病(A)”的概率——本质是用“血糖超标”这一新信息,更新了“患糖尿病”的初始概率判断。
贝叶斯定理的数学表达式如下,清晰揭示了三者的关联:

公式中P(B)为边际概率(事件B发生的总概率),其作用是对结果进行归一化处理(确保后验概率在0~1之间)。由于在分类任务中,P(B)对所有类别而言是固定值,因此比较不同类别的后验概率时,可直接忽略P(B),仅对比分子部分即可。
二、核心模型:朴素贝叶斯分类器深度剖析
贝叶斯分类的落地核心是“朴素贝叶斯分类器”(Naive Bayes Classifier)。这里的“朴素”(Naive)并非贬义,而是指一个简化计算的关键假设——给定类别标签的前提下,所有特征之间相互独立。正是这个假设,让贝叶斯定理从理论走向实用。
2.1 为什么需要“条件独立性假设”?
在实际分类任务中,样本往往包含多个特征(比如用“年龄、血压、血糖”三个特征判断是否患病)。若不做任何假设,计算联合概率P(特征1,特征2,...,特征n|类别)时,会面临“维度灾难”——随着特征数量增加,计算量呈指数级增长,甚至无法实现。
而条件独立性假设认为:在已知类别,所有特征之间没有关联。比如“已知某人患病”,则“年龄”和“血压”这两个特征互不影响。这个假设虽在现实中难以完全成立,但能将复杂的联合概率拆解为单个特征概率的乘积,极大简化计算,且在多数场景下仍能保证较好的分类精度。
2.2 核心分类逻辑(公式简化版)
分类任务的目标的是:给定样本的特征向量X=(x₁,x₂,...,xₙ),判断它属于哪个类别C(类别集合为{C₁,C₂,...,Cₖ})。结合贝叶斯定理和条件独立性假设,可推导得出朴素贝叶斯的核心分类逻辑:

三、朴素贝叶斯分类器的实现步骤
题目:
“西瓜数据集”是机器学习入门中一个非常经典的分类数据集,常用于讲解和实现基本的分类算法。该数据集通过若干个特征来描述西瓜的外观和内部质量,并据此判断是否为“好瓜”。
1. 数据准备
在代码中,我们首先通过load_data函数加载数据,将数据分为特征和标签两部分。对于西瓜数据集,前6个特征是离散值(色泽、根蒂等),后2个是连续值(密度、含糖率)。
2. 计算先验概率
先验概率P(Y)表示在没有其他信息的情况下,某个类别出现的概率。在代码中通过calc_prior函数实现:
def calc_prior(labels):
total = len(labels)
priors = defaultdict(float)
for label in labels:
priors[label] += 1
for k in priors:
priors[k] /= total
print("先验概率:", dict(priors))
return priors
3. 计算条件概率
对于离散特征,计算P(Xi|Y),即给定类别下某个特征取特定值的概率。在代码中通过
calc_discrete_probs实现:
def calc_discrete_probs(separated, idx):
probs = {}
for label, items in separated.items():
freq = defaultdict(int)
for item in items:
freq[item[idx]] += 1
total = len(items)
probs[label] = {k: (v / total) for k, v in freq.items()}
return probs
对于连续特征,通常假设其服从高斯分布,计算均值和方差。在代码中通过
calc_continuous_probs实现:
def calc_continuous_probs(separated, idx):
stats = {}
for label, items in separated.items():
values = [item[idx] for item in items]
mean = sum(values) / len(values)
var = sum((x - mean) ** 2 for x in values) / len(values)
stats[label] = (mean, var)
return stats
4. 高斯概率密度函数
对于连续变量,使用高斯概率密度函数计算概率:
def gaussian_prob(x, mean, var):
if var == 0: var = 1e-6 # 防止除零
exponent = math.exp(-((x - mean) ** 2) / (2 * var))
return (1 / math.sqrt(2 * math.pi * var)) * exponent
5. 分类预测
使用贝叶斯定理计算后验概率,并取最大概率对应的类别作为预测结果:
results = {}
for label in priors:
prob = math.log(priors[label]) # 使用对数防止下溢
# 乘以各个属性的条件概率
for idx in discrete_idxs:
val = test_sample[idx]
prob_dict = discrete_probs[idx].get(label, {})
p = prob_dict.get(val, 1e-6) # 使用小概率值防止零概率
prob += math.log(p)
for idx in continuous_idxs:
val = test_sample[idx]
mean, var = continuous_probs[idx][label]
p = gaussian_prob(val, mean, var)
prob += math.log(p)
results[label] = prob
6.完整代码
import math
from collections import defaultdict
def load_data(file_path):
train_data = []
labels = []
with open(file_path, 'r', encoding='utf-8') as f:
for line in f:
parts = line.strip().split()
features = parts[:6] + [float(x) for x in parts[6:8]]
label = parts[8]
train_data.append(features)
labels.append(label)
return train_data, labels
def calc_prior(labels):
total = len(labels)
priors = defaultdict(float)
for label in labels:
priors[label] += 1
for k in priors:
priors[k] /= total
print("先验概率:", dict(priors))
return priors
def separate_by_class(data, labels):
separated = defaultdict(list)
for i in range(len(data)):
separated[labels[i]].append(data[i])
return separated
def calc_discrete_probs(separated, idx):
probs = {}
for label, items in separated.items():
freq = defaultdict(int)
for item in items:
freq[item[idx]] += 1
total = len(items)
probs[label] = {k: (v / total) for k, v in freq.items()}
return probs
def calc_continuous_probs(separated, idx):
stats = {}
for label, items in separated.items():
values = [item[idx] for item in items]
mean = sum(values) / len(values)
var = sum((x - mean) ** 2 for x in values) / len(values)
stats[label] = (mean, var)
return stats
def gaussian_prob(x, mean, var):
if var == 0: var = 1e-6
exponent = math.exp(-((x - mean) ** 2) / (2 * var))
return (1 / math.sqrt(2 * math.pi * var)) * exponent
def classify_naive_bayes(train_data, labels, test_sample):
priors = calc_prior(labels)
separated = separate_by_class(train_data, labels)
discrete_idxs = list(range(6))
continuous_idxs = [6, 7]
discrete_probs = {}
for idx in discrete_idxs:
discrete_probs[idx] = calc_discrete_probs(separated, idx)
continuous_probs = {}
for idx in continuous_idxs:
continuous_probs[idx] = calc_continuous_probs(separated, idx)
results = {}
for label in priors:
prob = math.log(priors[label])
for idx in discrete_idxs:
val = test_sample[idx]
prob_dict = discrete_probs[idx].get(label, {})
p = prob_dict.get(val, 1e-6)
prob += math.log(p)
for idx in continuous_idxs:
val = test_sample[idx]
mean, var = continuous_probs[idx][label]
p = gaussian_prob(val, mean, var)
prob += math.log(p)
results[label] = prob
print("对数后验概率:", results)
pred_label = max(results, key=results.get)
print(f"\n预测结果: '{pred_label}' 的概率最大,该瓜为{'好瓜' if pred_label == '是' else '坏瓜'}")
if __name__ == "__main__":
file_path = "E:/pythonProject/train.txt"
train_data, labels = load_data(file_path)
test_sample = ['青绿', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', 0.697, 0.460]
classify_naive_bayes(train_data, labels, test_sample)
7.结果:
四、全面评估:优缺点与典型适用场景
任何算法都有其适用边界,朴素贝叶斯也不例外。清晰了解其优缺点,才能在实际场景中合理选型:
4.1 核心优点
-
计算效率极高:无需迭代训练,仅通过概率统计即可完成分类,适合大规模数据(如千万级文本分类);
-
模型易于实现:核心逻辑仅涉及基础概率计算,代码量少,入门门槛低;
-
对小规模数据友好:即使样本量较小,也能通过概率估计获得较好的分类效果,无需担心过拟合;
-
抗噪声能力强:对缺失数据和异常值的敏感度较低,少量噪声不会显著影响分类结果。
4.2 主要缺点
-
条件独立性假设局限:现实中特征往往存在关联(如“身高”和“体重”相关),会导致分类精度下降;
-
对特征分布敏感:若特征实际分布与模型假设的分布(如高斯分布)偏差较大,分类效果会明显变差;
-
难以处理高维复杂特征:当特征维度极高(如百万级特征)且存在多重共线性时,模型性能会大幅下滑。
4.3 典型适用场景
结合优缺点,朴素贝叶斯的“优势场景”非常明确:
-
文本分类领域:垃圾邮件识别、新闻分类、情感分析、垃圾评论过滤(这是朴素贝叶斯最经典、最擅长的场景);
-
医疗辅助诊断:根据生理指标(如体温、血压、血常规数据)判断疾病类型;
-
轻量级推荐系统:结合用户的基础特征(如年龄、性别)和行为特征(如点击、收藏),推荐简单品类;
-
小规模数据集分类:当数据量少、特征维度不高,且需要快速得到分类结果时,朴素贝叶斯是最优选择之一。
更多推荐

所有评论(0)