朴素贝叶斯分类器_sklearn朴素贝叶斯分类器

朴素贝叶斯分类器_sklearn朴素贝叶斯分类器所谓分类,就是根据事物的特征(Feature)对其归类(Class)特征的数据特点有两种可能:1.离散/标签2.连续/浮点数(大样本/小样本)下面我们分别来看一、离散/标签这是一个病人

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

 

所谓分类,就是根据事物的特征(Feature)对其归类(Class)

 

 特征的数据特点有两种可能:

  1. 离散/标签

  2. 连续/浮点数(大样本/小样本)

 

下面我们分别来看

一、离散/标签

这是一个病人分类的例子

某个医院早上收了六个门诊病人,如下表。

  症状  职业   疾病

  打喷嚏 护士   感冒
  打喷嚏 农夫   过敏
  头痛  建筑工人 脑震荡
  头痛  建筑工人 感冒
  打喷嚏 教师   感冒
  头痛  教师   脑震荡

现在又来了第七个病人,是一个打喷嚏建筑工人。请问他患上感冒的概率有多大?

 

根据贝叶斯定理

 P(A|B) = P(B|A) P(A) / P(B)

可得

   P(感冒|打喷嚏x建筑工人)
    = P(打喷嚏x建筑工人|感冒) x P(感冒)
    / P(打喷嚏x建筑工人)

假定”打喷嚏”和”建筑工人”这两个特征是独立的,因此,上面的等式就变成了

   P(感冒|打喷嚏x建筑工人)
    = P(打喷嚏|感冒) x P(建筑工人|感冒) x P(感冒)
    / P(打喷嚏) x P(建筑工人)

 

如何计算: P(打喷嚏|感冒)?

特征1:病人症状统计表

  打喷嚏 头痛 合计
感冒  2  1 3
过敏  1  0 1
脑震荡  0  2 2
合计 3 3 6

 

 

          P(打喷嚏|感冒) = 2/3 = 0.67

 

 

 

如何计算: P(建筑工人|感冒)?

特征2:病人职业统计表

  护士 农夫 建筑工人 教师 合计
感冒  1  0 1 1 3
过敏  0  1 0 0 1
脑震荡  0  0 1 1 2
合计 1 1 2 2 6

 

 

        P(建筑工人|感冒) = 1/3 = 0.33

 

 

 

因此,这个打喷嚏的建筑工人,得了感冒的概率不难计算得:

   P(感冒|打喷嚏x建筑工人)
    = P(打喷嚏|感冒) x P(建筑工人|感冒) x P(感冒)
    / P(打喷嚏) x P(建筑工人)

           = 0.67 x 0.33 x 0.5 / 0.5 x 0.33
    = 0.67

同理,可以计算这个病人患上过敏或脑震荡的概率。比较这几个概率,就可以知道他最可能得什么病。

这就是贝叶斯分类器的基本方法:在统计资料的基础上,依据某些特征,计算各个类别的概率,从而实现分类。

 

注意,

1. 为了简化计算,朴素贝叶斯算法做了一假设:“朴素的认为各个特征相互独立”。

2. 其次,如果只是得到最大值对应的那个作为其分类,那么,我们可以省略分母计算,从而进一步简化计算过程

3. 贝叶斯公式推导能够成立有个重要前期,就是各个证据(evidence)不能为0。也即对于任意特征Fx,P(Fx)不能为0。而显示某些特征未出现在测试集中的情况是可以发生的。因此实现上通常要做一些小的处理,例如把所有计数进行+1加法平滑(additive smoothing,又叫拉普拉斯平滑(Laplace smothing))。而如果通过增加一个大于0的可调参数alpha进行平滑,就叫Lidstone平滑

例如,在所有6个分为C=1的影评样本中,某个特征F1=1不存在,则P(F1=1|C=1)  = 0/6,P(F1=0|C=1)  = 6/6。

经过加法平滑后,P(F1=1|C=1)  = (0+1)/(6+2)=1/8,P(F1=0|C=1)  = (6+1)/(6+2)=7/8。

注意分母的+2,这种特殊处理使得2个互斥事件的概率和恒为1。

4. 当特征很多的时候,大量小数值的小数乘法会有溢出风险。因此,通常的实现都是将其转换为log

log[P(C)*P(F1|C)*P(F2|C)…P(Fn|C)] = log[P(C)]+log[P(F1|C)] + … +log[P(Fn|C)]

将乘法转换为加法,就彻底避免了乘法溢出风险。

 

二、连续/浮点数(大样本,分区间

  第二个是账号分类的例子

  这个问题是这样的,对于SNS社区来说,不真实账号(使用虚假身份或用户的小号)是一个普遍存在的问题,作为SNS社区的运营商,希望可以检测出这些不真实账号,从而在一些运营分析报告中避免这些账号的干扰,亦可以加强对SNS社区的了解与监管。

      如果通过纯人工检测,需要耗费大量的人力,效率也十分低下,如能引入自动检测机制,必将大大提升工作效率。这个问题说白了,就是要将社区中所有账号在真实账号和不真实账号两个类别上进行分类。

  运营商决定考察账号的三个特征:日志数量/注册天数、好友数量/注册天数、是否使用真实头像。

 

  运维人员曾经人工检测过的1万个账号,得到这三个特征的先验统计概率

 

特征1(F1):日志数量/注册天数 统计表

  [0, 0.05)
[0.05, 0.2)  [0.2, +∞) 合计
真实  0.12  0.30  0.28 0.60
虚假  0.25  0.10  0.05 0.40
合计  0.37  0.40  0.33 1

 

 

   P(F1_2|真实) = 0.30/0.60 = 0.5

 

 

特征2(F2):好友数量/注册天数 统计表

  [0, 0.1 ) [0.1, 0.5) [0.5, +∞) 合计
真实  0.10  0.20 0.30 0.60
虚假  0.15  0.15 0.10 0.40
合计  0.25  0.35 0.40 1

 

 

     P(F2_2|真实) = 0.20/0.60 = 0.33

 

 

特征3(F3):是否使用真实头像(1/0) 统计表

  1 0 合计
真实  0.48 0.12 0.60
虚假  0.01 0.39 0.40
合计 0.49 0.51 1

 

 

    P(F3_2|真实) = 0.12/0.60 = 0.20

 

 

因此,下面这个账号(0.1, 0.2, 0)为真实账号的概率,分析如下:

  日志数量/注册天数:0.1 ~ F1_2

  好友数量/注册天数:0.2 ~ F2_2

  是否使用真实头像:0     ~ F3_2

   P(真实|F1_2 x F2_2 x F3_2)
    = P(F1_2|真实) x P(F1_2|真实)x P(F3_2|真实) x P(真实)
    / P(F1_2) x P(F2_2) x P(F3_2)

           = 0.5 x 0.33 x 0.2 x 0.6 / 0.4 x 0.35 x 0.51
    = 0.28

同理,可以计算

  P(虚假|F1_2 x F2_2 x F3_2) = 0.51

 

三、连续/浮点数(小样本,设正态

这是一个性别分类的例子

 

下面是一组人类身体特征的统计资料。

  性别  身高(英尺) 体重(磅)  脚掌(英寸)

  男    6       180     12
  男    5.92     190     11
  男    5.58     170     12
  男    5.92     165     10
  女    5       100     6
  女    5.5      150     8
  女    5.42     130     7
  女    5.75     150     9

已知某人身高6英尺、体重130磅,脚掌8英寸,请问该人是男是女?

 

根据朴素贝叶斯分类器,计算下面这个式子的值。

P(身高|性别) x P(体重|性别) x P(脚掌|性别) x P(性别)

这里的困难在于,由于身高、体重、脚掌都是连续变量,不能采用离散变量的方法计算概率。而且由于样本太少,所以也无法分成区间计算。怎么办?

 

这时,

首先假设男性和女性的身高、体重、脚掌都是正态分布

然后,通过样本计算出均值和方差;

接着,由正态分布的密度函数,计算出各自条件概率密度。

 

比如,男性的身高是均值5.855、方差0.035的正态分布。

所以,男性的身高为6英尺的条件概率密度为:

朴素贝叶斯分类器_sklearn朴素贝叶斯分类器

 

 

有了这些数据以后,就可以计算(身高,体重,脚掌)=(6、130、8)的性别分类了。

  P(身高=6|男) x P(体重=130|男) x P(脚掌=8|男) x P(男)
    = 6.1984 x e-9

  P(身高=6|女) x P(体重=130|女) x P(脚掌=8|女) x P(女)
    = 5.3778 x e-4

可以看到,女性的概率比男性要高出将近10000倍,所以判断该人为女性。

 

 

 四、python实现的代码

 

import numpy as np


def loadDataSet():
    docs = [['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
            ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
            ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
            ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
            ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
            ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
    classes = [0,1,0,1,0,1]    # 1为辱骂, 0为否
    return docs, classes

#创建一个带有所有单词的列表
def create_all_words(docs):
    all_words = set([])
    for doc in docs:
        all_words = all_words | set(doc)
    return list(all_words)

# 将句子根据其中的单词转成向量
def create_all_words_counter(all_words, doc):
    all_words_counter = [0] * len(all_words)
    for word in doc:
        if word in all_words:
            all_words_counter[all_words.index(word)] = 1   # 置1,伯努利模型
            #all_words_counter[all_words.index(word)] += 1 # 加1,多项式模型
        else:
            print('word ',word ,'not in dict')
    return all_words_counter


# 计算P(i)和P(w[i]|C[1])和P(w[i]|C[0]),这里有两个技巧
# 一个是开始的分子分母没有全部初始化为0是为了防止其中一个的概率为0导致整体为0,
# 另一个是后面乘用对数防止因为精度问题结果为0
def trainNB0(all_words_counters, classes):
    num_docs = len(all_words_counters)
    num_all_words = len(all_words_counters[0])
    pAbusive = np.sum(classes) / num_docs # 辱骂的先验概率

    pNum = np.ones((2, num_all_words))  # 置1,没有置0,这是一个技巧
    pDenom = np.array([2.0, 2.0])

    for i in range(num_docs):
        pNum[classes[i]] += all_words_counters[i]
        pDenom[classes[i]] += np.sum(all_words_counters[i])

    p0Vec = np.log(pNum[0]) - np.log(pDenom[0])  # 处于精度的考虑,这里使用对数减法!!
    p1Vec = np.log(pNum[1]) - np.log(pDenom[1])
    return p0Vec, p1Vec, pAbusive
   
def classifyNB(all_words_counter, p0Vec, p1Vec, pAbusive):
    p1 = np.sum(all_words_counter * p1Vec) + np.log(pAbusive)    # 乘以辱骂先验概率。与前对应,这里用了对数加!!
    p0 = np.sum(all_words_counter * p0Vec) + np.log(1.0 - pAbusive)
    if p1 > p0:
        return 1 # 分类1:辱骂
    else:
        return 0 # 分类2:否
       

   
if __name__ == '__main__':
    docs, classes = loadDataSet()
    all_words = create_all_words(docs)
    all_words_counters = []
    for doc in docs:
        all_words_counters.append(create_all_words_counter(all_words, doc))
    p0Vec, p1Vec, pAb = trainNB0(np.array(all_words_counters), np.array(classes))
    
    testDoc = ['love', 'my', 'dalmation']
    all_words_counter = np.array(create_all_words_counter(all_words, testDoc))
    print(testDoc, 'classified as: ', classifyNB(all_words_counter, p0Vec, p1Vec, pAb))
    
    testDoc = ['stupid', 'garbage']
    all_words_counter = np.array(create_all_words_counter(all_words, testDoc))
    print(testDoc, 'classified as: ', classifyNB(all_words_counter, p0Vec, p1Vec, pAb))
    
    

 

 

参考:

http://www.ruanyifeng.com/blog/2013/12/naive_bayes_classifier.html

http://blog.csdn.net/lsldd/article/details/41542107

http://www.jb51.net/article/57540.htm

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/168147.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • java字符串gb18030编码和utf8编码互转[通俗易懂]

    java字符串gb18030编码和utf8编码互转[通俗易懂]java字符串gb18030编码和utf8编码互转

  • Origin绘图后导出图片的方法

    Origin绘图后导出图片的方法在发表论文的时候,期刊要求的图线一般是tif格式的图,而不是Origin格式的。我们用Origin绘制完成一张图后,需要将它导出成图片格式,以满足投稿要求。这一节来介绍一下如何利用Origin导出图片。【注意,导出成图片格式后就不能通过双击图片链接到Origin程序进行编辑了】如下图所示,点击Origin菜单栏上的File—>ExportGraph—>OpenDialog

  • linux工具类之流量监视

    linux工具类之流量监视

  • Android 的CompoundButton(抽象类按钮)、StringBuffer(字符串变量)「建议收藏」

    Android 的CompoundButton(抽象类按钮)、StringBuffer(字符串变量)「建议收藏」1、写在前面的话本人40岁纯小白一枚,最近对AndroidAPP有了兴趣,目前的任务:通过AndroidStudio利用Apchepoi、EasyExcel等第三方库,编写APP,实现移动端APP与后台Excel的数据交互。这次利用CSDN平台记录下自己的成长。纯属小白,有概念或者描述错误,希望大佬们不吝赐教,再此谢过。2、任务目标目前正在学习这本书,在做P110页的作业时,发现的问题:作业要求,点击“进入主页”的按钮,一次性获取已近选取的多选框的text属性,然后Toast出

  • stat 函数详解

    stat 函数详解stat函数作用:获取文件信息头文件:include<sys/types.h>#include<sys/stat.h>#include<unistd.h>​函数原型:intstat(constchar*path,structstat*buf)​返回值:成功返回0,失败返回-1;​参数:文件路径(名),structstat…

    2022年10月24日
  • shell输出数组元素_shell中使用数组

    shell输出数组元素_shell中使用数组数组介绍平时的定义a=1,b=2,c=3,变量如果多了,再一个一个定义很费劲,并且取变量的也费劲简单的说,数组就是相同数据类型的元素按一定顺序排列的集合数组就是把有限个类型相同的变量用一个名字命名,然后用编号区分他们得边合。这个名字成为数组名,编号成为数组下标。组成数组的各个变量成为数组的分称为数组的元素,有时也称为下标变量数组定义与增删改查法1:array=(value1value2valu…

    2022年10月26日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号