在介绍朴素贝叶斯算法原理之前,先为大家介绍几个公式和几个概念:
首先介绍两个概念:
- 先验概率:自我理解就是在没有任何证据的情况下,凭借先前经验来进行某件事件的概率的推算
- 后验概率:已知某些证据的情况下,推断出的事件发生的概率
举个简单的例子来帮助大家理解,也是经常被人们用的一个例子:
假设在街上遇到一个陌生人,然后我们去猜测这个人姓什么?我们会想到,中国姓氏百分比中,王姓所占比例为7.1%左右,排名第一,因此我们会猜测这个人姓王;但是如果我们已知这个人来自于家村,那我们便会猜测这个人姓于。前者是先验的,后者是后验的。
后验概率也是一种条件概率。
接下来介绍一个公式:
贝叶斯公式:
事件独立性:
或
事件A和事件B独立
事件条件独立:
事件A和事件B关于事件C条件独立
接下来引入正题,来介绍朴素贝叶斯法
朴素贝叶斯是基于贝叶斯定理以及特征条件独立假设的的一种分类方法。也正因为基于特征条件独立假设,所以命名为朴素贝叶斯。
假设存在数据集是由
独立同分布产生的N个样本点,
为样本的特征向量,
为样本标记。朴素贝叶斯的任务就是通过给定的样本来学习X与Y的联合概率分布
,然后在通过后验概率
最大化来判断样本所属的类别。
我们知道,,因此想要学得
实际上就是对先验概率
以及特征的条件概率
的学习。
我们假设为样本第j维特征
的取值个数,j=1, 2, 3, ..., n;
为样本的标记空间中的元素,k=1, 2, 3, ..., K,表示样本的第k种类别;
因为在此之前我们假设了特征条件独立,因此满足下式:
所以,后验概率可以表示为:
不难注意到,对于给定的样本特征,对于不同的类别
,上式的分母总是相同的,因此当我们得到一个样本特征向量
时,衡量其每个类别的后验概率时可以忽略分母,只计算分子即可,所以:
这样我们就得出了样本的类别y。
最后我们如何求得以及
呢?其实我们可以运用古典概型的思路去考虑:所有样本空间或事件空间的容量为N,即样本容量,几
发生的频数为下式:
为指示函数,满足:
因此我们可以得出:
那么同理我们可以得出的表达式,首先设样本第j维特征
可能取值集合为
,则:
这样,当给定某个样本特征时,我们就能求得其属于各个类的后验概率,从中我们取出后验概率最大的那个类别,便是这个样本的预测类别。
以上便是朴素贝叶斯的基本思想,以下为本人使用Python编写的朴素贝叶斯的相关程序:
import numpy as np
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
np.set_printoptions(linewidth=1000, suppress=True)
class NB(object):
def __init__(self, n, lamda):
"""
:param lamda: 用于做拉普拉斯平滑的,防止概率为0出现,常取值为1
:param n: 将每一维连续特征离散成多少个值,每一维特征的取值范围为0, 1, ..., n-1
"""
self.lamda = lamda
self.n = n
self.x = load_iris()["data"]
self.y = load_iris()["target"]
# 对所有样本特征进行离散化
self.disperse_attr(n)
# 分为训练样本和测试样本
self.x_train, self.x_test, self.y_train, self.y_test = train_test_split(self.x, self.y, test_size=0.3, random_state=1)
self.classes = np.unique(self.y)
def disperse_attr(self, n):
"""
将连续特征离散化
:param n: 每一维特征离散后的取值个数,每一维特征的取值范围为0, 1, ..., n-1
:return:
"""
# attr_min表示每一维特征的最小值,attr_max表示每一维特征的最大值
attr_min = np.min(self.x, axis=0)
attr_max = np.max(self.x, axis=0)
for dim, range_ in enumerate(zip(attr_min, attr_max)):
min_, max_ = range_
section = np.linspace(min_ - 0.00001 * min_, max_, n + 1, endpoint=True)
for i in range(n):
self.x[:, dim][np.logical_and(section[i] < self.x[:, dim], self.x[:, dim] <= section[i + 1])] = i
def prior_probability(self):
# self.prior_prob用于存放每个类的先验概率,self.prior_prob={类别1:类别1的先验概率, 类别2:类别2的先验概率, ...}
# self.condition_prob用于存放条件概率, self.condition_prob = {类别1:prob_array_1, 类别2:prob_array_2, ...} ,
# prob_array_k.shape = (特征个数,每个特征取值数)代表已知为类别k的条件下每一维特征的概率
self.prior_prob = {}
self.condition_prob = {}
for clss in self.classes:
self.prior_prob[str(clss)] = list(self.y_train).count(clss) / len(self.y_train)
self.condition_prob[str(clss)] = []
for attr_dim in range(self.x_train.shape[1]):
# 第一层循环遍历第attr_dim维特征
prob = [None] * self.n
for attr in np.arange(self.n):
# 第二层循环遍历第attr_dim维特征的每一种取值情况(0至self.n-1),实际上每一维特征的取值情况都是0至self.n - 1
prob[attr] = (np.sum((np.logical_and(self.x_train[:, attr_dim] == attr, self.y_train == clss)).astype(np.int8)) + self.lamda) / (list(self.y_train).count(clss) + self.n * self.lamda)
self.condition_prob[str(clss)].append(prob)
self.condition_prob[str(clss)] = np.array(self.condition_prob[str(clss)])
for clss in self.condition_prob:
print("================类别%s的各维特征的条件概率==================" % clss)
for value in range(self.x_train.shape[1]):
print("已知类别为%s条件下,第%s维特征取值(%s~%s)的条件概率:\n" % (clss, value + 1, 0, self.n - 1), self.condition_prob[clss][value])
def test(self):
self.y_test_pred = []
for test_sample in self.x_test:
# posterior_prob用于存放当前样本在每个类的后验概率,键为类别,值为类对应的后验概率
posterior_prob = {}
for clss in self.classes:
# prob_clss为当前类的先验概率
prob_clss = list(self.y_train).count(clss) / len(self.y_train)
prod = 1
for i in range(len(test_sample)):
prod *= self.condition_prob[str(clss)][i][int(test_sample[i])]
posterior_prob[clss] = prob_clss * prod
reverse_ = [(value, key) for key, value in posterior_prob.items()]
self.y_test_pred.append(max(reverse_)[1])
self.y_test_pred = np.array(self.y_test_pred)
print("\n################鸢尾花数据测试集预测结果#####################")
print("测试样本预测标记:", self.y_test_pred)
print("测试样本真实标记:", self.y_test)
print("测试样本个数:", len(self.y_test))
print("预测错误样本个数:", len(self.y_test_pred) - list(self.y_test_pred - self.y_test).count(0))
print("预测准确率:%.2f%s" % (accuracy_score(self.y_test, self.y_test_pred) * 100, "%"))
def main():
nb = NB(3, 1)
nb.prior_probability()
nb.test()
if __name__ == "__main__":
main()