一、贝叶斯分类基础
贝叶斯分类是一类基于贝叶斯定理的监督学习算法,在统计学分类方法中占有重要地位。贝叶斯方法的核心是通过概率统计方法进行分类决策。

1. 贝叶斯定理


贝叶斯定理描述了在已知某些条件下某事件发生的概率。公式表示为:

其中:

P(A|B) 是后验概率:在B发生条件下A发生的概率

P(B|A) 是似然概率:在A发生条件下B发生的概率

P(A) 是先验概率:A发生的概率

P(B) 是边际概率:B发生的概率

2. 朴素贝叶斯分类器
朴素贝叶斯分类器基于一个"朴素"的假设:所有特征之间相互独立。尽管这个假设在现实中很少成立,但朴素贝叶斯在实践中表现往往出人意料地好。

原理:

特征条件假设:假设每个特征之间没有联系,给定训练数据集,其中每个样本都包括维特征,即,类标记集合含有种类别,即。

对于给定的新样本,判断其属于哪个标记的类别,根据贝叶斯定理,可以得到属于类别的概率:

后验概率最大的类别记为预测类别,即。

朴素贝叶斯算法对条件概率分布作出了独立性的假设,通俗地讲就是说假设各个维度的特征x1,x2,⋯,xn互相独立,在这个假设的前提上,条件概率可以转化为:

代入贝叶斯公式中,得到:

于是,朴素贝叶斯分类器可表示为:

因为对所有的,上式中的分母的值都是一样的,所以可以忽略分母部分,朴素贝叶斯分类器最终表示为: 

算法简单,易于实现

对小规模数据表现良好

对缺失数据不太敏感

在多分类问题中表现良好

主要缺点:

特征独立性假设可能导致精度下降

对输入数据的表达形式敏感

二、朴素贝叶斯分类器的实现步骤
题目:
“西瓜数据集”是机器学习入门中一个非常经典的分类数据集,常用于讲解和实现基本的分类算法。该数据集通过若干个特征来描述西瓜的外观和内部质量,并据此判断是否为“好瓜”。

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)


AI写代码

7.结果:

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐