前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >朴素贝叶斯算法优化与 sklearn 实现

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

作者头像
用户3147702
发布2022-06-27 13:04:51
5400
发布2022-06-27 13:04:51
举报
文章被收录于专栏:小脑斧科技博客

1. 引言

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

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

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

代码语言:javascript
复制
['love', 'my', 'dalmation'] 属于非侮辱类
['stupid', 'garbage'] 属于非侮辱类

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

2. 拉普拉斯平滑

上一篇文章中,我们利用贝叶斯分类器对文档进行分类时,需要算多个概率的乘积以获得文档属于某个类别的概率,即计算 p(w0|1) p(w1|1) p(w2|1),只要有一个概率值为0,那么最终的结果就会随之变成 0,这就是上一篇文章中,算法运行结果两个测试用例都是非侮辱类的原因。 要降低这种影响,可以讲所有词的出现数初始化为 1,并将分母初始化为 2,这个做法就是拉普拉斯平滑。 我们将上一篇日志中代码的 trainNB0 方法中的 p0Num、p1Num、p0Denom、p1Denom 赋值语句改为:

代码语言:javascript
复制
p0Num = np.ones(vocabularysNum)
p1Num = np.ones(vocabularysNum)
p0Denom = 2.0
p1Denom = 2.0

3. 下溢出问题的解决

进行拉普拉斯平滑运算后,我们运行程序,仍然得出了两个测试样本均属于非侮辱类的结果,这是为什么呢? 我们查看最终计算出的 p0 和 p1 会发现,他们的结果都是 0,这又是为什么呢? 这是因为出现了另一个问题 — 下溢出。 我们的概率运算中,所有参与运算的概率都太小了,小数相乘会使运算的积进一步减小,最终结果向下溢出超出了计算机浮点数的精度,就都会变成 0。 解决办法很自然的可以想到 — 将乘法运算转换为加法运算,但如何在保证算法正确性的前提下进行转换呢? 在代数中,ln(a * b) = ln(a) + ln(b),同时,自然对数可以保证运算趋势的正确性:

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

代码语言:javascript
复制
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

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

代码语言:javascript
复制
p0:  -7.694848072384611
p1:  -9.826714493730215
['love', 'my', 'dalmation'] 属于非侮辱类
p0:  -7.20934025660291
p1:  -4.702750514326955
['stupid', 'garbage'] 属于侮辱类

4. 朴素贝叶斯算法的优缺点

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

4.1. 优点

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

4.2. 缺点

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

5. 使用 sklearn 实现朴素贝叶斯算法

sklearn 提供了朴素贝叶斯算法的实现类 — sklearn.naive_bayes.MultinomialNB。 下面的列表中,我们将分类数称为 nc,将特征数称为 nf。

5.1. 构造参数

sklearn.naive_bayes.MultinomialNB 类构造参数

参数名

类型

可选参数

默认值

说明

alpha

float

非负浮点数

1

拉普拉斯平滑系数

fit_prior

boolean

True/False

True

是否使用先验分类概率

class_prior

array

None 或array(nc*1)

None

如果指定 fit_prior 为 True,该参数用来提供先验概率

5.2. 类属性

sklearn.naive_bayes.MultinomialNB 类属性

属性名

类型

说明

classlog_prior

array(nc*1)

每个分类的平滑对数先验概率

intercept_

array(nc*1)

将多项式朴素贝叶斯理解为线性模型时,与 classlog_prior 相同

featurelog_prob

array(nc*nf)

每个分类的每个特征的对数先验概率(P(x_i|y))

coef_

array(nc*nf)

将多项式朴素贝叶斯理解为线性模型时,与 featurelog_prob 相同

classcount

array(nc*1)

在拟合过程中每个分类的样本数

featurecount

array(nc*nf)

在拟合过程中每个分类的每个特征的样本数

5.3. 类方法

  • 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]) — 返回模型的平均精度

5.4. 示例

代码语言:javascript
复制
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。

6. 后记

对于相互独立的样本来说,朴素贝叶斯是一个非常不错的分类器,在自然语言处理和文本特征分析、过滤等领域有着广泛的应用。 事实上,朴素贝叶斯共有三种模型,他们的区别在于计算条件概率的公式不同: 1. 高斯朴素贝叶斯 — 用于符合高斯分布(正态分布)的连续样本数据的分类 2. 多项式朴素贝叶斯 — 我们已经介绍的内容就是多项式朴素贝叶斯模型 3. 伯努利朴素贝叶斯 — 每个特征的取值为0或1,即计算特征是否存在的概率,他是唯一将样本中不存在的特征也引入计算概率的朴素贝叶斯模型

7. 参考资料

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。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-11-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小脑斧科技博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 引言
  • 2. 拉普拉斯平滑
  • 3. 下溢出问题的解决
  • 4. 朴素贝叶斯算法的优缺点
    • 4.1. 优点
      • 4.2. 缺点
      • 5. 使用 sklearn 实现朴素贝叶斯算法
        • 5.1. 构造参数
          • 5.2. 类属性
            • 5.3. 类方法
              • 5.4. 示例
              • 6. 后记
              • 7. 参考资料
              相关产品与服务
              NLP 服务
              NLP 服务(Natural Language Process,NLP)深度整合了腾讯内部的 NLP 技术,提供多项智能文本处理和文本生成能力,包括词法分析、相似词召回、词相似度、句子相似度、文本润色、句子纠错、文本补全、句子生成等。满足各行业的文本智能需求。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档