【机器学习实战】-基于概率论的分类方法:朴素贝叶斯
【机器学习实战】读书笔记
**朴素贝叶斯:**称为“朴素”的原因,整个形式化过程只做最原始、最简单的假设,特征之间没有关联,是统计意义上的独立。
**优点:**在数据较少的情况下仍然有效,可以处理多类别问题。
**缺点:**对于输入数据的准备方式较为敏感。
**适用数据类型:**标称型数据。
基于贝叶斯决策理论的分类方法
贝叶斯是贝叶斯决策理论的一部分,假设有两类数据p1(x, y)表示数据点(x, y)属于类别1的概率,p2(x, y)表示数据点属于类别2的概率,对一个新的数据点A(x, y),用下面的规则来判断它的类别:
- 如果p1(x, y) > p2(x, y),那么类别为1。
- 如果p2(x, y) > p1(x, y),那么类别为2。
以上原则体现出了贝叶斯决策论的核心思想,选择最高概率的决策。贝叶斯概率引入先验知识和逻辑推理来处理不确定命题。另一种概率解释称为频数概率(frequency probability),它只从数据本身获得结论,并不考虑逻辑推理及先验知识。
条件概率(贝叶斯准则)
贝叶斯准则
如果已知P(x|c),要求P(c|x),可以使用下面的计算方法:
p
(
c
∣
x
)
=
p
(
x
∣
c
)
p
(
c
)
p
(
x
)
p(c|x)=\frac{p(x|c)p(c)}{p(x)}
p(c∣x)=p(x)p(x∣c)p(c)
在这给出简单证明:
∵
p
(
x
∣
c
)
=
p
(
x
c
)
p
(
c
)
∴
p
(
x
∣
c
)
p
(
c
)
=
p
(
x
c
)
→
p
(
c
∣
x
)
=
p
(
x
c
)
p
(
x
)
∴
p
(
c
∣
x
)
=
p
(
x
∣
c
)
p
(
c
)
p
(
x
)
原命题得证
\because p(x|c)=\frac{p(xc)}{p(c)}\\ \therefore p(x|c)p(c)=p(xc) \to p(c|x)=\frac{p(xc)}{p(x)}\\ \therefore p(c|x)=\frac{p(x|c)p(c)}{p(x)}原命题得证
∵p(x∣c)=p(c)p(xc)∴p(x∣c)p(c)=p(xc)→p(c∣x)=p(x)p(xc)∴p(c∣x)=p(x)p(x∣c)p(c)原命题得证
语言通俗解释,p(x|c)p( c)获得就是c和x同时发生的概率,除以x发生的概率,就是在x的前提下c发生的概率。
使用条件概率来分类
对上面的点就可以使用贝叶斯分类来计算属于某个类c的概率
p
(
c
i
∣
x
,
y
)
=
p
(
x
,
y
∣
c
i
)
p
(
c
i
)
p
(
x
,
y
)
p(c_i|x,y)=\frac{p(x,y|c_i)p(c_i)}{p(x,y)}
p(ci∣x,y)=p(x,y)p(x,y∣ci)p(ci)
使用这些定义,可以定义贝叶斯分类准则为:
- 如果P(c1|x,y)>P(c2|x,y),那么属于类别c1。
- 如果P(c1|x,y)<P(c2|x,y),那么属于类别c2。
使用朴素贝叶斯进行文档分类
朴素贝叶斯是用于文档分类的常用算法。朴素贝叶斯的一般过程:
(1) 收集数据:可以使用任何方法。书上使用RSS源。
(2) 准备数据:需要数值型或者布尔型数据。
(3) 分析数据:有大量特征时,绘制特征作用不大,此时使用直方图效果更好。
(4) 训练算法:计算不同的独立特征的条件概率。
(5) 测试算法:计算错误率。
(6) 使用算法:一个常见的朴素贝叶斯应用是文档分类。可以在任意的分类场景中使用朴
素贝叶斯分类器,不一定非要是文本
书中还提到了独立的概念,独立指的是统计意义上的独立,即一个特征或者单词出现的可能性与它和其他单词相邻没有关系。这个假设正是朴素贝叶斯分类器中朴素(naive)一词的含义。朴素贝叶斯分类器中的另一个假设是,每个特征同等重要。举了个例子,如果每个特征需要N个样本,那么对于10个特征将需要N^10的样本,当特征为1000时,那就是N的1000次方,所需样本数随着特征数目的增大而迅速增大,如果特征之间相互独立,那么数据量就变为10特征时为10×N,特征数为1000时为1000×N,因为有关联的时候是十个特征共同构建一个模型,所以空间就是N的10次方,但是独立时,每个特征可以用N个数据单独建立,最后组合起来就可以了,所以是10×N。
朴素贝叶斯分类器通常有两种实现方式:一种基于贝努利模型实现,一种基于多项式模型实现。这里采用前一种实现方式。该实现方式中并不考虑词在文档中出现的次数,只考虑出不出现,因此在这个意义上相当于假设词是等权重的。后面给出的实际上是多项式模型,它考虑词在文档中的出现次数。
使用python进行文本分类
文本中获取特征,需要拆分文本,特征是来自文本的词条(token),一个词条是字符的任意组合。将每一个文本片段表示为一个词条向量,其中值为1表示词条出现在文档中,0表示词条未出现。
1.准备数据:从文本中构建词向量
建立词汇表,然后将文档转换为词汇表上的向量
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']
]
# 1代表侮辱性文字,0代表正常言论
classVec = [0, 1, 0, 1, 0, 1]
return postingList, classVec
def createVocaList(dataSet):
# 创建一个空集
vocabSet = set([])
# 创建并返回两个集合的并集
for document in dataSet:
vocabSet = vocabSet | set(document)
return list(vocabSet)
# 词集模型,每个词的出现与否作为一个特征
def setOfWords2Vec(vocabList, inputSet):
# 创建一个其中所含元素都为0的向量
returnVec = [0] * len(vocabList)
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
loadDataSet()函数创建一些实验样本,返回的第一个变量是词条切分后的文档集合,第二个返回的变量是类别标签集合,有两类,侮辱性和非侮辱性。createVocaList()函数使用set集合的形式,把文档中出现的不重复的词作为集合返回。而setOfWord2Vec()函数输入参数是词汇表vocabList和文档inputSet,创建一个为0的词汇表向量,来记录文档在当前词汇表所形成的向量,若文档中的词在词汇表中出现,则对应的词汇表向量标记为1,如果文档中的词条没在词汇表中,则不标记,最后返回文档对应的词汇表向量。
书中提到目前的setOfWord2Vec()函数是把词的出现与否作为一个特征,为词集模型(set-of-words model)。如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现在文档中所不能表达的某种信息,这种方法被称为词袋模型(bag-of-words model)。在词袋中,每个单词可以出现多次,而在词集中,每个词只能出现一次。词袋模型对应的词集模型(文档词向量标记函数)如下更修改
# 词袋模型,每个词出现的次数做为特征
def setOfWords2Vec1(vocabList, inputSet):
# 创建所含元素都为0的向量
returnVec = [0] * len(vocabList)
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
2.训练算法:从词向量计算概率
原理:重写贝叶斯准则,将之前的x、y替换为w。w表示是一个向量,有多个数值组成,在这个例子中,数值个数与词汇表中的词的个数相同即
p
(
w
)
=
1
p(w) = 1
p(w)=1,重写的贝叶斯准则也如下所示:
p
(
c
i
,
w
)
=
p
(
w
∣
c
i
)
p
(
c
i
)
p
(
w
)
p(c_i, w) = \frac{p(w|c_i)p(c_i)}{p(w)}
p(ci,w)=p(w)p(w∣ci)p(ci)
公式中p(w)已知,p(ci)可以直接根据标签量计算出来,唯一不好计算的就是p(w|ci),这里使用朴素贝叶斯的假设,将w展开为一个个独立的特征,就实现以下转换
p
(
w
0
,
w
1
,
w
2
.
.
.
w
N
∣
c
i
)
=
p
(
w
0
∣
c
i
)
p
(
w
1
∣
c
i
)
p
(
w
2
∣
c
i
)
…
p
(
w
N
∣
c
i
)
p(w_0, w_1,w_2...w_N|c_i)=p(w_0|c_i)p(w_1|c_i)p(w_2|c_i) \dots p(w_N|c_i)
p(w0,w1,w2...wN∣ci)=p(w0∣ci)p(w1∣ci)p(w2∣ci)…p(wN∣ci)
转换成计算每个类别的词条出现的概率。
伪代码如下:
计算每个类别中的文档数目
对每篇训练文档:
对每个类别:
如果词条出现在文档中->增加改词条的计数值
增加所有词条的计数值
对每个类别:
对每个词条:
将该词条的数目除以总词条数目得到条件概率
返回每个类别的条件概率
代码如下:
# 朴素贝叶斯分类器
def trainNB0(trainMatrix, trainCategory):
"""
:param trainMatrix: 对应标签的词组向量类似于[0,1,0,1,0,0,1]这种,记录每个标签对应的词条出现情况
:param trainCategory: 文档标签
:return:标签1的log(概率),标签2的log(概率),类别1的概率
"""
numTrainDocs = len(trainMatrix)
numWords = len(trainMatrix[0])
pAbusive = sum(trainCategory)/float(numTrainDocs)
# p0Num = zeros(numWords); p1Num = zeros(numWords)
# p0Denom = 0.0; p1Denom = 0.0
# 上面两个公式是假设每个特征独立,如果有个特征为0,则全部特征为0,为避免这种情况,将所有词出现数初始化为1,将分母初始化为2
p0Num = ones(numWords); p1Num = ones(numWords)
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 = log(p1Num/p1Denom)
p0Vect = log(p0Num/p0Denom)
return p0Vect, p1Vect, pAbusive
numTrainDocs记录文本数量,numWords记录词汇表的数量,pAbusive记录类别为1的比例是多少,p0Num、p0Denom分别记录类别为0的词汇量的个数,和词汇量的总数,同理类比p1Num和p1Denom。因为使用朴素贝叶斯假设,所以防止一个概率为0其他都为0,都初始化为1,分母初始化为2。因为大部分因子都非常小,防止程序下溢或浮点数舍入导致错误,采用自然对数进行处理,这样概率大的还是大,不会影响最终结果,所以采用log。
3.朴素贝叶斯分类函数
代码:
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
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 = createVocaList(listOPosts)
trainMat = []
for postinDoc in listOPosts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0V, p1V, pAb = trainNB0(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))
classifyNB是计算贝叶斯规则的,输入分别是要分类的向量vec2Classify,p(w|c0),p(w|c1),P(c1),多个参数构成w,所以使用numpy向量来计算,使用log函数,相乘等于各元素相加,sum()将要预测分类的向量含有可能的概率加起来,再加上分类概率就是最终概率。
testingNB()是测试函数,加载词汇表和标签,然后将词汇表合并成一个词条向量,把训练词汇数据标记为向量的形式存在trainMat中,然后计算类别1类别0等的概率,输入要预测的词汇向量,转换为向量形式,然后使用classifyNB进行预测。
运行效果:
文章来源:https://www.toymoban.com/news/detail-843285.html
4.项目完整代码
'''简单的朴素贝叶斯分类器'''
from numpy import *
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']
]
# 1代表侮辱性文字,0代表正常言论
classVec = [0, 1, 0, 1, 0, 1]
return postingList, classVec
def createVocaList(dataSet):
# 创建一个空集
vocabSet = set([])
# 创建并返回两个集合的并集
for document in dataSet:
vocabSet = vocabSet | set(document)
return list(vocabSet)
# 词集模型,每个词的出现与否作为一个特征
def setOfWords2Vec(vocabList, inputSet):
# 创建一个其中所含元素都为0的向量
returnVec = [0] * len(vocabList)
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
# 词袋模型,每个词出现的次数做为特征
def setOfWords2Vec1(vocabList, inputSet):
# 创建所含元素都为0的向量
returnVec = [0] * len(vocabList)
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
# 朴素贝叶斯分类器
def trainNB0(trainMatrix, trainCategory):
"""
:param trainMatrix: 对应标签的词组向量类似于[0,1,0,1,0,0,1]这种,记录每个标签对应的词条出现情况
:param trainCategory: 文档标签
:return:标签1的log(概率),标签2的log(概率),类别1的概率
"""
numTrainDocs = len(trainMatrix)
numWords = len(trainMatrix[0])
pAbusive = sum(trainCategory)/float(numTrainDocs)
# p0Num = zeros(numWords); p1Num = zeros(numWords)
# p0Denom = 0.0; p1Denom = 0.0
# 上面两个公式是假设每个特征独立,如果有个特征为0,则全部特征为0,为避免这种情况,将所有词出现数初始化为1,将分母初始化为2
p0Num = ones(numWords); p1Num = ones(numWords)
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 = log(p1Num/p1Denom)
p0Vect = log(p0Num/p0Denom)
return p0Vect, p1Vect, pAbusive
# listOPosts, listClasses = loadDataSet()
# myVocablist = createVocaList(listOPosts)
# 创建列表包含所有词
# trainMat = []
# for postinDoc in listOPosts:
# trainMat.append(setOfWords2Vec(myVocablist, postinDoc))
#
# p0v, p1v, pAb=trainNB0(trainMat, listClasses)
# print(pAb)
# print(p0v)
# print(p1v)
# 朴素贝叶斯分类函数
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
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 = createVocaList(listOPosts)
trainMat = []
for postinDoc in listOPosts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0V, p1V, pAb = trainNB0(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))
testingNB()
总结与反思
这个例子主要是来讲解朴素贝叶斯怎么进行文本情感分类,基于条件概率和特征独立来计算词条向量的情感概率值,有很多地方都进行了简化,像开始例子就提供了词汇表,不需要分词,自己在做文本情感分析的时候上来就需要分词,建立词汇向量表,预测文本也得做分词处理形成分词向量,这里简化了。
如有错误欢迎指正,加油,时间不等人。文章来源地址https://www.toymoban.com/news/detail-843285.html
到了这里,关于【机器学习实战】-基于概率论的分类方法:朴素贝叶斯的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!