《机器学习实战》第四章基于概率论的朴素贝叶斯

时间:2024-03-25 20:09:26

《机器学习实战》

第四章.基于概率论的分类方法朴素贝叶斯

4.1朴素贝叶斯名词概念解释

贝叶斯决策论:是概率框架下实施决策的基本方法。在所有相关概率都已知的理想情形下,贝叶斯决策论考虑如何基于这些概率和误判损失来选择最优的类别标记。

词向量:将语言数学化一种最简单的词向量方式是 one-hot representation,就是用一个很长的向量来表示一个词,向量的长度为词典的大小,向量的分量只有一个 1,其他全为 0, 1 的位置对应该词在词典中的位置。但这种词表示有两个缺点:(1)容易受维数灾难的困扰,尤其是将其用于 Deep Learning 的一些算法时;(2)不能很好地刻画词与词之间的相似性(术语好像叫做“词汇鸿沟”)。

特征:在此实验中文档中出现的词条。词条可以看成单词,将每一个文本片段表示为一个词条向量,其中值为1表示词条出现在文档中,0表示没出现。

词集模型:对于一篇文档中出现的每个词,我们不考虑其出现的次数,而只考虑其在文档中是否出现,并将此作为特征;假设我们已经得到了所有文档中出现的词汇列表,那么根据每个词是否出现,就可以将文档转为一个与词汇列表等长的向量。

词袋模型:就是在词集模型的基础上,还要考虑单词在文档中出现的次数,从而考虑文档中某些单词出现多次所包含的信息。

留存交叉验证:一份文件中,随机选择某些为测试集剩下的为训练集,进行算法验证的方式。

4.2基于贝叶斯决策理论的分类方法

朴素贝叶斯

优点:在数据较少的情况下依然有效,可以处理多分类问题

缺点:对于输入数据的准备方式较为敏感

适用数据类型:标称型数据

《机器学习实战》第四章基于概率论的朴素贝叶斯假设这个数据集,由两类数据组成,一个是圈,一个是三角

。其中的统计参数来源先忽略。

用p1(x,y)表示数据点(x,y)属于类别1圆点的概率

用p2(x,y)表示数据点(x,y)来表示类别2三角形的概率

对于新的数据点(x,y),用以下规则来判断他的类别

如果p1(x,y) > p2(x,y) 那么类别分为1

如果p2(x,y) > p1(x,y) 那么类别分为2

选择高概率对应的类别是贝叶斯决策理论的核心思想,即选择具有最高概率的决策。



4.3条件概率

4.3.1 概念

条件概率是指事件A在另外一个事件B已经发生条件下的发生概率。条件概率表示为:P(A|B),读作“在B条件下A的概率”。条件概率可以用决策树进行计算。

4.3.2 计算公式

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

4.3.3 使用条件概率进行分类

假设这里要被分类的类别有两类,类c1和类c2,那么我们需要计算概率p(c1|x,y)和p(c2|x,y)的大小并进行比较:

如果:p(c1|x,y)>p(c2|x,y),则(x,y)属于类c1

p(c1|x,y)<p(c2|x,y),则(x,y)属于类c2

p(x,y|c)表示的含义:已知类别c1条件下,取到点(x,y)的概率;

p(c1|x,y)表达的含义:在给定点(x,y)的条件下,求该点属于类c1的概率值。

这样的概率计算,利用贝叶斯准则来进行变换计算:p(ci|x,y)=p(x,y|ci)*p(ci)/p(x,y)

4.4朴素贝叶斯中朴素的含义

"朴素"含义:本章算法全称叫朴素贝叶斯算法,显然除了贝叶斯准备,朴素一词同样重要。这就是条件独立性假设的概念。条件独立性假设是指特征之间的相互独立性假设,所谓独立,是指的是统计意义上的独立,即一个特征或者单词出现的可能性与它和其他单词相邻没有关系。举个例子来说,假设单词bacon出现在unhealthy后面是与delisious后面的概率相同。当然,我们知道其实并不正确,但这正是朴素一词的含义。同时,朴素贝叶斯另外一个含义是,这些特征同等重要。虽然这些假设都有一定的问题,但是朴素贝叶斯的实际效果却很好。

4.5朴素贝叶斯的理解

公式:《机器学习实战》第四章基于概率论的朴素贝叶斯

最终目的:求出p(类别|特征)

4.5.1例题分析

给定数据如下:《机器学习实战》第四章基于概率论的朴素贝叶斯










问题是,如果一对男女朋友,男生想女生求婚,男生的四个特点分别是不帅,性格不好,身高矮,不上进,请你判断一下女生是还是不嫁

这是一个典型的分类问题,转为数学问题就是比较p(|(不帅、性格不好、身高矮、不上进))p(不嫁|(不帅、性格不好、身高矮、不上进))的概率,谁的概率大?

转化成贝叶斯公式:《机器学习实战》第四章基于概率论的朴素贝叶斯

根据公式转化成求变量:

p(不帅、性格不好、身高矮、不上进|嫁)、p(不帅、性格不好、身高矮、不上进)、p(嫁)

上述的三个变量则是根据已知的数据集进行推算

p(不帅、性格不好、身高矮、不上进|) = p(不帅|)*p(性格不好|)*p(身高矮|)*p(不上进|)

注释:这里就是朴素的来源,表示每一个特征是独立的

为什么将特征设置成为独立的?

1.             为了降低数据维度,假如没有这个假设,那么我们对右边这些概率的估计其实是不可做的,这么说,我们这个例子有4个特征,其中帅包括{帅,不帅},性格包括{不好,好,爆好},身高包括{高,矮,中},上进包括{不上进,上进},那么四个特征的联合概率分布总共是4维空间,总个数为2*3*3*2=36个。

2.             假如我们没有假设特征之间相互独立,那么我们统计的时候,就需要在整个特征空间中去找,比如统计p(不帅、性格不好、身高矮、不上进|嫁)

公式转化成,根据上述的公式,先计算p(|不帅、性格不好、身高矮、不上进)的概率为

 《机器学习实战》第四章基于概率论的朴素贝叶斯

= (1/2*1/6*1/6*1/6*1/2)/(1/3*1/3*7/12*1/3)

根据同样的方法,计算不嫁的概率。

公式如下:

《机器学习实战》第四章基于概率论的朴素贝叶斯

=((1/6*1/2*1*1/2)*1/2)/(1/3*1/3*7/12*1/3)

答案最终是不嫁

 

4.6朴素贝叶斯完成文档分类

4.6.1朴素贝叶斯的一般过程

 

朴素贝叶斯的一般过程

(1)      收集数据:可以使用任何方法。本章使用RSS

(2)      准备数据:需要数值型或者布尔型的数据

(3)      分析数据:有大量特征时绘制特征作用不大,此时使用直方图效果更好

(4)      训练算法:计算不同的独立特征的条件概率

(5)      测试算法:计算错误率

(6)      使用算法:一个常见的朴素贝叶斯应用是文档分类。可以在任意的分类场景中使用朴素贝叶斯分类器,不一定非要是文本

注:朴素贝叶斯分类器通常有两种实现方式:一种是基于贝努力模型实现的,一种基于多项式模型实现。这里采用前一种实现方式,该实现方式中并不考虑词在文档中的出现次数,只考虑出不出现因此在这个意义上相当于假设次是等权重的。

4.6.2训练算法

fromnumpyimport*
import re
#词表到向量的转换函数
#
实验样本函数
def loadDataSet():
   
postingList = [['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'],
                  
]
   
classVec = [0,1,0,1,0,1]#为什么留个数字
   
return postingList,classVec#返回的是词条切分后的文档集合,类标签的集合
#
创建一个包含再所有文档中出现的不重复词的列表
def createVocabList(dataSet):
   
vocabSet = set([])#创建一个空集包含在所有文档中出现的不重复词的列表
   
for documentindataSet:
       
vocabSet = vocabSet | set(document)#创建两个集合的并集document是单独的一个词向量
    #print('set',len(vocabSet))
    return list(vocabSet)
#此函数,输出的是文档向量,向量的每个元素为1或者0 分别表示词汇表中的单词再输入文档中是否出现
def setOfWords2Vec(vocabList,inputSet):#输入参数为词汇表及某个文档,输出的是文档向量
   
returnVec = [0]*len(vocabList)#创建一个与词汇列表向量长度相等的空向量
   
for wordininputSet:
       
if wordinvocabList:
           
returnVec[vocabList.index(word)] = 1#index()用来检测vocabList中是否含有word字符串
            #print(vocabList)
            #print(word)
        else: print("the word :%s %d is not in myVocabulary!"%word)
   
return returnVec#返回的每个inputset在整个词汇表中出现单词的数字向量最终是一个矩阵文档被转化成词向量

list0Posts,listClasses = loadDataSet()#得到图中示例的文档集合


myVocabList = createVocabList(listOposts)
print(setOfWords2Vec(myVocabList,listOposts[3]))

4.6.2 训练算法:从词向量中计算概率

前面将一组单词转换成了一组数字,下面将这些数字用来计算概率,已知,一个词是否出现在了一篇文档中,也知道该文档所属的类别。重写贝叶斯准则,将之前的xy,替换成w,w是一个向量,由多个值组成,重新写出贝叶斯准则:  

为每个类计算该值,然后比较这两个概率值的大小。

举例:通过类别i(侮辱性留言或非侮辱性留言)种文档数除以总的文档书来计算概率p(ci),计算p(w|ci)利用朴素贝叶斯假设。将w展开为一个个独立的特征,可以将概率写作p(w0|ci)p(w2|ci)…….来计算概率。

函数伪代码:
计算每个类别中的文档数目
对每篇训练文档:
    对每个类别:
    如果词条出现再文档中->增加该词条的计数值
    增加所有词条的计数值
对每个类别:
    对每个词条:
        将该词条的数目除以总词条数目的到的条件概率
返回每个类别的条件概率


#朴素贝叶斯分类器训练函数
def trainNBO(trainMatrix,trainCategory):#输入参数为文档矩阵trainMatrix,和每篇文档类别标签所构成的向量trainCategory
    numTrainDocs = len(trainMatrix)#初始的单词向量有六组,代表着总共有多少篇文章
   
#print('输入参数1',len(trainMatrix))
    #print('numTrainDocs',numTrainDocs)
    numWords = len(trainMatrix[0])#计算词汇集的长度
   
#print('numwords',numWords)
    #print('sum',sum(trainCategory))
    pAbusive = sum(trainCategory)/float(numTrainDocs)#计算的是属于侮辱性文档的概率
   
#print('pab',pAbusive) = 0.5 有一半是侮辱性文章
    p0Num = ones(numWords);p1Num=ones(numWords)#根据现实情况修改分类器
   
#print('p0',p0Num)
    p0Denom = 2.0;p1Denom =2.0#根据现实情况修改分类器
   
for i in range(numTrainDocs):#循环每一篇测试文章
       
if trainCategory[i] ==1:#如果是侮辱性文章
           
p1Num += trainMatrix[i]#向量相加并且其中的侮辱性词语多加了一个1
           
#print('p1num',p1Num)
            p1Denom += sum(trainMatrix[i])
           
#print('p1deom',p1Denom)
       
else:
           
p0Num += trainMatrix[i]
            p0Denom +=sum(trainMatrix[i])
    p1Vect = log(p1Num/p1Denom)
   p0Vect = log(p0Num/p0Denom)
    return p0Vect,p1Vect,pAbusive

list0Posts
,listClasses = loadDataSet()#listClasses类别
myVocabList=createVocabList(list0Posts)#得到一个set集合词汇表
#print('my',myVocabList)
trainMat = []

4.6.3测试算法:根据现实情况修改分类器

#朴素贝叶斯分类函数
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):#输入四个参数,要分类的向量vec2Classify使用函数trainNB0计算的三个概率
    p1 = sum(vec2Classify * p1Vec) +log(pClass1)#计算两个向量相乘的结果对应元素相乘,先将两个向量中的第一个元素相乘,
   
p0 = sum(vec2Classify * p0Vec) + log (1.0- pClass1)#然后将第二个元素相乘
   
if p1 > p0:
       
return 1
   
else:
       
return 0
def testingNB():
   
list0Posts,listClasses = loadDataSet()
   
myVocabList =createVocabList(list0Posts)
    trainMat = []
    for postinDoc in list0Posts:
       
trainMat.append(setOfWords2Vec(myVocabList,postinDoc))
   
p0V,p1V,pAb = trainNBO(array(trainMat),array(listClasses))
   
testEntry = ['love','my','dalmation']
   
thisDoc =array(setOfWords2Vec(myVocabList,testEntry))
   
print(testEntry,'classified as :',classifyNB(thisDoc,p0V,p1V,pAb))
   
testEntry = ['stupid','garbage']
   
thisDoc =array(setOfWords2Vec(myVocabList,testEntry))
   
print(testEntry,'classified as :',classifyNB(thisDoc,p0V,p1V,pAb))

4.6.4准备数据:文档词袋模型

#朴素贝叶斯的词袋模型
def bag0fWords2VecMN(vocabList,inputSet):
   
returnVec = [0]*len(vocabList)
   
for wordininputSet:
       
if wordinvocabList:
           
returnVec[vocabList.index(word)] += 1
   
return returnVec
mysent =
'this book isthe best book on python.'

4.7使用朴素贝叶斯过滤垃圾邮件

4.7.1 准备数据:切分文本

def textParse(bigString):#接收一个包含大写的字符串并将其解析为字符串列表,去掉了少于两个字符的字符串,并且将所有字符串砖汉成为小写
   
import re
   
listOfTokens = re.split(r'\W*',bigString)
   
return [tok.lower()fortokinlistOfTokensiflen(tok) >2]

4.7.2 测试算法:使用朴素贝叶斯进行交叉验证

def spamTest() :
   
docList = []; classList = [];fullText = []
    for i inrange(1,26) : # 导入并解析文本文件,25个普通邮件和25个垃圾邮件
        wordList = textParse(open('F:\\bayes\\email\spam\%d.txt'%i).read())
       
docList.append(wordList)
        fullText.extend(wordList)
        classList.append(1)
       
wordList = textParse(open('F:\\bayes\\email\ham\%d.txt'%i).read())
       
docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)
   
print(classList)
   
vocabList = createVocabList(docList)#将所有的普通文件汇集成词汇集合set
  
 trainingSet = list(range(50)); testSet = []
   
for i in range(10) :#随机构建训练集,包含10个样本
        randIndex = int(random.uniform(0,len(trainingSet)))
       
testSet.append(trainingSet[randIndex])
        del(trainingSet[randIndex])
   
trainMat = []; trainClasses = [] # 用于存放训练集和训练样本的标签
   
for docIndexintrainingSet:
       
trainMat.append(setOfWords2Vec(vocabList,docList[docIndex]))
       
trainClasses.append(classList[docIndex])
    p0V, p1V,pSpam = trainNBO(trainMat,trainClasses)
   
errorCount = 0
   
for docIndexintestSet :#对测试集进行分类
       
wordVector = setOfWords2Vec(vocabList,docList[docIndex])
       
if classifyNB(array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:#误判
           
errorCount += 1
   
print ('the error rate is: ',float(errorCount) /len(testSet))#输出分类误差

4.8 使用朴素贝叶斯分类器从个人广告中获取区域倾向

参考文档: 

Python3:报错解决方式:

http://blog.csdn.net/dillon2015/article/details/52987792

python中正则表达式模块re的使用:

http://lib.csdn.net/article/python/43174

基于朴素贝叶斯的垃圾邮箱分类

http://blog.csdn.net/taoyanqi8932/article/details/53083983

Python Numpy的数组array和矩阵matrix

http://blog.csdn.net/zhihaoma/article/details/51002742

python3报错解决办法:UnicodeDecodeError:'gbk' codec can't decode byte 0xae in position 199: illegal multib

http://blog.csdn.net/dillon2015/article/details/52987568

pythonextendappend的区别

https://www.cnblogs.com/subic/p/6553187.html