您的位置:首页 > 其它

基于概率论的分类方法:朴素贝叶斯

2016-02-27 14:29 477 查看
在这里我们将完成两个过程:1.我们将充分利用Python的文本处理能力将文档切分为词向量,然后利用词向量对文档进行分类。2.我们将构建另一个分类器,观察其在真实的垃圾邮件数据集中的过滤效果。

1.概率知识:

1.1贝叶斯决策理论

朴素贝叶斯是贝叶斯决策理论的一部分,所以讲述朴素贝叶斯之前有必要快速了解一下贝叶斯决策理论。

假设我们有一个数据集,它由两类数据组成,数据分布如图1-1所示:



我们现在用p1(x,y)表示数据点(x,y)属于类别1(图中用圆点表示的类别)的概率,用p2(x,y)属于类别2(图中用三角形表示的类别)的概率,那么对于一个新数据点(x,y),可以用下面的规则来进行判断类别:

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

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

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

1.2 条件概率

假设现在有一个装了7块石头的盒子,其中3块是灰色的,4块是黑色的,所以从盒子中随机取出一块石头,是灰色的概率为3/7,是黑色的概率是4/7。我们使用p(grey)来表示取到灰色石头的概率。

如果这7块石头放在A、B两个盒子里(A中有2个灰色,2个黑色;B中有1个灰色,2个黑色),那么上述概率该怎么求?

有两种方法求条件概率:1、假定计算的是从B盒取到灰色石头的概率,记作p(gray|bucketB)。条件概率的计算公式如下所示:p(gray|bucketB)=p(gray and bucketB)/p(bucketB).首先,用B盒中灰色的个数除以两个盒子的总数,得到p(gray and bucketB)=1/7,其次,由于B盒中有3块石头,而总数为7,于是p(bucketB)=3/7.不难得到,p(gray|bucketB)的值为1/3,p(gray|bucketA)的值为2/4;

2、另一种计算条件概率的方法为贝叶斯准则,通过交换条件概率中的条件与结果,即如果已知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)} .讨论完条件概率,接下来的问题是如何将其应用到分类器中。

1.3 使用条件概率来分类

具体应用贝叶斯准则得到:p(ci|x,y)=p(x,y|ci)p(c)p(x,y)p(c_i|x,y)=\frac{p(x,y|c_i)p(c)}{p(x,y)}

使用这些定义,可以定义贝叶斯分类准则为:

如果p(c1|x,y)>p(c2|x,y)p(c_1|x,y)>p(c_2|x,y),那么属于类别c1c_1

如果p(c2|x,y)>p(c2|x,y)p(c_2|x,y)>p(c_2|x,y),那么属于类别c2c_2

2.使用Python进行文本分类

要从文本中获取特征,需要先拆分文本。如何做尼?这里的特征是来自文本的词条(token),一个词条是字符的任意组合。可以把词条想象为单词,也可以是非单词词条,如URL、IP地址或者任意其他字符串。然后将每一个文本片段表示为一个词条向量,其中值为1表示词条出现在文档中,0表示词条未出现。

接下来首先介绍将文本转换为数字向量的过程,然后介绍如何基于这些向量来计算条件概率,并在此基础上构建分类器。最后还要介绍一些利用Python实现朴素贝叶斯过程中需要考虑的问题。

2.1 准备数据:从文本中构建词向量

考虑出现在所有文档中的所有单词,再决定将哪些词纳入词汇表,然后必须要将每一篇文章转换为词汇表上的向量。

程序清单2-1:词表到向量的转换函数

def createVocabList(dataSet):
vocabSet = set([])
for document in dataSet:
vocabSet = vocab Set | set(document)
return list(vocabSet)


creatVocabList()会创建一个包含在所有文档中出现的不重复词的列表。将词条列表输给set构造函数,set就会返回一个不重复的词表。首先,创建一个空集合,然后将每篇文档返回的新词集合添加到该集合中。操作符|用于求两个集合的并集。

def setOfWords2Vec(vocabList,inputSet)
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


获得词汇表后,便可以使用函数setOfWords2Vec(),该函数的输入参数为词汇表以及某个文档,输出的是文档向量,向量的每一元素为1或0,分别表示词汇表中的单词再输入文档中是否出现。函数首先创建一个和词汇表等长的向量,并将其元素设置为0,接着,遍历文档中的所有单词,如果出现了词汇表中的单词,则将输出的文档向量中的对应值设为1。

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

程序清单2-2: 朴素贝叶斯分类器训练函数

def trainNB0(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
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   #每个单词出现的次数除以该类别中的总单词数
p0Vect = p0Num/p0Denom


输入参数为所有文档的词向量trainMatrix,以及由每篇文档类别标签所构成的向量trainCategory。

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

利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,即计算p(w0|1)p(w1|1)p(w2|1)p(w_0|1)p(w_1|1)p(w_2|1)。如果其中一个概率值为0,那么最后乘积也为0。为避免这种影响,可以将所有词的出现次数初始化为1,并将分母初始化为2。将trainNB0()的第4行和第5行修改为:

p0Num = ones(numWords); p1Num = ones(numWords)
p0Denom = 2.0; p1Denom = 2.0


另一个问题是下溢出。当计算乘积p(w0|ci)p(w1|ci)p(w2|ci)...p(w_0|c_i)p(w_1|c_i)p(w_2|c_i)...时,由于大部分因子都很小,所以程序会下溢出或者得到不正确的答案,于是可以通过求对数避免下溢出或者浮点数舍入导致的错误。通过修改return前的两行代码:

p1Vect = log(p1Num/p1Denom)
p0Vect = log(p0Num/p0Denom)


程序清单2-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


有4个输入:要分类的向量vec2Classify以及使用函数trainNB0()计算得到的三个概率。

2.4 示例:使用朴素贝叶斯过滤垃圾邮件

2.4.1 准备数据:切分文本

程序清单2-4:

def textParse(bigString):
import re
listOfTokens = re.split(r'\W*',bigString)
return [tok.lower() for tok in listOfTokens if len(tok)>2]


返回长度大于2并转换成小写的字符串。

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

程序清单2-5:

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 = creatVocabList(docList)
trainingSet = range(50);testSet = []
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:
trainMat.append(setOfWords2Vec(vocabList,docList[docIndex]))
trainClasses.append(classList[docIndex])
p0V,p1V,pSpam = trainNB0(array(trainMat),array(trainClasses))
errorCount = 0
for docIndex in testSet:
wordVector = setOfWords2Vec(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)


导入文件夹spam和ham下的文本文件,接下来构建一个测试集与一个训练集,本例中共有50封电子邮件,其中10封被随机选择为测试集。变量trainingSet是一个整数列表,其中的值从0到49。接下来,随机选择其中10个文件。选择出的数字所对应的文档被添加到测试集,同时也将其从训练集中剔除。这种随机选择数据的一部分作为测试集,而剩余部分作为训练集的过程称为留存交叉验证。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: