1.朴素贝叶斯算法
1.1基本概念
其分类原理是利用贝叶斯公式根据某特征的先验概率计算出其后验概率,然后选择具有最大后验概率作为该特征所属的类。之所以称之为“朴素”,是因为贝叶斯分类只做最原始、最简单的假设:所有的特征之间是相对独立的。
其核心思想是通过考虑各个特征的概率来预测分类(即对于给出的待分类样本,计算该样本在每个类别下出现的概率,最大的就被认为是该分类样本所属于的类别
朴素贝叶斯被广泛地应用在文本分类、垃圾邮件过滤、情感分析等场合
1.2朴素贝叶斯的优缺点
优点:
具有稳定的分类效率
在数据较少时仍然有效,可以处理多类别的问题
对缺失数据不太敏感
进行分类时对时间和空间的开销都比较小
缺点:
对于输入数据的准备方式比较敏感,需要对数据进行适当的预处理
需要假设属性之间相互独立,这在实际情况中往往不太现实
需要知道先验概率,但是由于先验概率大多取决于假设,故很容易因此导致预测效果不佳
1.3 朴素贝叶斯的一般过程
准备数据:收集并预处理数据,将数据分为特征和标签
特征选择:选择对分类有帮助的特征
模型训练:使用训练数据计算每个类别的先验概率和条件概率
预测:对新数据进行分类,选择概率最大的类别作为预测结果
2.算法实现
先验概率
P(cj)代表还没有训练模型之前,根据历史数据/经验估算cj拥有的初始概率。P(cj)常被称为cj的先验概率(prior probability) ,它反映了cj的概率分布,该分布独立于样本。通常可以用样例中属于cj的样例数|cj|比上总样例数|D|来近似,即:
后验概率
给定数据样本x时cj成立的概率P(cj | x )被称为后验概率(posterior probability),因为它反映了在看到数据样本 x后 cj 成立的置信度。
注:大部分机器学习模型尝试得到后验概率
贝叶斯定理
朴素贝叶斯算法的核心是贝叶斯公式,公式如下:
条件独立性假设
最终选择使后验概率最大的类别:
计算步骤
接下来我们通过一个例子来说明计算步骤
判断是否能够PlayTennis
我们需要通过已有数据计算出,如果给出一个天气情况,这个人去不去打网球。
如
现在假设有一个样例 x
x = {Sunny, Hot, High,Weak}
它应该属于类别Yes?No?
1.统计个计算先验概率
统计在各个条件下出去Yes和No的天数
统计各个特征出现的次数
2.计算条件概率
计算在Yes和No的条件下各个特征出现的概率
离散特征:
连续特征(假设服从高斯分布):
对于多个特征:
3.计算在已知该特征下Yes和No发生的条件概率
现在 假设有一个样例 x
x = {Sunny, Hot, High,Weak}
Y es的概率 P(Yes | x )
∝ p(Yes)*p(Sunny|Yes)* p(Hot|Yes)* p(High|Yes)* p(Weak|Yes)
=9/14*2/9*2/9*3/9*6/9
=0.007039
No的概率 P(No | x )
∝ p(No)*p(Sunny| No)* p(Hot| No)* p(High| No)* p(Weak| No)
=5/14*3/5*2/5*4/5*2/5
=0.027418
max (P(Yes| x ), P(No| x ) ) = P(No| x ) 所以可以把 x 分类为No
拉普拉斯修正
避免零概率事件,适用于离散特征
若某个属性值在训练集中没有与某个类同时出现过,则训练后的模型会出现 over-fitting 现象。比如“敲声=清脆”测试例,训练集中没有该样例,因此连乘式计算的概率值为0,无论其他属性上明显像好瓜,分类结果都是“好瓜=否”,这显然不合理。
为了避免其他属性携带的信息,被训练集中未出现的属性值“抹去”,在估计概率值时通常要进行“拉普拉斯修正”:
令 N 表示训练集 D 中可能的类别数,𝑁𝑖N_i表示第i个属性可能的取值数,则贝叶斯公式可修正为:
3.实例:判断西瓜好坏
训练集:
待测样本
代码实现:
import pandas as pd
def preprocess_data():
# 创建包含训练数据的DataFrame
train_data = pd.DataFrame({
'色泽': ['青绿', '乌黑', '乌黑', '青绿', '浅白', '青绿', '乌黑', '乌黑', '乌黑', '青绿', '浅白', '浅白', '青绿', '浅白', '乌黑', '浅白', '青绿'],
'根蒂': ['蜷缩', '蜷缩', '蜷缩', '蜷缩', '蜷缩', '稍蜷', '稍蜷', '稍蜷', '稍蜷', '硬挺', '硬挺', '蜷缩', '稍蜷', '稍蜷', '稍蜷', '蜷缩', '蜷缩'],
'敲声': ['浊响', '沉闷', '浊响', '沉闷', '浊响', '浊响', '浊响', '浊响', '沉闷', '清脆', '清脆', '浊响', '浊响', '沉闷', '浊响', '浊响', '沉闷'],
'纹理': ['清晰', '清晰', '清晰', '清晰', '清晰', '清晰', '稍糊', '清晰', '稍糊', '清晰', '模糊', '模糊', '稍糊', '稍糊', '清晰', '模糊', '稍糊'],
'脐部': ['凹陷', '凹陷', '凹陷', '凹陷', '凹陷', '稍凹', '稍凹', '稍凹', '稍凹', '平坦', '平坦', '平坦', '凹陷', '凹陷', '稍凹', '平坦', '稍凹'],
'触感': ['硬滑', '硬滑', '硬滑', '硬滑', '硬滑', '软粘', '软粘', '硬滑', '硬滑', '软粘', '硬滑', '软粘', '硬滑', '硬滑', '软粘', '硬滑', '硬滑'],
'密度': [0.697, 0.774, 0.634, 0.608, 0.556, 0.403, 0.481, 0.437, 0.666, 0.243, 0.245, 0.343, 0.639, 0.657, 0.360, 0.593, 0.719],
'含糖率': [0.460, 0.376, 0.318, 0.215, 0.237, 0.149, 0.211, 0.091, 0.265, 0.057, 0.099, 0.161, 0.198, 0.370, 0.042, 0.103, 0.103],
'好瓜': ['是', '是', '是', '是', '是', '是', '是', '是', '否', '否', '否', '否', '否', '否', '否', '否', '否']
})
# 创建包含测试数据的DataFrame,即待预测的西瓜样本的各项特征
test_data = pd.DataFrame({
'色泽': ['青绿'],
'根蒂': ['蜷缩'],
'敲声': ['浊响'],
'纹理': ['清晰'],
'脐部': ['凹陷'],
'触感': ['硬滑'],
'密度': [0.697],
'含糖率': [0.460]
})
return train_data, test_data
def calculate_prior_probabilities(train_data):
"""
计算先验概率
"""
# 统计每个类别的样本数量,例如统计“是”和“否”的数量
class_counts = train_data['好瓜'].value_counts()
# 获取训练数据的总样本数量
total_samples = len(train_data)
# 用于存储每个类别的先验概率的字典
prior_probabilities = {}
# 遍历每个类别及其对应的样本数量
for class_label, count in class_counts.items():
# 计算每个类别的先验概率,即该类别的样本数量除以总样本数量
prior_probabilities[class_label] = count / total_samples
return prior_probabilities
def calculate_conditional_probabilities(train_data):
"""
计算条件概率
"""
# 用于存储每个类别下每个特征的条件概率的字典
conditional_probabilities = {}
# 获取训练数据中除了“好瓜”标签列之外的所有特征列
features = train_data.columns.drop('好瓜')
# 获取训练数据中所有的类别标签,即“是”和“否”
class_labels = train_data['好瓜'].unique()
# 遍历每个类别标签
for class_label in class_labels:
# 获取属于当前类别的数据子集
class_data = train_data[train_data['好瓜'] == class_label]
# 在条件概率字典中为当前类别创建一个空字典,用于存储该类别下每个特征的条件概率
conditional_probabilities[class_label] = {}
# 遍历每个特征
for feature in features:
# 获取当前特征在当前类别数据子集中的所有唯一值
feature_values = class_data[feature].unique()
# 在当前类别下的条件概率字典中为当前特征创建一个空字典,用于存储该特征取不同值时的条件概率
conditional_probabilities[class_label][feature] = {}
# 遍历当前特征的每个唯一值
for value in feature_values:
# 统计当前特征取当前值时在当前类别数据子集中的样本数量
count = len(class_data[class_data[feature] == value])
# 计算当前特征取当前值时在当前类别下的条件概率,即该数量除以当前类别数据子集的样本数量
conditional_probabilities[class_label][feature][value] = count / len(class_data)
return conditional_probabilities
def predict_sample(sample, prior_probabilities, conditional_probabilities):
"""
预测单个样本的类别
"""
# 初始化最大概率为 -1,用于比较不同类别的后验概率
max_prob = -1
# 初始化预测的类别为 None
predicted_class = None
# 遍历每个类别标签
for class_label in prior_probabilities:
# 初始化当前类别的后验概率为该类别的先验概率
prob = prior_probabilities[class_label]
# 遍历样本的每个特征和对应的值
for feature, value in sample.items():
# 如果当前特征在当前类别的条件概率字典中,并且当前特征值也在该特征的条件概率字典中
if feature in conditional_probabilities[class_label] and value in conditional_probabilities[class_label][feature]:
# 将当前特征值在当前类别下的条件概率乘到当前类别的后验概率上
prob *= conditional_probabilities[class_label][feature][value]
# 如果当前类别的后验概率大于最大概率
if prob > max_prob:
# 更新最大概率为当前类别的后验概率
max_prob = prob
# 更新预测的类别为当前类别
predicted_class = class_label
return predicted_class
if __name__ == "__main__":
# 调用数据预处理函数,获取训练数据和测试数据
train_data, test_data = preprocess_data()
# 计算先验概率
prior_probabilities = calculate_prior_probabilities(train_data)
# 计算条件概率
conditional_probabilities = calculate_conditional_probabilities(train_data)
# 预测测试样本
for index, sample in test_data.iterrows():
# 对每个测试样本进行预测
prediction = predict_sample(sample, prior_probabilities, conditional_probabilities)
print(f"预测结果: {prediction}")
运行结果: