基于概率论的分类方法:朴素贝叶斯
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:词表到向量的转换函数
creatVocabList()会创建一个包含在所有文档中出现的不重复词的列表。将词条列表输给set构造函数,set就会返回一个不重复的词表。首先,创建一个空集合,然后将每篇文档返回的新词集合添加到该集合中。操作符|用于求两个集合的并集。
获得词汇表后,便可以使用函数setOfWords2Vec(),该函数的输入参数为词汇表以及某个文档,输出的是文档向量,向量的每一元素为1或0,分别表示词汇表中的单词再输入文档中是否出现。函数首先创建一个和词汇表等长的向量,并将其元素设置为0,接着,遍历文档中的所有单词,如果出现了词汇表中的单词,则将输出的文档向量中的对应值设为1。
2.2 训练算法:从词向量计算概率
程序清单2-2: 朴素贝叶斯分类器训练函数
输入参数为所有文档的词向量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行修改为:
另一个问题是下溢出。当计算乘积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前的两行代码:
程序清单2-3:朴素贝叶斯分类函数
有4个输入:要分类的向量vec2Classify以及使用函数trainNB0()计算得到的三个概率。
2.4 示例:使用朴素贝叶斯过滤垃圾邮件
2.4.1 准备数据:切分文本
程序清单2-4:
返回长度大于2并转换成小写的字符串。
2.4.2 测试算法:使用朴素贝叶斯进行交叉验证
程序清单2-5:
导入文件夹spam和ham下的文本文件,接下来构建一个测试集与一个训练集,本例中共有50封电子邮件,其中10封被随机选择为测试集。变量trainingSet是一个整数列表,其中的值从0到49。接下来,随机选择其中10个文件。选择出的数字所对应的文档被添加到测试集,同时也将其从训练集中剔除。这种随机选择数据的一部分作为测试集,而剩余部分作为训练集的过程称为留存交叉验证。
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个文件。选择出的数字所对应的文档被添加到测试集,同时也将其从训练集中剔除。这种随机选择数据的一部分作为测试集,而剩余部分作为训练集的过程称为留存交叉验证。
相关文章推荐
- 浅解java 中thread.join()的用法
- 1、串(字符串)以及串的模式匹配算法
- 卫星地图
- [ZJOI2008] [BZOJ1036] 树的统计Count
- 2015年年终总结学习篇:为了梦想不服输,再苦也不能停止脚步
- C语言(按键获取与函数)
- asp.net 链接数据库时 尝试加载 Oracle 客户端库时引发 BadImageFormatException。
- 1000桶毒酒问题
- MySQL 获得当前日期时间 函数
- ReactJS
- javascript类的类比详解-大白话版
- uva11078【RMQ】
- 高斯消元模板
- keil烧写错误整理
- 使用OPENCV2.4.9中CvvImage没有的问题
- FTP
- java内存溢出和内存泄露
- FTP
- 优先级队列:PriorityQueue
- 数据结构算法之排序系列Java、C源码实现(7)--归并排序