一、引言
在机器学习的分类算法中,朴素贝叶斯(Naive Bayes)是一种基于贝叶斯定理与特征条件独立假设的分类方法。由于其在某些特定场景下(如文本分类)的高效性和准确性,朴素贝叶斯算法被广泛应用于各种实际场景中。本文将详细介绍朴素贝叶斯算法的原理、特点以及如何使用Python进行实战。。
二、朴素贝叶斯算法原理
1.贝叶斯定理
贝叶斯定理是关于条件概率的定理,它描述了如何根据新的证据更新某个假设的概率。在分类问题中,我们可以将某个类别视为假设,将特征视为证据,通过贝叶斯定理计算给定特征下某个类别的概率。
贝叶斯定理的公式如下:
其中:
- ( P(C|F) ) 是后验概率,即在给定特征 ( F ) 下类别 ( C ) 的概率。
- ( P(F|C) ) 是条件概率,即在类别 ( C ) 下特征 ( F ) 的概率。
- ( P(C) ) 是先验概率,即类别 ( C ) 的概率。
- ( P(F) ) 是特征 ( F ) 的概率,通常作为归一化因子。
2. 朴素贝叶斯分类器
朴素贝叶斯分类器的一个重要假设是特征条件独立,即给定类别下,各个特征之间是独立的。这个假设大大简化了计算过程,使得我们可以分别计算每个特征的条件概率,然后相乘得到后验概率。
3.计算过程
(1) 计算先验概率
先验概率 P(C) :对于每个类别 ,我们统计训练集中该类别的样本数 ,然后除以总样本数 N ,得到该类别的先验概率 。
公式:
(2)计算条件概率
条件概率 P(F|C) :对于每个类别 和每个特征 ,我们统计在该类别下该特征出现的次数 ,然后除以该类别的样本数 ,得到该特征在该类别下的条件概率 。
公式:
注意:当某个特征在特定类别中没有出现时,条件概率 会为0,这会导致整个后验概率为0。为了避免这个问题,通常使用拉普拉斯平滑(Laplace smoothing)等技术。
(3)计算后验概率
后验概率 P(C|F):根据贝叶斯定理和特征条件独立假设,我们可以计算给定特征下每个类别的后验概率。由于多个概率的乘积可能导致数值下溢,通常使用对数概率来避免这个问题。
公式(对数形式):
注意:由于 ( P(F) 对于所有类别都是相同的,所以在比较不同类别的后验概率时可以忽略它。
(4)预测类别
选择具有最高后验概率(或对数概率和)的类别作为预测结果。
公式(选择最高概率的类别):
或者,使用对数概率和的形式:
(5)拉普拉斯平滑
当某个特征在某个类别中没有出现时,其条件概率为0,这会导致整个后验概率为0,从而影响分类的准确性。为了解决这个问题,我们可以使用拉普拉斯平滑(Laplace smoothing)来估计条件概率。
拉普拉斯平滑的基本思想是在每个计数上加上一个小的常数(通常是1),以避免出现概率为0的情况。这样,条件概率可以重新计算为:
其中 是特征 在类别 C 中出现的次数,N(C) 是类别 C 中的样本数, N 是所有可能的特征数, 是平滑参数,通常取1。
三、代码实现
1.数据准备
以下将根据天气的条件为特征,来判断是否出门来作代码实例
训练集实例:
No. | 天气 | 气温 | 湿度 | 风 | 类别 |
1 | 晴 | 热 | 高 | 无 | N |
2 | 晴 | 热 | 高 | 有 | N |
3 | 多云 | 热 | 高 | 无 | Y |
4 | 雨 | 适中 | 高 | 无 | Y |
5 | 雨 | 冷 | 正常 | 无 | Y |
6 | 雨 | 冷 | 正常 | 有 | N |
7 | 多云 | 冷 | 正常 | 有 | Y |
8 | 晴 | 适中 | 高 | 无 | N |
9 | 晴 | 冷 | 正常 | 无 | Y |
10 | 雨 | 适中 | 正常 | 无 | Y |
11 | 晴 | 适中 | 正常 | 有 | Y |
12 | 多云 | 适中 | 高 | 有 | Y |
13 | 多云 | 热 | 正常 | 无 | Y |
14 | 雨 | 适中 | 高 | 有 | N |
如果给一个新样本:X = (天气 = 晴, 气温 = 冷, 湿度 = 高, 风 = 有),想知道是否外出?
代码:
def loadDataSet():
# 训练集
dataSet = [['晴', '热', '高', '无', 'N'],
['晴', '热', '高', '有', 'N'],
['多云', '热', '高', '无', 'Y'],
['雨', '适中', '高', '无', 'Y'],
['雨', '冷', '正常', '无', 'Y'],
['雨', '冷', '正常', '有', 'N'],
['多云', '冷', '正常', '有', 'Y'],
['晴', '适中', '高', '无', 'N'],
['晴', '冷', '正常', '无', 'Y'],
['雨', '适中', '正常', '无', 'Y'],
['晴', '适中', '正常', '有', 'Y'],
['多云', '适中', '高', '有', 'Y'],
['多云', '热', '正常', '无', 'Y'],
['雨', '适中', '高', '有', 'N']]
# 测试集
testSet = ['晴', '冷', '高', '有']
return dataSet, testSet
2.计算先验概率
分析:该数据集中出门结果为Y的数量是9,N的数量为5,因此先验概率计算的结果为:
P(Y) = 9/14 P(N) = 5/14
代码:
def calculate_prior_probabilities(data_set, class_labels):
label_counts = {}
for feature_set in data_set:
label = feature_set[-1]
if label not in label_counts:
label_counts[label] = 0
label_counts[label] += 1
prior_probabilities = {label: count / len(data_set) for label, count in label_counts.items()}
return prior_probabilities
结果:
先验概率:
N: 0.35714285714285715
Y: 0.6428571428571429
3.计算条件概率
分析:在出门结果为Y的条件下,多云为4,雨为3,晴为2。因此部分条件概率为
P(天气: 多云|Y) = 4/9 P(天气: 雨|Y) = 3/9 P(天气: 晴|Y) = 2/9
代码:
def calculate_conditional_probabilities(data_set, feature_index, class_labels):
feature_list = [example[feature_index] for example in data_set]
unique_features = set(feature_list)
conditional_probabilities = {}
for feature in unique_features:
conditional_probabilities[feature] = {}
for label in class_labels:
subset_data = [example for example in data_set if
example[-1] == label and example[feature_index] == feature]
prob = (len(subset_data) + 1) / (class_labels[label] + len(unique_features)) # 拉普拉斯平滑
conditional_probabilities[feature][label] = prob
return conditional_probabilities
结果:
特征1的条件概率:
P(雨|Y) = 0.3333333333333333
P(雨|N) = 0.375
P(晴|Y) = 0.25
P(晴|N) = 0.5
P(多云|Y) = 0.4166666666666667
P(多云|N) = 0.125
特征2的条件概率:
P(热|Y) = 0.25
P(热|N) = 0.375
P(冷|Y) = 0.3333333333333333
P(冷|N) = 0.25
P(适中|Y) = 0.4166666666666667
P(适中|N) = 0.375
特征3的条件概率:
P(正常|Y) = 0.6363636363636364
P(正常|N) = 0.2857142857142857
P(高|Y) = 0.36363636363636365
P(高|N) = 0.7142857142857143
特征4的条件概率:
P(无|Y) = 0.6363636363636364
P(无|N) = 0.42857142857142855
P(有|Y) = 0.36363636363636365
P(有|N) = 0.5714285714285714
4.计算后验概率
分析:对于对(天气 = 晴, 气温 = 冷, 湿度 = 高, 风 = 有)
P(Y|晴,冷,高,有)=P(Y|晴)*P(Y|冷)*P(Y|高)*P(Y|有)
P(N|晴,冷,高,有)=P(N|晴)*P(N|冷)*P(N|高)*P(N|有)
代码:
posteriors = {}
predicted_label = None
for label in class_labels:
posteriors[label] = math.log(priors[label])
for i in range(len(test_set)):
feature = test_set[i]
posteriors[label] += math.log(conditional_probs[i][feature][label])
结果:
类别Y的后验概率(对数得分): -4.949941225423999
类别N的后验概率(对数得分): -4.005148983417629
5.朴素贝叶斯分类器
代码:
# 朴素贝叶斯分类器
def naive_bayes_classifier(data_set, test_set):
class_labels = {label: 0 for label in set([example[-1] for example in data_set])}
for feature_set in data_set:
class_labels[feature_set[-1]] += 1
# 打印先验概率
priors = calculate_prior_probabilities(data_set, class_labels)
print("先验概率:")
for label, prob in priors.items():
print(f"{label}: {prob}")
# 计算并打印条件概率
conditional_probs = {}
for i in range(len(data_set[0]) - 1):
conditional_probs[i] = calculate_conditional_probabilities(data_set, i, class_labels)
print(f"特征{i+1}的条件概率:")
for feature, prob_dict in conditional_probs[i].items():
for label, prob in prob_dict.items():
print(f" P({feature}|{label}) = {prob}")
# 计算并打印后验概率(对数得分)
posteriors = {}
predicted_label = None
for label in class_labels:
posteriors[label] = math.log(priors[label])
for i in range(len(test_set)):
feature = test_set[i]
posteriors[label] += math.log(conditional_probs[i][feature][label])
print(f"类别{label}的后验概率(对数得分): {posteriors[label]}")
# 选择得分最高的类别
predicted_label = max(posteriors, key=posteriors.get)
print("预测类别:", predicted_label)
return predicted_label
# 加载数据集
data_set, test_set = loadDataSet()
# 使用朴素贝叶斯分类器进行预测并打印结果
naive_bayes_classifier(data_set, test_set)
结果:
预测类别: N
可以判断出在(天气 = 晴, 气温 = 冷, 湿度 = 高, 风 = 有)的条件下,结果为不出门
完整代码:
import numpy as np
import math
import pandas as pd
# 加载数据集
def loadDataSet():
# 训练集
dataSet = [['晴', '热', '高', '无', 'N'],
['晴', '热', '高', '有', 'N'],
['多云', '热', '高', '无', 'Y'],
['雨', '适中', '高', '无', 'Y'],
['雨', '冷', '正常', '无', 'Y'],
['雨', '冷', '正常', '有', 'N'],
['多云', '冷', '正常', '有', 'Y'],
['晴', '适中', '高', '无', 'N'],
['晴', '冷', '正常', '无', 'Y'],
['雨', '适中', '正常', '无', 'Y'],
['晴', '适中', '正常', '有', 'Y'],
['多云', '适中', '高', '有', 'Y'],
['多云', '热', '正常', '无', 'Y'],
['雨', '适中', '高', '有', 'N']]
# 测试集
testSet = ['晴', '冷', '高', '有']
return dataSet, testSet
# 计算先验概率
def calculate_prior_probabilities(data_set, class_labels):
label_counts = {}
for feature_set in data_set:
label = feature_set[-1]
if label not in label_counts:
label_counts[label] = 0
label_counts[label] += 1
prior_probabilities = {label: count / len(data_set) for label, count in label_counts.items()}
return prior_probabilities
# 计算条件概率
def calculate_conditional_probabilities(data_set, feature_index, class_labels):
feature_list = [example[feature_index] for example in data_set]
unique_features = set(feature_list)
conditional_probabilities = {}
for feature in unique_features:
conditional_probabilities[feature] = {}
for label in class_labels:
subset_data = [example for example in data_set if
example[-1] == label and example[feature_index] == feature]
prob = (len(subset_data) + 1) / (class_labels[label] + len(unique_features)) # 拉普拉斯平滑
conditional_probabilities[feature][label] = prob
return conditional_probabilities
# 朴素贝叶斯分类器
def naive_bayes_classifier(data_set, test_set):
class_labels = {label: 0 for label in set([example[-1] for example in data_set])}
for feature_set in data_set:
class_labels[feature_set[-1]] += 1
# 打印先验概率
priors = calculate_prior_probabilities(data_set, class_labels)
print("先验概率:")
for label, prob in priors.items():
print(f"{label}: {prob}")
# 计算并打印条件概率
conditional_probs = {}
for i in range(len(data_set[0]) - 1):
conditional_probs[i] = calculate_conditional_probabilities(data_set, i, class_labels)
print(f"特征{i+1}的条件概率:")
for feature, prob_dict in conditional_probs[i].items():
for label, prob in prob_dict.items():
print(f" P({feature}|{label}) = {prob}")
# 计算并打印后验概率(对数得分)
posteriors = {}
predicted_label = None
for label in class_labels:
posteriors[label] = math.log(priors[label])
for i in range(len(test_set)):
feature = test_set[i]
posteriors[label] += math.log(conditional_probs[i][feature][label])
print(f"类别{label}的后验概率(对数得分): {posteriors[label]}")
# 选择得分最高的类别
predicted_label = max(posteriors, key=posteriors.get)
print("预测类别:", predicted_label)
return predicted_label
# 加载数据集
data_set, test_set = loadDataSet()
# 使用朴素贝叶斯分类器进行预测并打印结果
naive_bayes_classifier(data_set, test_set)
四、分析与总结
朴素贝叶斯分类器具有以下优点:
- 实现简单,计算效率高。
- 在一些问题上表现出色,如文本分类和垃圾邮件过滤。
- 对缺失数据和异常数据不敏感。
然而,它也存在一些局限性:
- 特征独立性假设在现实中往往不成立,这会影响分类的准确性。
- 在处理不平衡数据集时可能表现不佳。
- 需要对特征进行适当的选择和预处理,以提高分类性能
结论
朴素贝叶斯分类器是一种简单而有效的分类方法,在文本分类等领域有着广泛的应用。通过利用贝叶斯定理和特征条件独立假设,我们可以快速计算给定特征集下每个类别的后验概率,并选择后验概率最大的类别作为预测结果。然而,我们也需要注意到朴素贝叶斯分类器的局限性,并在实际应用中结合具体问题和数据集的特点来选择合适的特征和参数,以提高分类性能。