一、朴素贝叶斯理论
1、贝叶斯决策理论
贝叶斯决策理论核心思想:
- 如果p1(x,y) > p2(x,y),那么类别为1
- 如果p1(x,y) < p2(x,y),那么类别为2
哪个概率高则选择哪一个类别。其中,p1(x,y)表示数据点(x,y)属于类别1的概率。
2、贝叶斯准则-条件概率(conditional probability)计算
P ( A ∣ B ) = P ( A ∩ B ) P ( B ) P(A|B)={{P(A∩B)}\over{P(B)}} P(A∣B)=P(B)P(A∩B)
-
第一种条件概率计算式:
可得 P ( A ∩ B ) = P ( A ∣ B ) P ( B ) P(A∩B)=P(A|B)P(B) P(A∩B)=P(A∣B)P(B) 同理可得 P ( A ∩ B ) = P ( B ∣ A ) P ( A ) P(A∩B)=P(B|A)P(A) P(A∩B)=P(B∣A)P(A)所以 P ( A ∣ B ) P ( B ) = P ( B ∣ A ) P ( A ) P(A|B)P(B)=P(B|A)P(A) P(A∣B)P(B)=P(B∣A)P(A)即 P ( A ∣ B ) = P ( B ∣ A ) P ( A ) P ( B ) P(A|B)={{P(B|A)P(A)}\over{P(B)}} P(A∣B)=P(B)P(B∣A)P(A) -
第二种条件概率计算公式(全概率):
样本空间由红色部分的A和绿色部分的A’组成
其中 P ( B ) = P ( B ∩ A ) + P ( B ∩ A ′ ) P(B)=P(B∩A)+P(B∩A') P(B)=P(B∩A)+P(B∩A′)由上节推导可知 P ( B ∩ A ) = P ( B ∣ A ) P ( A ) P(B∩A)=P(B|A)P(A) P(B∩A)=P(B∣A)P(A)可得全概率公式 P ( B ) = P ( B ∣ A ) P ( A ) + P ( B ∣ A ′ ) P ( A ′ ) P(B)=P(B|A)P(A)+P(B|A')P(A') P(B)=P(B∣A)P(A)+P(B∣A′)P(A′)其含义是:如果A和A’构成样本空间,那么事件B的概率,就等于A和A’的概率分别乘以B对这两个事件的条件概率之和。 P ( A ∣ B ) = P ( B ∣ A ) P ( A ) P ( B ∣ A ) P ( A ) + P ( B ∣ A ′ ) P ( A ′ ) P(A|B)={{P(B|A)P(A)}\over{P(B|A)P(A)+P(B|A')P(A')}} P(A∣B)=P(B∣A)P(A)+P(B∣A′)P(A′)P(B∣A)P(A)
3、两种推断
- 贝叶斯推断
P
(
A
∣
B
)
=
P
(
A
)
P
(
B
∣
A
)
P
(
B
)
P(A|B)=P(A){{P(B|A)}\over{P(B)}}
P(A∣B)=P(A)P(B)P(B∣A)其中,P(A)称为“先验概率(prior probability)”,即在B事件发生之前,我们对A事件概率的一个判断。
P(A|B)称为“后验概率(posterior probability)”,即在B事件发生之后,我们对事件概率的重新评估。
P(B|A)/{P(B)称为“可能性函数(likelyhood),这是一个调整因子,使得预估概率更接近真实概率。
后验概率 = 先验概率 x 调整因子
- 朴素贝叶斯推断
朴素贝叶斯对条件概率分布做了条件独立性假设。 P ( a ∣ X ) = p ( X ∣ a ) p ( a ) = p ( x 1 , x 2 , . . . . . , x n ∣ a ) p ( a ) P(a|X)=p(X|a)p(a)=p(x1,x2,.....,xn|a)p(a) P(a∣X)=p(X∣a)p(a)=p(x1,x2,.....,xn∣a)p(a)假设每个特征独立,则进一步拆分公式 P ( a ∣ X ) = p ( X ∣ a ) p ( a ) = p ( x 1 ∣ a ) ∗ p ( x 2 ∣ a ) ∗ . . . . . ∗ p ( x n ∣ a ) p ( a ) P(a|X)=p(X|a)p(a)={p(x1|a)*p(x2|a)*.....*p(xn|a)}p(a) P(a∣X)=p(X∣a)p(a)=p(x1∣a)∗p(x2∣a)∗.....∗p(xn∣a)p(a)
4、贝叶斯定理分类思想
基于概率,通过某对象的先验概率,利用贝叶斯公式计算出其后验概率,即该对象属于某一类的概率,选择具有最大后验概率的类作为该对象所属的类。
5、贝叶斯分类方法种类
朴素贝叶斯:各特征间独立,直接计算条件概率进行分类,是最简单的一种贝叶斯分类算法。
贝叶斯网络:不满足各特征独立条件时,使用贝叶斯网络分类。贝叶斯网络也称为信念网络,或有向无环图模型,是一种概率图模型。
6、贝叶斯分类典型应用
垃圾邮件过滤
文本情感分类:电影影评
论坛真实账号监测
浏览器输入自动提示
7、朴素贝叶斯优缺点
基于概率论进行分类,朴素的含义是:各样本特征间独立
优点:可以处理小样本情况,可以处理多分类问题
缺点:对于输入数据比较敏感
8、朴素贝叶斯的三种实现
- 离散:基于伯努力模型实现,即先验为伯努力分布的朴素贝叶斯
伯努力模型中,对于一个样本来说,其特征用的是全局的特征,每一个特征的取值是布尔型,即true和false或者1和0.在文本分类中,就是一个特征有没有在一个文档中出现,而不考虑其出现的次数。在文本里面对应的就是词集模型:单词构成的集合,集合自然每个元素都只有一个,也即词集中的每个单词都只有一个。 - 离散:基于多项式模型实现,即先验为多项式分布的朴素贝叶斯
该模型常用于文本分类,特征是单词,取值是单词的出现次数,对应的就是词袋模型:如果一个单词在文档中出现不止一次,并统计其出现的次数(频数)。是在自然语言处理和信息检索中的一种简单假设。在这种模型中,文本(段落或文档)被看作是无序的词汇集合,忽略语法甚至是单词的顺序。 - 连续:基于高斯模型实现,即先验为高斯分布的朴素贝叶斯
当特征是连续变量的时候,运用多项式模型就会导致很多条件概率为0,此时即使做平滑,所得到的条件概率也难以描述真实情况,存在较大偏差,在处理连续的特征变量,应采用高斯模型。连续变量高斯离散化计算过程:利用样本的特征列计算出均差和方差,然后构造概率密度函数,将每个具体样本值代入到密度函数中,得到概率值,该概率值可以反映各个值的相对可能性。
朴素贝叶斯改进
- 拉普拉斯平滑:防止由于某一个概率为0,导致分类概率为0的不合理情形
- 条件概率对数化:防止小数相乘出现的下溢问题
二、朴素贝叶斯功能块实现
def loadDataSet(): # 导入数据
def createVocabList(dataSet): # 建立基于训练集的词汇表
def setOfWords2Vec(vocabList,inputSet): # 将训练集向量化
def trainNB0(trainMatrix,trainCategory): # 基于训练集和类别标签训练算法输出概率集合
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1): # 输入测试集和训练后的概率集测试算法
def testingNB(): # 主函数
#!/user/bin/env python
# -*- coding:utf-8 -*-
#@Time : 2020/3/7 16:28
#@Author: fangyuan
#@File : 朴素贝叶斯构建词向量.py
def loadDataSet():
"""
@return:
"""
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']]
# 1 代表侮辱性文字,0 代表正常言论
classVec = [0,1,0,1,0]
return postingList,classVec
def createVocabList(dataSet):
"""
@param dataSet:
@return:创建一个包含所有文档中出现的不重复词的列表,将切分的实验样本词条整理成不重复的词条列表,也就是词汇表
"""
# set()返回一个不重复词表
# 首先,创建一个空集合vocabSet,然后,将每篇文档返回的新词集合添加到该集合中
vocabSet = set([])
for document in dataSet:
vocabSet = vocabSet | set(document)
return list(vocabSet)
def setOfWords2Vec(vocabList,inputSet):
"""
@param vocabList:参考的词汇表
@param inputSet:输入文档
@return:根据vocabList词汇表,将inputSet向量化,输出文档向量,向量的每一个元素为1或0,分别表示词汇表中的单词在输入文档中是否出现
"""
# 首先创建一个和词汇表等长的向量,并将其元素都设置为0
returnVec = [0]*len(vocabList)
# 遍历文档中所有单词,如果出现了词汇表中的单词,则将输出的文档向量中的对应值设为1
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] = 1
else:print("the word:%s is not in my Vocabulary!" % word)
return returnVec
if __name__ == '__main__':
postingList,classVec = loadDataSet()
print('postingList:\n',postingList)
myVocabList = createVocabList(postingList)
print('myVocabList:\n',myVocabList)
trainMat = []
for postinDoc in postingList:
trainMat.append(setOfWords2Vec(myVocabList,postinDoc))
print('trainMat:\n',trainMat)
#!/user/bin/env python
# -*- coding:utf-8 -*-
#@Time : 2020/3/9 11:24
#@Author: fangyuan
#@File : 朴素贝叶斯分类.py
import numpy as np
from functools import reduce
from math import log
def loadDataSet():
"""
@return:
"""
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']]
# 1 代表侮辱性文字,0 代表正常言论
classVec = [0,1,0,1,0,1]
return postingList,classVec
# 创建词汇表-文档向量化的第一步
def createVocabList(dataSet):
"""
@param dataSet:
@return:创建一个包含所有文档中出现的不重复词的列表,将切分的实验样本词条整理成不重复的词条列表,也就是词汇表
"""
# set()返回一个不重复词表
# 首先,创建一个空集合vocabSet,然后,将每篇文档返回的新词集合添加到该集合中
vocabSet = set([])
for document in dataSet:
vocabSet = vocabSet | set(document)
return list(vocabSet)
# 词集法-文档向量化第二步
def setOfWords2Vec(vocabList,inputSet):
"""
@param vocabList:参考的词汇表
@param inputSet:输入文档
@return:根据vocabList词汇表,将inputSet向量化,输出文档向量,向量的每一个元素为1或0,分别表示词汇表中的单词在输入文档中是否出现
"""
# 首先创建一个和词汇表等长的向量,并将其元素都设置为0
returnVec = [0]*len(vocabList)
# 遍历文档中所有单词,如果出现了词汇表中的单词,则将输出的文档向量中的对应值设为1
for word in inputSet:
# 如果词条存在于词汇表中,则词汇表对应置1
if word in vocabList:
returnVec[vocabList.index(word)] = 1
else:print("the word:%s is not in my Vocabulary!" % word)
# 返回文档向量,其长度为词汇表长度
return returnVec
"""
条件概率计算;
P(侮辱类|stupid,garbage)=P(stupid|侮辱类)*P(garbage|侮辱类)*P(侮辱类)/P(stupid,garbage)
P(非侮辱类|stupid,garbage)=P(stupid|非侮辱类)*P(garbage|非侮辱类)*P(非侮辱类)/P(stupid,garbage)
由于我们只需要比较大小,且分母相同,固只需要计算分子即可
P(stupid|侮辱类)计算:首先找出所有的侮辱类样本,构造子空间,然后在该子空间中计算stupid单词出现的概率即可
[take,help,stop,posting,food,park,so,worthless,garbage,how,is,him,mr,licks......]
"""
def trainNB0(trainMatrix,trainCategory):
"""
@param trainMatrix: 经转化后得到的向量矩阵,每一行的长度为单词表长度
@param trainCategory: 给定的类别,classVec = [0,1,0,1,0,1]
@return:
"""
# 计算训练的文档数量,矩阵为6行,即为6个样本
numTrainDocs = len(trainMatrix)
# 计算每篇文档的词条数,32个,即有32个特征
numWords = len(trainMatrix[0])
# 文档属于侮辱类的概率,P(侮辱类),3/6=0.5
pAbusive = sum(trainCategory)/float(numTrainDocs)
# 创建numpy.one数组,词条出现数初始化为1,拉普拉斯平滑
# p0Num存放非侮辱类概率即非侮辱类中take,help,stop.....的概率,初始化长度为32,且值都为1,如P(garbage|非侮辱类)
# p1Num存放侮辱类概率如P(garbage|侮辱类)
p0Num = np.ones(numWords)
p1Num = np.ones(numWords)
# p0Num = np.zeros(numWords)
# p1Num = np.zeros(numWords)
# 分母初始化为2,拉普拉斯平滑
p0Denom = 2.0
p1Denom = 2.0
# p0Denom = 0.0;
# p1Denom = 0.0
for i in range(numTrainDocs):
# 将文档中数据归类,为1则放在侮辱类数组中,为0则放在非侮辱类数组中
if trainCategory[i] == 1:
# p1Num是数组
# 向量化算法都是32个特征概率一起算的,不用for循环
# 将标签为1侮辱类的样本数据全部加到一起,即将第2,4,6行数据加到一起,变成只包括1行数据,32个特征
# 将2,4,6列单词个数加起来(包括重复单词)
# [1,2,1,1,1,1,2,2,4,2,2,3,2,1,1,1,1,2,2,2,2,1,2,1,1,1,2,1,3,1,1,1]
p1Num += trainMatrix[i]
# p1Denom是数:第2,4,6行元素个数21
p1Denom += sum(trainMatrix[i])
else:
#[2,1,4,2,2,2,1,2,1,3,1,1,1,2,2,2,2,1,1,1,2,2,1,2,2,2,1,2,2,2,2,2]
p0Num += trainMatrix[i]
# 第1,3,5行元素个数24个
p0Denom += sum(trainMatrix[i])
# 取对数,防止下溢出
p1Vect = np.log(p1Num/p1Denom)
p0Vect = np.log(p0Num/p0Denom)
# p1Vect = p1Num/p1Denom
# p0Vect = p0Num/p0Denom
# 返回属于侮辱类的条件概率数组,属于非侮辱类的条件概率数组,文档属于侮辱类的概率
return p0Vect,p1Vect,pAbusive
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):
# P(stupid|侮辱类)*P(garbage|侮辱类)*P(侮辱类)
# sum(vec2Classify * p1Vec)表示P(stupid|侮辱类)*P(garbage|侮辱类),log(pClass1)表示P(侮辱类)
# 其中,p1Vec本身是由log表示,所以sum和+表示了概率计算式的*
p1 = sum(vec2Classify * p1Vec) + log(pClass1)
p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
if p1 > p0:
return 1
else:
return 0
def testingNB():
listOPosts,listClasses = loadDataSet()
# 创建词汇表,文档向量化第一步
myVocabList = createVocabList(listOPosts)
trainMat = []
# 向量化-文档向量化第二步
for postinDoc in listOPosts:
# 将一个一个样本,利用词汇表转换成词向量
trainMat.append(setOfWords2Vec(myVocabList,postinDoc))
# 训练-计算条件概率 0-非侮辱类条件概率集 1-侮辱类条件概率集
p0V,p1V,pAb = trainNB0(np.array(trainMat),np.array(listClasses))
testEntry = ['love','my','dalmation']
thisDoc = np.array(setOfWords2Vec(myVocabList,testEntry))
print(testEntry,'classified as:',classifyNB(thisDoc,p0V,p1V,pAb))
testEntry = ['stupid','garbage']
thisDoc = np.array(setOfWords2Vec(myVocabList,testEntry))
print(testEntry,'classified as:',classifyNB(thisDoc,p0V,p1V,pAb))
if __name__ == '__main__':
testingNB()