【机器学习基础】朴素贝叶斯的算法实现

共 2888字,需浏览 6分钟

 ·

2021-02-06 16:37

前言

本次我们将梳理下朴素贝叶斯(Naive Bayes)的相关内容。
本文约1.6k字,预计阅读10分钟。

概要

朴素贝叶斯算法是一种适用于二分类和多分类分类问题的「分类算法」。在贝叶斯概率框架下,通过相应推导得知,「期望风险最小化等价于后验概率最大化」。对于后验概率的计算,可以通过「联合概率分布建模」,得到后验概率「生成模型」);

对于生成模型来说,根据「贝叶斯定理」,可以将其写成:

在朴素贝叶斯中,由于条件概率难以计算,因此提出一个强烈的假设:「特征独立性假设」,即各个特征是独立同分布的,不存在交互,这也是朴素贝叶斯名称的来由。先验概率和条件概率的估计不作展开,感兴趣的可以参考《统计学习方法》,通过转化,最终最大化后验概率(MAP),即「先验概率与类条件概率的乘积」

【注】期望风险最小化等价于后验概率最大化的推导可以参考《机器学习》,详细的推导,可以参考《统计学习方法》。

算法面试

在算法面试中,设计朴素贝叶斯相关的问题包括:

  1. 为什么朴素贝叶斯如此“朴素”?
  2. 朴素贝叶斯基本原理和预测过程;
  3. 简单说说贝叶斯定理;
  4. 使用朴素贝叶斯如何进行垃圾分类?

今天我们讨论的问题是:

朴素贝叶斯的算法实现。

对于朴素贝叶斯来说,这既对我们的算法原理进行考察,也检验了编程能力。我以建立整个朴素贝叶斯算法模型类来展开,主要分为:

  1. 确定朴素贝叶斯的类型(高斯朴素贝叶斯或者伯努利朴素贝叶斯等);
  2. 模型的拟合,重点在于模型到底保存了什么内容;
  3. 后验概率的计算;
  4. 最大后验概率的输出;

1. 模型类型

对于类条件概率参数的估计,我们采用极大似然估计法,首先最重要的是「假设随便变量(特征)服从什么分布」,对于不同的假设,也对应着不同的朴素贝叶斯,例如伯努利朴素贝叶斯、高斯朴素贝叶斯、多项分布朴素贝叶斯。最常见的是通过假设「高斯分布」,在模型拟合中,「只需要估计训练数据(每个类别的所有样本的每个特征)的平均值和标准偏差」

因此,我们可以得到初步的框架:

class Gaussian_NaiveBayes
  def init(self):

     
   def mean(self, X):  # 计算均值
     return sum(X) / float(len(X))
    
   def stdev(self, X):  # 计算标准差
     avg = self.mean(X)
   return math.sqrt(sum([pow(x-avg, 2for x in X]) / float(len(X)-1))

2. 模型拟合

通过对朴素贝叶斯原理的理解,我们知道,学习联合概率模型,需要通过极大似然法估计先验概率(假设服从伯努利分布)和类条件概率参数,对于高斯朴素贝叶斯来说,整个训练数据集,我们需要保存:

  1. 每个类对应的数量(可以计算先验概率)和总样本数量(可以作为模型的一个属性);
  2. 每个类的每个特征的均值与标准偏差(可以计算类条件概率);

综上所述,我们可以通过「字典」的形式进行保存:

因此:

 def summarize(self, train_data):
   summaries = [(self.mean(column), self.stdev(column), len(column)) for column in zip(*train_data)]
  return summaries

 def fit(self, X, y):
   self.total_rows = len(X)
   labels = list(set(y))
   data = {label: [] for label in labels}
   for f, label in zip(X, y):
     data[label].append(f)
   self.model = {label: self.summarize(value) for label, value in data.items()}

3. 后验概率的计算

对于每个类别的后验概率计算,需要求得先验概率和类条件概率。首先对于类条件概率,根据高斯公式求得,

 def gaussian_probabality(self, x, mean, stdev):
   exponent = math.exp(-math.pow(x - mean, 2) / (2 * math.pow(stdev, 2)))
   return (1 / (math.sqrt(2 * math.pi) * stdev)) * exponent

然后迭代计算所有类的后验概率:

 def calculate_probabalities(self, input_data):
      probalalities = {}
      for label, summaries in self.model.items():
        probalalities[label] = summaries[0][2] / self.total_rows # 先验
        for i in range(len(summaries)):
          mean, stdev, _ = summaries[i]
          probalalities[label] *= self.gaussian_probabality(  # 先验 * 条件概率
            input_data[i], mean, stdev)
      return probalalities

4. 最大后验概率

最大后验概率的计算就是排序,得到最大概率对应的标签值。对于测试集来说,遍历所有样本进行计算。

 def predict(self, X_test):
  labels = []
  for i in X_test:
   label = sorted(
    self.calculate_probabalities(i).items(),
    key=lambda x: x[-1])[-1][0]
   labels.append(label)
  return np.array(labels)

总结

以上便是对“朴素贝叶斯算法实现”的总结。最重要的是需要一个完善的类框架,才能够好的结合原理进行代码的复现。至于其他几个问题,比较简单,这里就不作详细讨论。

往期精彩回顾





本站知识星球“黄博的机器学习圈子”(92416895)

本站qq群704220115。

加入微信群请扫码:

浏览 59
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报