朴素贝叶斯算法优化与 sklearn 实现

2018-11-11 00:16:14   最后更新: 2018-11-11 00:18:15   访问数量:192




上一篇日志中,我们主要介绍了贝叶斯算法,并提供了 python 实践:

朴素贝叶斯算法的推导与实践

 

但运行上一篇日志中的示例,我们发现出现了下面的结果:

['love', 'my', 'dalmation'] 属于非侮辱类 ['stupid', 'garbage'] 属于非侮辱类

 

这显然是不正确的,本文,我们就来解决这个问题,同时对算法进行优化并使用 sklearn 来实现算法的实践

 

上一篇文章中,我们利用贝叶斯分类器对文档进行分类时,需要算多个概率的乘积以获得文档属于某个类别的概率,即计算 p(w0|1) * p(w1|1) * p(w2|1),只要有一个概率值为0,那么最终的结果就会随之变成 0,这就是上一篇文章中,算法运行结果两个测试用例都是非侮辱类的原因

要降低这种影响,可以讲所有词的出现数初始化为 1,并将分母初始化为 2,这个做法就是拉普拉斯平滑

我们将上一篇日志中代码的 trainNB0 方法中的 p0Num、p1Num、p0Denom、p1Denom 赋值语句改为:

p0Num = np.ones(vocabularysNum) p1Num = np.ones(vocabularysNum) p0Denom = 2.0 p1Denom = 2.0

 

 

进行拉普拉斯平滑运算后,我们运行程序,仍然得出了两个测试样本均属于非侮辱类的结果,这是为什么呢?

我们查看最终计算出的 p0 和 p1 会发现,他们的结果都是 0,这又是为什么呢?

这是因为出现了另一个问题 -- 下溢出

我们的概率运算中,所有参与运算的概率都太小了,小数相乘会使运算的积进一步减小,最终结果向下溢出超出了计算机浮点数的精度,就都会变成 0

解决办法很自然的可以想到 -- 将乘法运算转换为加法运算,但如何在保证算法正确性的前提下进行转换呢?

在代数中,ln(a * b) = ln(a) + ln(b),同时,自然对数可以保证运算趋势的正确性:

 

因此我们通过对数运算优化训练函数 trainNB0 与测试函数 classifyNB:

def trainNB0(trainMap, results): """ 朴素贝叶斯分类器训练函数 :param trainMap: 训练文档矩阵 :param results: 训练类别标签向量 :return: p0Vect - 侮辱类的条件概率数组 p1Vect - 非侮辱类的条件概率数组 pAbusive - 文档属于侮辱类的概率 """ dataListNum = len(trainMap) vocabularysNum = len(trainMap[0]) """ 计算文档属于侮辱词概率 """ pAbusive = sum(results) / float(dataListNum) p0Num = np.ones(vocabularysNum) p1Num = np.ones(vocabularysNum) p0Denom = 2.0 p1Denom = 2.0 """ 将所有行按是否是侮辱类分别叠加,统计各个词出现的次数 """ for i in range(dataListNum): if results[i] == 1: p1Num += trainMap[i] p1Denom += sum(trainMap[i]) else: p0Num += trainMap[i] p0Denom += sum(trainMap[i]) """ 计算概率 """ p1Vect = np.log(p1Num / p1Denom) p0Vect = np.log(p0Num / p0Denom) return p0Vect, p1Vect, pAbusive def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): """ 朴素贝叶斯分类器分类函数 :param vec2Classify: 待分类的词条数组 :param p0Vec: 侮辱类的条件概率数组 :param p1Vec: 非侮辱类的条件概率数组 :param pClass1: 文档属于侮辱类的概率 :return: 是否属于侮辱类,0. 不属于,1. 属于 """ p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1) print("p0: ", p0) p1 = sum(vec2Classify * p1Vec) + np.log(pClass1) print("p1: ", p1) if p1 > p0: return 1 else: return 0

 

 

最终我们得到了正确的结果:

p0: -7.694848072384611 p1: -9.826714493730215 ['love', 'my', 'dalmation'] 属于非侮辱类 p0: -7.20934025660291 p1: -4.702750514326955 ['stupid', 'garbage'] 属于侮辱类

 

 

通过上一篇日志的介绍和本文的优化,我们了解了朴素贝叶斯算法的原理和应用,他是一种基于概率的分类器算法,可以用来处理不相干因子的多分类问题,例如根据词频进行文本分类等问题

那么他又具有哪些优缺点呢?

 

优点

  1. 算法原理和实现简单,通过概率分类
  2. 对小规模数据表现很好,适合多分类增量式训练任务

 

缺点

  1. 对输入数据的表达形式很敏感
  2. 需要计算先验概率,分类决策存在错误率
  3. 要求样本之间相互独立,这就是“朴素”的意思,这个限制有时很难做到,或使用者误以为符合而造成错误的结果

 

sklearn 提供了朴素贝叶斯算法的实现类 -- sklearn.naive_bayes.MultinomialNB

下面的列表中,我们将分类数称为 nc,将特征数称为 nf

 

构造参数

sklearn.naive_bayes.MultinomialNB 类构造参数
参数名类型可选参数默认值说明
alphafloat非负浮点数1拉普拉斯平滑系数
fit_priorbooleanTrue/FalseTrue是否使用先验分类概率
class_priorarrayNone 或array(nc*1)None如果指定 fit_prior 为 True,该参数用来提供先验概率

 

类属性

sklearn.naive_bayes.MultinomialNB 类属性
属性名类型说明
class_log_prior_array(nc*1)每个分类的平滑对数先验概率
intercept_array(nc*1)将多项式朴素贝叶斯理解为线性模型时,与 class_log_prior_ 相同
feature_log_prob_array(nc*nf)每个分类的每个特征的对数先验概率(P(x_i|y))
coef_array(nc*nf)将多项式朴素贝叶斯理解为线性模型时,与 feature_log_prob_ 相同
class_count_array(nc*1)在拟合过程中每个分类的样本数
feature_count_array(nc*nf)在拟合过程中每个分类的每个特征的样本数

 

类方法

  • fit(X, y[, sample_weight]) -- 训练朴素贝叶斯模型
  • get_params([deep]) -- 获取参数
  • set_params(**params) -- 设置参数
  • partial_fit(X, y[, classes, sample_weight]) -- 部分样本上的增量拟合
  • predict(X) -- 预测
  • predict_log_proba(X) -- 返回测试向量X的对数概率估计
  • predict_proba(X) -- 返回测试向量X的概率估计
  • score(X, y[, sample_weight]) -- 返回模型的平均精度

 

示例

import numpy as np from sklearn.naive_bayes import MultinomialNB if __name__ == '__main__': X = np.random.randint(5, size=(6, 100)) y = np.array([1, 2, 3, 4, 5, 6]) clf = MultinomialNB() clf.fit(X, y) print(clf.predict(X[2:3]))

 

 

上面的示例,我们通过随机数创建了一个 6*100 的矩阵,其中每个元素都是0到5的随机数,我们用这个矩阵的每一行分别对应 1、2、3、4、5、6,最终,我们用第三行来测试这个模型,果然得到了预期的数字:3

 

对于相互独立的样本来说,朴素贝叶斯是一个非常不错的分类器,在自然语言处理和文本特征分析、过滤等领域有着广泛的应用

事实上,朴素贝叶斯共有三种模型,他们的区别在于计算条件概率的公式不同:

  1. 高斯朴素贝叶斯 -- 用于符合高斯分布(正态分布)的连续样本数据的分类
  2. 多项式朴素贝叶斯 -- 我们已经介绍的内容就是多项式朴素贝叶斯模型
  3. 伯努利朴素贝叶斯 -- 每个特征的取值为0或1,即计算特征是否存在的概率,他是唯一将样本中不存在的特征也引入计算概率的朴素贝叶斯模型

 

Peter Harrington 《机器学习实战》

李航 《统计学习方法》

https://zh.wikipedia.org/wiki/%E6%9C%B4%E7%B4%A0%E8%B4%9D%E5%8F%B6%E6%96%AF%E5%88%86%E7%B1%BB%E5%99%A8

https://scikit-learn.org/dev/modules/generated/sklearn.naive_bayes.MultinomialNB.html

 






读书笔记      机器学习      sklearn      贝叶斯      bayes      朴素贝叶斯      navie bayes      拉普拉斯     


京ICP备15018585号