贝叶斯公式/朴素贝叶斯分类器及python自实现

本文从贝叶斯与频率概率的对比入手理解贝叶斯决策的思维方式。通过两个实例理解贝叶斯的思想与流程,然后梳理了朴素贝叶斯分类器的算法流程,最后从零开始实现了朴素分类器的算法。

1.起源、提出与贝叶斯公式

贝叶斯派别主要是与古典统计学相比较区分的。

古典统计学基于大数定理,将一件事件经过大量重复实验得到的频率作为事件发生的概率,如常说的掷一枚硬币,正面朝上的概率为0.5。但,如果事先知道硬币的密度分布并不均匀,那此时的概率是不是就不为0.5呢?这种不同于“非黑即白”的思考方式,就是贝叶斯式的思考方式。

贝叶斯除了提出上述思考方式之外,还特别提出了举世闻名的贝叶斯定理。贝叶斯公式:

P ( B i ∣ A ) = P ( B i ) P ( A ∣ B i ) ∑ j = 1 n P ( B j ) P ( A ∣ B j ) P(B_i|A) = \frac{P(B_i)P(A|B_i)}{\sum_{j=1}^nP(B_j)P(A|B_j)} P(BiA)=j=1nP(Bj)P(ABj)P(Bi)P(ABi)

这里通过全概率公式,我们可以将上式等同于

P ( B ∣ A ) = P ( B ) P ( A ∣ B ) P ( A ) = P ( B ) P ( A ∣ B ) P ( A ) P(B|A) = \frac{P(B)P(A|B)}{P(A)} = P(B)\frac{P(A|B)}{P(A)} P(BA)=P(A)P(B)P(AB)=P(B)P(A)P(AB)

右边的分式中,分子的P(A)称为先验概率,是B事件未发生前,对A事件概率的判断;P(A|B)即是在B事件发生之后,对A事件发生的后验概率。这整个分式我们也称之为‘’可能性函数(Likelyhood)‘’,这是一个调整因子,对A事件未发生之前B的概率进行调整,以得到A事件发生的前提下B事件发生的后验概率P(B|A)。

以一句话与一张图概括:贝叶斯定理是一种在已知其他概率的情况下求概率的方法。

在这里插入图片描述

2.以实例感受贝叶斯决策:癌症病人计算 问题

有一家医院为了研究癌症的诊断,对一大批人作了一次普查,给每人打了试验针,然后进行统计,得到如下统计数字:

  1. 这批人中,每1000人有5个癌症病人;
  2. 这批人中,每100个正常人有1人对试验的反应为阳性
  3. 这批人中,每100个癌症病人有95入对试验的反应为阳性。

通过普查统计,该医院可开展癌症诊断。现在某人试验结果为阳性,诊断结果是什么?

在这里插入图片描述
(全是公式实在不好打,就用自己笔记代替啦)

上述例子是基于最小错误率贝叶斯决策,即哪类的概率大判断为哪类,也是我们常见的分类决策方法。但是将正常人错判为癌症病人,与将癌症病人错判为正常人的损失是不一样的。将正常人错判为癌症病人,会给他带来短期的精神负担,造成一定的损失,这个损失比较小。如果把癌症病人错判为正常人,致使患者失去挽救的机会,这个损失就大了。这两种不同的错判所造成损失的程度是有显著差别的。

所以,在决策时还要考虑到各种错判所造成的不同损失,由此提出了最小风险贝叶斯决策

我们将 I i j I_{ij} Iij记为将j类误判为i类所造成的损失。此处类别为2,如将样本x判为癌症病人 c 1 c_1 c1造成损失的数学期望为:

R 1 = I 11 P ( C 1 ∣ X ) + I 12 P ( C 2 ∣ X ) ​ R_1=I_{11}P(C_1|X)+I_{12}P(C_2|X)​ R1=I11P(C1X)+I12P(C2X)

同理,将样本x判为癌症病人 c 2 c_2 c2造成损失的数学期望为

R 2 = I 21 P ( C 1 ∣ X ) + I 22 P ( C 2 ∣ X ) R_2=I_{21}P(C_1|X)+I_{22}P(C_2|X) R2=I21P(C1X)+I22P(C2X)

选择最小风险作为决策准则,若 R 1 &lt; R 2 R_1&lt;R_2 R1<R2,则样本X ϵ R 1 \epsilon R_1 ϵR1,否则X ϵ R 2 \epsilon R_2 ϵR2

3.以实例感受贝叶斯修正先验概率:狼来了

给出一些合理解释:

事件A表示:“小孩说谎”;事件B表示:“小孩可信”。

村民起初对这个小孩的信任度为0.8,即P(B)=0.8。

我们认为可信的孩子说谎的可能性为0.1。即P(A|B)=0.1。

不可信的孩子说谎的可能性为0.5,即P(A|^B)=0.5(用 ^B表示B的对立事件)。

求小孩第一次说谎、第二次说谎后,村民对这个小孩的信任度从P(B)=0.8会发生什么变化?

在这里插入图片描述

由此我们可以得到贝叶斯决策另一个道理,**贝叶斯决策会不断用后验概率,逐渐修正先验概率。**投射到现实生活,我们也可以理解为当对某件事进行判断决策时,关于此事得到的信息越多,对此事的决策越准,而绝非yes/no的五五开决策。

还是狼来的例子,如果这个孩子要改邪归正,他需要多少次才能把信任度提高到80%?

如果要把信任度提高,那接下来就需要通过几次 不说谎 的经历来修正村民的观念,那可信度的计算,要记得将P(A|B)换成P((fei)A|B),(A上方的横线打不出来…),就是可信的孩子不说谎的可能性为1-P(A|B)=1-0.1,同样,不可信的孩子不说谎的概率为1-0.5

此时,我们就要用代码来实现而不是手算啦。代码如下:

def calculateTrustDegree(pb):
  PB_A = float((1-PA_B)*pb)/((1-PA_B)*pb+(1-PA_Bf)*(1-pb))
  return PB_A
PA_B = 0.1
PA_Bf = 0.5
pb = 0.138
N = 0
while(pb <=0.8):
    pb = calculateTrustDegree(pb)
    N+=1
print("He need {0} times with honest saying,villager can trust him again.".format(N))

# N = 6
# He need 6 times with honest saying,villager can trust him again.

4.朴素贝叶斯分类器

贝叶斯分类算法是一大类分类算法的总称。

贝叶斯分类算法以样本可能属于某类的概率来作为分类依据

朴素贝叶斯分类算法是贝叶斯分类算法中最简单的一种,采用了**“属性条件独立性假设”**:对已知类别,假设所有属性互相独立,也就是在概率的计算时,可以将一个类别的每个属性直接相乘。这在概率论中应该学过,两个事件独立时,两个事件同时发生的概率等于两个事件分别发生的乘积。

给定一个属性值,其属于某个类的概率叫做条件概率。对于一个给定的类值,将每个属性的条件概率相乘,便得到一个数据样本属于某个类的概率。

我们可以通过计算样本归属于每个类的概率,然后选择具有最高概率的类来做预测。

我们以鸢尾花分类实例来过算法流程,并不适用sklearn库自实现朴素贝叶斯分类器。

实例数据集介绍:鸢尾花数据集包含4维的特征,共三类150个样本,每类均有50个样本。

算法流程概括如下:

  1. 计算样本属于某类的先验概率,属于A类的概率为 属 于 A 类 的 样 本 数 所 有 样 本 数 \frac{属于A类的样本数}{所有样本数} A,以此类推
  2. 计算类条件概率,离散值通过类别数量比值,此数据集的属性特征为连续值所以通过 概率密度函数 来计算。首先计算在一个属性的前提下,该样本属于某类的概率;相乘合并所有属性的概率,即为某个数据样本属于某类的类条件概率
    • 计算每个特征属于每类的条件概率
      • 概率密度函数实现
      • 计算每个属性的均值和方差
      • 按类别提取属性特征,这里会得到 类别数目*属性数目 组 (均值,方差)
    • 按类别将每个属性的条件概率相乘,如下所示
      • 判断为A类的概率:p(A|特征1)*p(A|特征2)*p(A|特征3)*p(A|特征4)…
      • 判断为B类的概率:p(B|特征1)*p(B|特征2)*p(B|特征3)*p(B|特征4)…
      • 判断为C类的概率:p(C|特征1)*p(C|特征2)*p(C|特征3)*p(C|特征4)…
  3. 先验概率*类条件概率,回顾一下贝叶斯公式, P ( B i ∣ A ) = P ( B i ) P ( A ∣ B i ) ∑ j = 1 n P ( B j ) P ( A ∣ B j ) P(B_i|A) = \frac{P(B_i)P(A|B_i)}{\sum_{j=1}^nP(B_j)P(A|B_j)} P(BiA)=j=1nP(Bj)P(ABj)P(Bi)P(ABi)。由于样本确定时,贝叶斯公式的分母都是相同的。所以判断样本属于哪类只需要比较分子部分:先验概率*类条件概率,最终属于哪类的概率最大,则判别为哪类,此处为最小错误率贝叶斯分类,若采用最小风险需要加上判断为每个类别的风险损失值。

5.代码实现

1.数据集载入,划分训练集与测试集
data_df = pd.read_csv('IrisData.csv')
def splitData(data_list,ratio):
  train_size = int(len(data_list)*ratio)
  random.shuffle(data_list)
  train_set = data_list[:train_size]
  test_set = data_list[train_size:]
  return train_set,test_set

data_list = np.array(data_df).tolist()
trainset,testset = splitData(data_list,ratio = 0.7)
print('Split {0} samples into {1} train and {2} test samples '.format(len(data_df), len(trainset), len(testset)))

# Split 150 samples into 105 train and 45 test samples 
2.计算先验概率

此时需要先知道数据集中属于各类别的样本分别有多少。我们通过一个函数实现按类别划分数据。

两个返回值分别为划分好的数据字典,以及划分好的数据集中每个类别的样本数

def seprateByClass(dataset):
  seprate_dict = {}
  info_dict = {}
  for vector in dataset:
      if vector[-1] not in seprate_dict:
          seprate_dict[vector[-1]] = []
          info_dict[vector[-1]] = 0
      seprate_dict[vector[-1]].append(vector)
      info_dict[vector[-1]] +=1
  return seprate_dict,info_dict

train_separated,train_info = seprateByClass(trainset)

# train_info:
# {'Setosa': 41, 'Versicolour': 33, 'Virginica': 31}

计算属于每个类别的先验概率

def calulateClassPriorProb(dataset,dataset_info):
  dataset_prior_prob = {}
  sample_sum = len(dataset)
  for class_value, sample_nums in dataset_info.items():
      dataset_prior_prob[class_value] = sample_nums/float(sample_sum)
  return dataset_prior_prob

prior_prob = calulateClassPriorProb(trainset,train_info)

#{'Setosa': 0.3904761904761905,
# 'Versicolour': 0.3142857142857143,
# 'Virginica': 0.29523809523809524}
3.计算类条件概率

3.1 首先计算每个特征属于每类的条件概率,前面说过这里我们使用概率密度函数来计算

概率密度函数实现:

方差公式:$ var = \frac{\sum(x-avg)^{2}}{n-1} , 概 率 密 度 函 数 : ,概率密度函数: p(xi|c) = \frac{1}{\sqrt{2\pi}\sigma_{c,i}}exp(-\frac{(xi-mean_{c,i}){2}}{2\sigma_{c,i}{2}})$ , σ \sigma σ是标准差(方差开方)

# 均值
def mean(list):
  list = [float(x) for x in list] #字符串转数字
  return sum(list)/float(len(list))
# 方差
def var(list):
  list = [float(x) for x in list]
  avg = mean(list)
  var = sum([math.pow((x-avg),2) for x in list])/float(len(list)-1)
  return var
# 概率密度函数
def calculateProb(x,mean,var):
    exponent = math.exp(math.pow((x-mean),2)/(-2*var))
    p = (1/math.sqrt(2*math.pi*var))*exponent
    return p

每个属性特征属于每类的条件概率是个组合。举例来说,这里有3个类和4个数值属性,然后我们需要每一个属性(4)和类(3)的组合的类条件概率。

为了得到这12个概率密度函数,那我们需要提前知道这12个属性分别的均值和方差,才可以带入到上述概率密度函数中计算。

计算每个属性的均值和方差:

def summarizeAttribute(dataset):
    dataset = np.delete(dataset,-1,axis = 1) # delete label
    summaries = [(mean(attr),var(attr)) for attr in zip(*dataset)]
    return summaries

summary = summarizeAttribute(trainset)
#[(5.758095238095239, 0.7345732600732595),
# (3.065714285714285, 0.18592857142857133),
# (3.5533333333333323, 3.2627051282051274),
# (1.1142857142857148, 0.6014285714285714)]

按类别提取属性特征,这里会得到 类别数目*属性数目 组 (均值,方差)

def summarizeByClass(dataset):
  dataset_separated,dataset_info = seprateByClass(dataset)
  summarize_by_class = {}
  for classValue, vector in dataset_separated.items():
      summarize_by_class[classValue] = summarizeAttribute(vector)
  return summarize_by_class

train_Summary_by_class = summarizeByClass(trainset)
#{'Setosa': [(4.982926829268291, 0.12445121951219511),
#  (3.3975609756097565, 0.1417439024390244),
#  (1.4707317073170731, 0.03412195121951221),
#  (0.24390243902439032, 0.012024390243902434)],
# 'Versicolour': [(5.933333333333334, 0.2766666666666667),
#  (2.7909090909090906, 0.08960227272727272),
#  (4.254545454545454, 0.23755681818181815),
#  (1.33030303030303, 0.03905303030303031)],
# 'Virginica': [(6.596774193548387, 0.5036559139784946),
#  (2.9193548387096775, 0.10427956989247314),
#  (5.5612903225806445, 0.37711827956989247),
#  (2.0354838709677416, 0.06369892473118278)]}

按类别将每个属性的条件概率相乘。

我们前面已经将训练数据集按类别分好,这里就可以实现,输入的测试数据依据每类的每个属性(就那个类别数*属性数的字典)计算属于某类的类条件概率。

def calculateClassProb(input_data,train_Summary_by_class):
  prob = {}
  for class_value, summary in train_Summary_by_class.items():
      prob[class_value] = 1
      for i in range(len(summary)):
          mean,var = summary[i]
          x = input_data[i]
          p = calculateProb(x,mean,var)
      prob[class_value] *=p
  return prob

input_vector = testset[1]
input_data = input_vector[:-1]
train_Summary_by_class = summarizeByClass(trainset)
class_prob = calculateClassProb(input_data,train_Summary_by_class)

#{'Setosa': 3.3579279836005993,
# 'Versicolour': 1.5896628317396685e-07,
# 'Virginica': 5.176617264913899e-12}
4.先验概率*类条件概率

朴素贝叶斯分类器

def bayesianPredictOneSample(input_data):
  prior_prob = calulateClassPriorProb(trainset,train_info)
  train_Summary_by_class = summarizeByClass(trainset)
  classprob_dict = calculateClassProb(input_data,train_Summary_by_class)
  result = {}
  for class_value,class_prob in classprob_dict.items():
      p = class_prob*prior_prob[class_value]
      result[class_value] = p
  return max(result,key=result.get)

终于把分类器写完啦,接下来就让我们看看测试数据的结果!

单个样本测试:

input_vector = testset[1]
input_data = input_vector[:-1]
result = bayesianPredictOneSample(input_data)
print("the sameple is predicted to class: {0}.".format(result))

# the sameple is predicted to class: Versicolour.

看看分类准确率

def calculateAccByBeyesian(dataset):
  correct = 0
  for vector in dataset:
      input_data = vector[:-1]
      label = vector[-1]
      result = bayesianPredictOneSample(input_data)
      if result == label:
          correct+=1
  return correct/len(dataset)

acc = calculateAccByBeyesian(testset)
# 0.9333333333333333

全部代码及数据集已上传至github,也可访问我的博客,第一次写博客不足之处欢迎大家提出交流学习。最近会持续性进行输出的~而且每一次输出都是一次新的输入

6. 参考文献

机器学习之用Python从零实现贝叶斯分类器

知乎-你对贝叶斯统计都有怎样的理解?

贝叶斯公式由浅入深大讲解—AI基础算法入门

先验乘后验贝叶斯定理

  • 58
    点赞
  • 370
    收藏
    觉得还不错? 一键收藏
  • 18
    评论
朴素贝叶斯分类器是一种常用的机器学习算法,用于进行文本分类、垃圾邮件过滤等任务。在Python中,有多个包可以实现朴素贝叶斯分类器,其中最常用的包是scikit-learn(sklearn)。 scikit-learn是一个功能强大的机器学习库,提供了丰富的机器学习算法实现,包括朴素贝叶斯分类器。使用scikit-learn实现朴素贝叶斯分类器的步骤如下: 1. 导入所需的库和模块: ```python from sklearn.feature_extraction.text import CountVectorizer from sklearn.naive_bayes import MultinomialNB ``` 2. 准备数据集: ```python # 假设有两个类别的文本数据,分别为正面和负面 X_train = ['I love this movie', 'This movie is great', 'I hate this movie', 'This movie is terrible'] y_train = ['positive', 'positive', 'negative', 'negative'] ``` 3. 特征提取: ```python # 使用CountVectorizer将文本转换为特征向量 vectorizer = CountVectorizer() X_train_vec = vectorizer.fit_transform(X_train) ``` 4. 构建朴素贝叶斯分类器模型并进行训练: ```python # 创建朴素贝叶斯分类器对象 classifier = MultinomialNB() # 使用训练数据进行模型训练 classifier.fit(X_train_vec, y_train) ``` 5. 使用模型进行预测: ```python # 假设有一个新的文本需要进行分类 X_test = ['This movie is amazing'] # 将新文本转换为特征向量 X_test_vec = vectorizer.transform(X_test) # 使用训练好的模型进行预测 y_pred = classifier.predict(X_test_vec) print(y_pred) ``` 以上就是使用scikit-learn包实现朴素贝叶斯分类器的基本步骤。你可以根据自己的需求和数据进行相应的调整和扩展。
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值