【机器学习】朴素贝叶斯算法

朴素贝叶斯--垃圾邮件分类过滤

  • 一、贝叶斯介绍
    • 【补充】
  • 二、朴素贝叶斯介绍
    • 2.1、朴素贝叶斯----垃圾邮件过滤实例
    • 2.2、朴素贝叶斯----垃圾邮件过滤实例(代码实现)
  • 三、总结

一、贝叶斯介绍

英国数学家.1701年出生于伦敦,做过神甫.1742年成为英国皇家学会会员.1763年4月7日逝世.贝叶斯在数学方面主要研究概率论.他首先将归纳推理法用于概率论基础理论,并创立了贝叶斯统计理论,对于统计决策函数、统计推断、统计的估算等做出了贡献.1763年发表了这方面的论著,对于现代概率论和数理统计都有很重要的作用.贝叶斯的另一著作《机会的学说概论》发表于1758年.贝叶斯所采用的许多术语被沿用至今.

他对统计推理的主要贡献是使用了"逆概率"这个概念,并把它作为一种普遍的推理方法提出来。贝叶斯定理原本是概率论中的一个定理,这一定理可用一个数学公式来表达,这个公式就是著名的贝叶斯公式。
结合上学期概率论的知识我们来举一个例子来看看贝叶斯公式。

我们假设集美大学男女比例6:4,男生都是有明显喉结,女生20%有明显喉结,80%无明显喉结。

【机器学习】朴素贝叶斯算法_第1张图片
如果我们去正向地推这个概率:随机取一个学生,他有喉结和没喉结的概率是多少?

如果我们逆向去推这个概率:有一个有明显喉结的学生,你只清楚他是否有明显喉结,但是无法判断他的性别,那此时你能判断出他是女生的概率吗?(虽然有点不切实际。。。)

那接下来就开始算一下:
假设我们集美大学总人数是X。
那么我们有明显喉结的男生的概率: X * P(boy) * P(obvious-throat | boy)
其中,P(boy) = 60%
P(obvious-throat | boy)是条件概率,他是指在是男生下明显喉结的概率,又上述给的条件男生都是有明显喉结,所以就是100%

有明显喉结的女生是 X * P(girl) * P(obvious-throat | girl)
那我们求明显喉结里有多少女生

明显喉结总数:X * P(boy) * P(obvious-throat | boy) + X * P(girl) * P(obvious-throat | girl)(就是上述所求两者相加)

那此时概率不就变成了如图:

请添加图片描述
那我们消去X,最后不就跟总数没关系了嘛,所以化简如图:
请添加图片描述
将分母可以合并成P(obvious-throat ),分子就是P(girl) * P(obvious - throat | girl )
【机器学习】朴素贝叶斯算法_第2张图片
那最后不就变成下图:
【机器学习】朴素贝叶斯算法_第3张图片
这个看得有点眼熟,这不就是把girl 看出 A,obvious - throat 看成B嘛,然后得到下面公式:
【机器学习】朴素贝叶斯算法_第4张图片
这不就是贝叶斯公式吗。

【补充】

先验概率:
一般都是单独事件发生的概率,如 P(A)、P(B)。 预判概率,可以是基于历史数据的统计,可以由背景常识得出,也可以是人的主观观点给出。

后验概率:
(若 P(A|B) 为正向,则 P(B|A) 为反向) ,基于先验概率求得的反向条件概率,形式上与条件概率相同

【机器学习】朴素贝叶斯算法_第5张图片

  1. P(A|B)是后验概率,一般是我们求解的目标。
  2. 2.P(A|B)是条件概率,又叫似然概率,一般是通过历史数据统计得到。一般不把它叫做先验概率,但从定义上也符合先验定义。
  3. P(A) 是先验概率,一般都是人主观给出的。贝叶斯中的先验概率一般特指它。
  4. P(B)其实也是先验概率,只是在贝叶斯的很多应用中不重要(因为只要最大后验不求绝对值),需要时往往用全概率公式计算得到。

二、朴素贝叶斯介绍

朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法 。

在上学期概率论也提到了贝叶斯定理:这个在250多年前发明的算法,在信息领域内有着无与伦比的地位。贝叶斯分类是一系列分类算法的总称,这类算法均以贝叶斯定理为基础,故统称为贝叶斯分类。朴素贝叶斯算法(Naive Bayesian) 是其中应用最为广泛的分类算法之一。

朴素贝叶斯分类器基于一个简单的假定:给定目标值时属性之间相互条件独立。怎么去理解这个,那就举个例子

2.1、朴素贝叶斯----垃圾邮件过滤实例

给你一封邮件,让你判断这是否属于垃圾邮件,其中D表示这封邮件,D里面有X个单词组成。d+表示垃圾邮件,d-表示正常邮件。
【机器学习】朴素贝叶斯算法_第6张图片
【机器学习】朴素贝叶斯算法_第7张图片

先验概率:P(d+)和P(d-)这两个概率好求,只是算垃圾邮件和正常邮件的比例

P(D)这两个公式里都有其实就是只比较分子大小,所以P(D)不用看,可以消掉。

那么现在就是看P(D|d+)和P(D|d-)。

那就来拿P(D|d+)来看,D里面有X个单词,那么P(D|d+)不就是等于P(d1,d2,d3,…,dx| d+) 也就是说这个垃圾邮件里出现跟我们这封邮件一样的概率是多少。那是不是可以理解为每个单词一样的概率是多少?
那么就可以转换为

请添加图片描述

就是在垃圾邮件中第一个单词相同概率,垃圾邮件第一个单词相同中第二个单词相同概率,以此类推。
这个式子前几个好算,但是后面就越来越难了。
所以我们就假设di和di-1是独立无关的,所以我们公式可以简化为:请添加图片描述
那这样我们就可以分别算每个单词在垃圾邮件里出现的概率的累乘起来不就是P(D|d+)嘛,同理可得P(D|d-)也是一样的,然后再比较,那个大就属于那个不就可以解决了吗。

2.2、朴素贝叶斯----垃圾邮件过滤实例(代码实现)

首先看到这里可以运用自己qq邮箱里的邮件然后用那些开头是广告的为垃圾邮件,其他的就是正常邮件就行。
首先要用这个qq邮箱读取时要有授权码,进入设置点击用户,找到POP3/SMTP服务 或者 IMAP/SMTP服务,然后直接根据你使用的服务器来打开(我这里直接用smtp都可以用,随便开启那个都行)
【机器学习】朴素贝叶斯算法_第8张图片
【机器学习】朴素贝叶斯算法_第9张图片

这里我使用了Imbox来读取所有的2022年9月19号的文件,直接打印并保存到txt文件,但是这里运行成功,没保存所有文件内容只保存了最后文件的内容,所以我是直接在输出那里直接输出。


import keyring
from imbox import Imbox

password = keyring.get_password('mpwdsaqbksbtbcfd', '[email protected]')

with Imbox('smtp.qq.com', '[email protected]', "mpwdsaqbksbtbcfd", ssl=True) as imbox:

# 获取全部邮件

    inbox_message_after = imbox.messages(date__gt=datetime.date(2022, 9, 19))
    for uid, message in inbox_message_after:

        print(message.subject)  # 邮件主题
        #print(message)
        #print(message.body['plain']) # 邮件文本格式正文
        
        f = open("E:/new/1.txt", 'w',encoding='gb18030')
        f.write(message.subject)

然后我这里就选择简体中文来方便后面翻译
【机器学习】朴素贝叶斯算法_第10张图片

输出之后就选取后面带有广告的部分作为垃圾邮件,其他的部分作为正常邮件,并且要翻译后保存。
【机器学习】朴素贝叶斯算法_第11张图片
后面的就是简单实现朴素贝叶斯----垃圾邮件分类过滤


```python
def createVocabList(dataSet):#创建词汇表
    vocabSet = set([]) #不重复的词,唯一的,集合
    for document in dataSet:#每个文章取词
        vocabSet = vocabSet | set(document) #创建并集
    return list(vocabSet)


def setOfWords2Vec(vocabList, inputSet):
    returnVec = [0] * len(vocabList) #创建一个其中所含元素都为0的向量
    for word in inputSet:           #遍历每个词条
        if word in vocabList:         #如果词条存在于词汇表中,则置1
            returnVec[vocabList.index(word)] = 1
        else: print("the word: %s is not in my Vocabulary!" % word)
    return returnVec      
#词袋模型:
def bagOfWord2VecMN(vocabList,inputSet):#对照词汇表,将输入句子转化为0,1组成的向量
    returnVec = [0]*len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1
    return returnVec

然后这里就是训练算法,这里其实要注意两个点,一个是初始化概率那边,要做平滑处理,因为每个项独立时是相乘,那如果后面乘上0的话,那最后不就是0了嘛,所以要平滑处理,一般都是按照分类的个数赋初值,这里是二分类,所以初值赋值为2,然后第二个就是最后只是比较大小判断是垃圾邮件还是正常邮件,那万一到后面两个都是很小的概率,很小的数相乘那不是接近0了,那要是位数再多点,然后就在四舍五入那不还是0嘛,所以我们就加上log,就只是比较大小,那就可以更好的显示效果

#训练算法:使用我们之前建立的trainNB0()函数
def trainNB0(trainMatrix,trainCategory):#输入参数为文档矩阵trainMatrix,和每篇文档类别标签所构成的向量trainCategory
    numTrainDocs = len(trainMatrix)#样本个数
    numWords = len(trainMatrix[0])
    pAbusive = sum(trainCategory)/float(numTrainDocs)
    #p0Num = zeros(numWords); p1Num = zeros(numWords)     #初始化概率  
    #p0Denom = 0.0; p1Denom = 0.0 
    p0Num = ones(numWords); p1Num = ones(numWords)      #防止后面乘上0了,平滑处理
    p0Denom = 2.0; p1Denom = 2.0    #通常设置成类别个数                   
    for i in range(numTrainDocs):   
        if trainCategory[i] == 1:#垃圾邮件
            p1Num += trainMatrix[i]
            p1Denom += sum(trainMatrix[i])#总次数
        else:
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    #p1Vect = p1Num/p1Denom         #change to log()
    #p0Vect = p0Num/p0Denom         #change to log()
    p1Vect = log(p1Num/p1Denom)     #如果很多非常小的值乘上然后四舍五入可能等于0,但我们只需要知道这两个的大小,所以转换成对数     
    p0Vect = log(p0Num/p0Denom)   
    return p0Vect,p1Vect,pAbusive#正常邮件,垃圾邮件,垃圾邮件概率

测试算法,看那个大,就返回对应的类型对应的数值

#测试算法:进行交叉验证
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    #vec2Classify * p1Vec是每个词出现的词频,0*数=0,1*数等于本身
    p1 = sum(vec2Classify * p1Vec) + log(pClass1)    #同时等式两边加上对数,没影响
    p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)   #2分类问题可以用1.0-pClass1,多分类需要修改
    if p1 > p0:
        return 1
    else: 
        return 0
#解析英文文本,并返回列表
def textParse(bigString):
    #将单词以空格划分
    listOfTokens = bigString.split()
    #去除单词长度小于2的无用单词
    return [tok.lower() for tok in listOfTokens if len(tok)>2]
#def textParse(bigString):
    import re
    listOfTokens =re.split(r'\W*',bigString)#切分
    return [tok.lower() for tok in listOfTokens if len(tok)>2]#全部变成小写的,最少切分两个词
def spamTest():
    #初始化数据列表
    docList = []; classList = []; fullText = []
    #spam和ham文件夹里的邮件是25封,所以用for循环25次
    for i in range(1, 26):
        #切分文本
        wordList = textParse(open("E:/workplace/environment/email/spam/%d.txt" % i).read())
        #切分后的文本以原始列表形式加入文档列表
        docList.append(wordList)
        #切分后的文本直接合并到词汇列表
        fullText.extend(wordList)
        #标签列表更新
        classList.append(1)#垃圾邮件
       
        wordList = textParse(open("E:/workplace/environment/email/ham/%d.txt" % i).read())
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)#正常邮件
        
        
    #创建一个包含所有文档中出现的不重复词的列表
    vocabList = createVocabList(docList)
    #初始化训练集和测试集列表
    trainingSet = list(range(50)); #取50个,全部
    testSet = []
    #随机构建测试集,随机选取10个样本作为测试样本,并从训练样本中剔除
    for i in range(10):
        #randIndex=random.uniform(a,b)用于生成指定范围内的随机浮点数
        randIndex = int(random.uniform(0, len(trainingSet)))
        #将该样本加入测试集中
        testSet.append(trainingSet[randIndex])#取train里面的放进test
        #同时将该样本从训练集中剔除
        del(trainingSet[randIndex])
    
    #初始化训练集数据列表和标签列表
    trainMat = []; trainClasses = []
    
    #遍历训练集
    for docIndex in trainingSet:
        #词表转换为向量,并加入到训练数据列表中
        trainMat.append(bagOfWord2VecMN(vocabList, docList[docIndex]))
        #相应的标签也加入训练标签列表中
        trainClasses.append(classList[docIndex])
    #朴素贝叶斯分类器训练函数
    p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses))
    #初始化错误计数
    errorCount = 0
   
   
    
     #遍历测试集来测试
    for docIndex in testSet:
        print(docIndex)
        #词表转换为向量
        wordVector = bagOfWord2VecMN(vocabList, docList[docIndex])
        #判断分类结果与原标签是否一致
        if classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
            #如果不一致则错误计数加1
            errorCount += 1
            #并且输出出错的文档
            print("classification error",docList[docIndex])
    #打印输出信息
    print('the erroe rate is: ', float(errorCount)/len(testSet))
    print(errorCount)
    #返回词汇表和全部单词列表

最后测试:

spamTest()

截图如下:【机器学习】朴素贝叶斯算法_第12张图片
【机器学习】朴素贝叶斯算法_第13张图片
【机器学习】朴素贝叶斯算法_第14张图片
这里就是因为数据比较少就50个,所以这个错误率就相对少一些。

三、总结

朴素贝叶斯法属于比较强条件下的一个生成模型,通过极大似然估计或贝叶斯估计,在特征条件独立的假设下,运用概率论中贝叶斯定理对新的输入进行预测。算法逻辑简单,易于实现,并且分类过程中时空开销小,但是,上面那个就拿垃圾邮件分类来说,不可能第一个单词相同和第二个单词相同是独立的,没关系的,所以,朴素贝叶斯模型假设属性之间相互独立,这个假设在实际应用中往往是不成立的,在属性个数比较多或者属性之间相关性较大时,分类效果不好。

你可能感兴趣的:(概率论)