前言
分类器有时会产生错误结果,这是要求分类器给出一个最优的类别猜测结果,同时给出这个猜测的概率估计值。
1.使用概率论分布进行分类
2.学习朴素贝叶斯分类器
基于贝叶斯决策理论的分类方法
优点:在数据较少的情况下仍然有效,可以处理多类别问题;
缺点:对于输入数据的准备方式较为敏感;
适用数据类型:标称型数据
朴素贝叶斯是贝叶斯决策理论的一部分,所以讲述朴素贝叶斯之前有必要了解一下贝叶斯决策理论。
贝叶斯决策理论的核心思想:选择最高概率的决策。
条件概率:
A桶:2个白球+2个黑球
B桶:1个白球+2个黑球
证明:P(白球|B桶)=P(白球 and B桶)/P(B桶)
P(白球 and 黑球|B桶) = 1/7 (用B桶中白色球个数除以2个桶中总的球数)
P(B桶) = 3/7 (B桶中有3个球,而球的总数是7)
P(白球|B桶) = P(白球 and B桶)/P(B桶)= 1/3
即 这是因为p(cx)=p(x|c)p(c)
使用条件概率来分类
p1(x, y) >p2(x, y) 那么属性类别1;
p2(x, y) >p1(x, y) 那么属性类别2;
注意:p(c1| x, y) 表示:给定某个由x , y表示的数据点,那么该点来自类别c1的概率是多少?
使用Python进行文本分类
将一个文本片段表示为一个词条向量,其中值为1表示词条出现在文档中,0表示词条为出现。
准备数据:从文本中构建向量
将文本看成单词向量或词条向量,也就是将句子转换成向量。
考虑出现在所有文档中的所有的单词,再决定将哪些词纳入词汇或者说索要的词汇集合,然后将每一篇文档转换为词汇表上的向量。于是
# -*- coding: utf-8 -*-
__author__ = 'Mouse'
from numpy import *
''' 创建一些实验样本,该函数返回的第一个变量是进行词条切分后文档集合 '''
''' 函数返回的第二个变量是一个类别标签的集合,这里有2类,侮辱和 正常言论。
这些文本是由人工标注 ,这些标志信息用于训练程序以便自动检测侮辱留言'''
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] #1 is abusive 侮辱的文字, 0 not 正常的言论
return postingList, classVec
''' 创建一个包含在所有文档中出现的不重复词的列表,为此使用Python的set数据类型,
将词条输给set构造函数,set就会返回一个不重复词表'''
def createVocabList(dataSet):
# 创建一个空集合,然后将每篇文档返回的新词集合添加到该集合中。
vocabSet = set([]) #create empty set
for document in dataSet:
#操作符|用于求两个集合的并集'''
vocabSet = vocabSet | set(document) #union of the two sets
return list(vocabSet)
''' 获得词汇表后,便可以使用函数setOfWord2Vec(),该函数的输入参数为词汇表及某个文档,输出的是文档向量,向量的每一个元素为1或0
分别表示词汇表中的单词再输入文档中是否出现 '''
def setOfWords2Vec(vocabList, inputSet):
returnVec = [0]*len(vocabList) # 创建一个和词汇表等长的向量,并将其置于0
print returnVec
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] = 1 #遍历文档中的所以的单词,如果出现了词汇表中的单词,则将输出的文档向量中对应值设为1
else: print "the word: %s is not in my Vocabulary!" % word
return returnVec
if __name__ == '__main__':
listOposts, listClasses = loadDataSet()
myVocabList = createVocabList(listOposts)
returnVec = setOfWords2Vec(myVocabList, listOposts[0])
print "myVocabList:", myVocabList
print "returnVec:", returnVec训练算法:从词向量计算概率
通过上述的公式,通过类别i(侮辱留言或非侮辱留言)中文档数除以总的文档数来计算概率p(ci);
然后计算p(w|ci) ,这里用到朴素贝叶斯假设,如果将w展开为一个独立特征,那么就可以将上述概率写作p(W0,W1,W2,...Wn|Ci),
由于这里的所有的词都是相互独立的,故也称作条件独立性假设,故可以用p(W0|c1)p(w1|c1)......p(Wn|Ci)来计算上述的概率。
# -*- coding: utf-8 -*-
from math import log
__author__ = 'Mouse'
from numpy import *
''' 创建一些实验样本,该函数返回的第一个变量是进行词条切分后文档集合 '''
''' 函数返回的第二个变量是一个类别标签的集合,这里有2类,侮辱和 正常言论。
这些文本是由人工标注 ,这些标志信息用于训练程序以便自动检测侮辱留言'''
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] # 1 is abusive 侮辱的文字, 0 not 正常的言论
return postingList, classVec
''' 创建一个包含在所有文档中出现的不重复词的列表,为此使用Python的set数据类型,
将词条输给set构造函数,set就会返回一个不重复词表'''
def createVocabList(dataSet):
# 创建一个空集合,然后将每篇文档返回的新词集合添加到该集合中。
vocabSet = set([]) # create empty set
for document in dataSet:
# 操作符|用于求两个集合的并集'''
vocabSet = vocabSet | set(document) # union of the two sets
return list(vocabSet)
''' 获得词汇表后,便可以使用函数setOfWord2Vec(),该函数的输入参数为词汇表及某个文档,输出的是文档向量,向量的每一个元素为1或0
分别表示词汇表中的单词再输入文档中是否出现 '''
def setOfWords2Vec(vocabList, inputSet):
returnVec = [0] * len(vocabList) # 创建一个和词汇表等长的向量,并将其置于0
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] = 1 # 遍历文档中的所以的单词,如果出现了词汇表中的单词,则将输出的文档向量中对应值设为1
else:
print "the word: %s is not in my Vocabulary!" % word
return returnVec
def trainNB0(trainMatrix, trainCategory):
numTrainDocs = len(trainMatrix) # 本次举例子采用的是6个
numWords = len(trainMatrix[0])
# 侮辱是数字1,正常言论是0,故 sum(trainCategory) 就是侮辱的言论
# 侮辱的文档除以总的文档p(ci)
pAbusive = sum(trainCategory) / float(numTrainDocs) # 3/6=0.5
p0Num = zeros(numWords)
p1Num = zeros(numWords) # change to ones()
p0Denom = 0.0
p1Denom = 0.0 # change to 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])
print "文章类别属于1(将所有词条组的标签属于1的,相加起来如词条组2+词条组4+词条组6):"
print p1Num
print "属于p1Denom(词条组2中有8个1+词条组4中有5个1+词条组6中有6个1=19): ", p1Denom
print "文章类别属于0 (将所有词条组的标签属于0的,相加起来如词条组1+词条组3+词条组5):"
print p0Num
print "属于p0Denom(词条组1中有7个1+词条组3中有8个1+词条组5中有9个1=24): ", p0Denom
p1Vect = p1Num / p1Denom # 将该词条的数目除以总词条数目得到的概率
p0Vect = p0Num / p0Denom #
return p0Vect, p1Vect, pAbusive # 返回每个类别的条件概率
if __name__ == '__main__':
listOposts, listClasses = loadDataSet()
print "最原始的文档:"
for i in range(len(listOposts)):
print listOposts[i]
print "原始文档下的6个词条组分别对应的标签(1表示侮辱的话,0表示正常言论):", listClasses
myVocabList = createVocabList(listOposts)
# 构建一个包含所有词列表
print "构建一个包含所有词列表:", "词汇个数为", len(myVocabList), ":"
print myVocabList
trainMat = []
for postinDoc in listOposts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
print "原始文档处理后的词条组:"
for i in range(len(trainMat)): # range(6) 表示,[0,1,2,3,4]
print trainMat[i]
p0V, p1V, pAb = trainNB0(trainMat, listClasses)
print "返回每个类别的条件概率:"
print "p0V:", p0V
print "p1V:", p1V
print "文档属于侮辱类的概率是:", pAb
运行结果为:
E:\Anaconda\python.exe E:/WorkSpace/py/algorithms/bays/bayes.py
最原始的文档:
['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']
原始文档下的6个词条组分别对应的标签(1表示侮辱的话,0表示正常言论): [0, 1, 0, 1, 0, 1]
构建一个包含所有词列表: 词汇个数为 32 :
['cute', 'love', 'help', 'garbage', 'quit', 'I', 'problems', 'is', 'park', 'stop', 'flea', 'dalmation', 'licks', 'food', 'not', 'him', 'buying', 'posting', 'has', 'worthless', 'ate', 'to', 'maybe', 'please', 'dog', 'how', 'stupid', 'so', 'take', 'mr', 'steak', 'my']
原始文档处理后的词条组:
[0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1]
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0]
[1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1]
[0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1]
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0]
文章类别属于1(将所有词条组的标签属于1的,相加起来如词条组2+词条组4+词条组6):
[ 0. 0. 0. 1. 1. 0. 0. 0. 1. 1. 0. 0. 0. 1. 1. 1. 1. 1.
0. 2. 0. 1. 1. 0. 2. 0. 3. 0. 1. 0. 0. 0.]
属于p1Denom(词条组2中有8个1+词条组4中有5个1+词条组6中有6个1=19): 19.0
文章类别属于0 (将所有词条组的标签属于0的,相加起来如词条组1+词条组3+词条组5):
[ 1. 1. 1. 0. 0. 1. 1. 1. 0. 1. 1. 1. 1. 0. 0. 2. 0. 0.
1. 0. 1. 1. 0. 1. 1. 1. 0. 1. 0. 1. 1. 3.]
属于p0Denom(词条组1中有7个1+词条组3中有8个1+词条组5中有9个1=24): 24.0
返回每个类别的条件概率:
p0V: [ 0.04166667 0.04166667 0.04166667 0. 0. 0.04166667
0.04166667 0.04166667 0. 0.04166667 0.04166667 0.04166667
0.04166667 0. 0. 0.08333333 0. 0.
0.04166667 0. 0.04166667 0.04166667 0. 0.04166667
0.04166667 0.04166667 0. 0.04166667 0. 0.04166667
0.04166667 0.125 ]
p1V: [ 0. 0. 0. 0.05263158 0.05263158 0. 0.
0. 0.05263158 0.05263158 0. 0. 0.
0.05263158 0.05263158 0.05263158 0.05263158 0.05263158 0.
0.10526316 0. 0.05263158 0.05263158 0. 0.10526316
0. 0.15789474 0. 0.05263158 0. 0. 0. ]
文档属于侮辱类的概率是: 0.5
Process finished with exit code 0
测试算法:根据现实情况修改分类器
利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,即p(W0|1)p(W1|1)p(W2|1)。
如果其中的一个概率为0,那么乘积也会变成0,为了降低这样影响,可以将所有词出现的次数初始化为1,并将分母初始化为2
p0Num = ones(numWords); p1Num = ones(numWords) #change to ones()
p0Denom = 2.0; p1Denom = 2.0 #change to 2.0另外一个问题就是,大部分因子都很小,所以程序会下溢出或者得不到正确的答案。 解决办法就是,对乘积数取自然对数 ln(a*b)=ln(a)+ln(b)p1Vect = log(p1Num/p1Denom) #change to log()
p0Vect = log(p0Num/p0Denom) #change to log()给出的f(x) 与ln(f(x)) 曲线,他们在相同的区间同时增加或减少,并且在相同的点上取得极值,他们的曲线虽然不同,当时不影响最终的结果。
构建完整的分类器
所有词条组中属于侮辱的概率p(1|w)=p(w|1)*p(1) / p(w)
= p(w0|1)*p(w1|1)*p(w2|1)...p(wn|1)*p(1) / p(w)
所有词条组中属于正常言论的概率p(0|w)=p(w|0)*p(0) / p(w)
= p(w0|0)*p(w1|0)*p(w2|0)...p(wn|0)*p(0) / p(w)
由于我们只需要比较 p(1|w) 和p(0|w) 的大小,故可以把 /p(w) 去掉,不会影响最终的结果
侮辱的概率 lnp(1|w) : ln [p(w0|1)*p(w1|1)*p(w2|1)...p(wn|1)*p(1)]=lnp[(w0|1)]+ln[p(w1|1)]+...+ln[p(wn|1)]+ln(p(1))
故分类器的代码如下:
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
#p(1)=
p1 = sum(vec2Classify * p1Vec) + log(pClass1) #element-wise mult ln(ab)=ln(a)+ln(b)
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))
p0V,p1V,pAb = trainNB0(array(trainMat),array(listClasses))
testEntry = ['love', 'my', 'dalmation']
thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
print "thisDoc:", thisDoc
print "p0V:", p0V
print "p1V:", p1V
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)准备数据:文档词袋模型
到目前为止,我们将每个词的出现与否作为一个特征,这可以被描述为词集(set-of-words model)
如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现在文档中所不能表达的某种信息,这种方法称为词袋模型。
在词袋中,每个单词可能出现多次,而在词集中,每个词只能出现一次。为了适应词袋模型,需要对函数setOfWords2Vec()稍加修改,修改后的函数称为bagOfWords2Vec().
示例:使用朴素贝叶斯过滤垃圾邮件
解决现实生活中的问题,需要先从文本内容得到字符串列表,然后生成向量。
准备数据:切分文本
def textParse(bigString): #input is big string, #output is word list
import re
listOfTokens = re.split(r'\W*', bigString)
return [tok.lower() for tok in listOfTokens if len(tok) > 2]
def spamTest():
docList=[]; classList = []; fullText =[]
for i in range(1,26):
wordList = textParse(open('email/spam/%d.txt' % i).read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(1)
wordList = textParse(open('email/ham/%d.txt' % i).read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
vocabList = createVocabList(docList)#create vocabulary
trainingSet = range(50); testSet=[] #create test set
for i in range(10):
randIndex = int(random.uniform(0,len(trainingSet)))
testSet.append(trainingSet[randIndex])
del(trainingSet[randIndex])
trainMat=[]; trainClasses = []
for docIndex in trainingSet:#train the classifier (get probs) trainNB0
trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))
trainClasses.append(classList[docIndex])
p0V,p1V,pSpam = trainNB0(array(trainMat),array(trainClasses))
errorCount = 0
for docIndex in testSet: #classify the remaining items
wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])
if classifyNB(array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
errorCount += 1
print "classification error",docList[docIndex]
print 'the error rate is: ',float(errorCount)/len(testSet)

本文介绍了朴素贝叶斯分类器的基础知识,包括贝叶斯决策理论、条件概率和分类原理。通过Python实现文本分类,讲解了如何构建分类器并处理数据,如文档词袋模型的构建。最后,以垃圾邮件过滤为例,展示了朴素贝叶斯在实际问题中的应用。
&spm=1001.2101.3001.5002&articleId=53728992&d=1&t=3&u=37556b7fcef24b408421396a56b41f62)
397

被折叠的 条评论
为什么被折叠?



