1 介绍
分类预测中,以概率论作为基础的算法较少,而朴素贝叶斯便是其中一种,它实现简单、预测分类的效率还很高,因此使用频率也很高。
由于它基于概率论,因此这里首先来了解一下概率论的基础。
如果事件A发送的概率表示为,事件B发生的概率为,那么事件A、B同时发生的概率我们就可以表示为。
在该基础之上,如果我想要求当事件B发生之后A发生的概率为多少,就被称为条件概率,其计算公式为:。
有了B事件发生之后A事件发生的概率公式,那么如果求呢?也就是在A事件发生之后的B事件发生的概率?
这便是贝叶斯定理,其推导过程如下:
结合上面两个式子得到完整的贝叶斯定理:
其中、 这种已知的概率,称为先验概率,它是通过过往经验和分析得到的概率,比如抛硬币我们会认为其概率就是0.5。
当事件发生之后求的反向概率,则被称为后验概率,比如上式中的 就是通过和先验概率求出来的反向条件概率。
而朴素贝叶斯的原理就是将贝叶斯原理与条件独立结合而成的算法:
P(类别 \mid 特征) = \frac{P(特征 \mid 类别)\times P(类别)}{P(特征)}相当于就是将A换为了特征、B换为了类别。
那么我们现在就可以通过这个公式求出当特征概率已知的情况下,它类别概率是什么,这不正好满足我们分类的需求嘛!
其中右边类别的概率、特征的概率、特征在类别下的概率都是已知的。
朴素贝叶斯中的朴素,含义就是假设预测的各个属性之间相互独立、每个属性独立的对分类结果产生影响。
比如判断一朵花的类型有叶长、花瓣数量两个特征,那么前面我们预测的方式都是结合这两者一起来预测花朵的类型,而在朴素贝叶斯中,就认为这两个特征是独立的影响类别判断、而不会将其结合在一起判断。
这样做使得其变得简单而简洁,但自然的,在某些情况下就会牺牲一定的分类准确度。
2 朴素贝叶斯算法实现
有了上面的基础,下面我们就可以来介绍朴素贝叶斯算法的python代码实现逻辑了。
流程如下:
- 假设X=\left\{x_1,x_2, \dots ,x_n\right\}为训练数据,而是训练数据的特征值。
- 假设Y=\left\{y_1,y_2,\dots,y_m \right\}为类别集合
- 计算每种类别在特征值条件下的概率:P(y_1\mid x),P(y_2\mid x),\dots,P(y_m\mid x)
- 寻找P(y_1\mid x),P(y_2\mid x),\dots,P(y_m\mid x)中最大的概率,则x就属于类别
而根据前面朴素贝叶斯算法原理,这里比较所有的大小,实际上比较的是P(类别_i \mid 特征),由于根据上面的转换公式,它等于:\frac{P(特征 \mid 类别)\times P(类别)}{P(特征)}
所有的P(特征)分母值都相同,所以相当于就是比较分子P(特征 \mid 类别)\times P(类别)的乘积大小。
那么如何计算得到这两个值呢?这就需要用到极大似然估计法与贝叶斯估计法了。
为了更加直观的看到效果,这里先给出一组测试数据:
def gen_data():
# 生成示例数据
data = {
"x": ["g", "g", "r", "r", "b", "g", "g", "r", "b", "b", "g","r", "g", "r", "b"],
"y": ["m", "s", "l", "s", "m", "s", "m", "s", "m", "l", "l", "s", "m", "m", "l"],
"labels": ["A", "A", "A", "A","A", "A", "A", "A", "B", "B", "B", "B", "B", "B", "B"],
}
return data
这里的测试数据中,x、y为两个特征值,也就是。labels则是一个类别,也就是。
2.1 极大似然估计
所谓极大似然估计,就是根据过往经验,推断出最有可能造成某个结果的参数值。
比如A箱子里面有999个白球、一个黑球,B箱子里面有999个黑球、一个白球。
那么当我说我抽出了要给白球时,你自然而然的就会认为我是从A箱子里面抽出来的,因为A箱子里面抽出白球的概率实在是太大了。
回到这里,我们求P(类别)时,可以认为它的概率就是它在训练数据中所有类别的占比,比如这里训练数据中的labels为其中一个类别,由于数据总共有15条,其中A占8条,B占7条,那么就能求出各自的概率分别为,。
此时对应的python代码为:
def get_P_labels(labels):
labels = list(labels) # 转换为 list 类型
P_label = {} # 设置空字典用于存入 label 的概率
for label in labels:
# 统计 label 标签在标签集中出现的次数再除以总长度
P_label[label] = labels.count(label) / float(
len(labels)