机器学习-朴素贝叶斯分类器

来源:互联网 发布:怎么执行mysql数据库 编辑:程序博客网 时间:2024/05/01 02:54

自学中,如有不足,请指出,谢谢~

素贝叶斯分类是一种十分简单的分类算法,朴素贝叶斯的思想基础是这样的:对于给出的待分类项,

求解在此项出现的条件下各个类别出现的概率,哪个最大,就认为此待分类项属于哪个类别。比如说现在有橘子,苹果,香蕉三类,

给出的特征有{形状,颜色},如果给出{圆形,红色}这个样本,那么它是P(苹果)的概率就是最大的,因此猜测它是苹果。

贝叶斯定理:


朴素贝叶斯有两个假设:

1、特征直接相互独立;

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是侮辱性的,0正常    return postingList,classVec#创建包含所有不重复的词的list(用set)def createVocabList(dataSet):    vocabSet = set([])  #创建空集合    for document in dataSet:        vocabSet = vocabSet | set(document) #集合∪    return list(vocabSet)#输入词汇表和某个文档,输出文档向量#作用:将某个文档转换成和词汇表等长的0,1向量,文档中的词出现在词汇表中,对应的位置为1def setOfWords2Vec(vocabList, inputSet):    returnVec = [0]*len(vocabList)#创建与词汇表等长的向量    for word in inputSet:         if word in vocabList: #Word在inputSet中,对应的元素置1            returnVec[vocabList.index(word)] = 1        else:            print "the word: %s is not in my Vocabulary!" % word    return returnVec
经过上述代码处理,输入的文档就会转换成0、1向量;

下面要计算这些数字的概率,通过贝叶斯概率知识,可以得到:


w是一个向量,即setOfWords2Vec函数产生的returnVec向量;Ci表示的就是类别,此例中为侮辱性留言和正常留言。

在朴素贝叶斯条件下,w向量的每个元素独立,则


因为分母P(w)是个常值,所以,需要最大化分子,即


实现代码如下:

#trainMatrix:postingList[i]中词汇在词汇表中是否出现的0,1向量所构成的矩阵def trainNB0(trainMatrix,trainCategory):    numTrainDocs = len(trainMatrix) #postingList[i]中i的值(样本个数)    numWords = len(trainMatrix[0]) #词汇表的长度    pAbusive = sum(trainCategory)/float(numTrainDocs) #是侮辱类的概率P(1)    #计算p(w0|1)p(w1|1)p(w2|1)多个概率乘积,因为太多太小的数相乘可能会下溢,    #所以取自然对数,转化成加法,避免错误    p0Num = ones(numWords); p1Num = ones(numWords)          p0Denom = 2.0; p1Denom = 2.0                            for i in range(numTrainDocs):        if trainCategory[i] == 1: #如果第i个样本是侮辱性的            p1Num += trainMatrix[i] #            p1Denom += sum(trainMatrix[i])        else: #如果第i个样本是正常的            p0Num += trainMatrix[i]            p0Denom += sum(trainMatrix[i])    p1Vect = log(p1Num/p1Denom)          #change to log()    p0Vect = log(p0Num/p0Denom)          #change to log()    return p0Vect,p1Vect,pAbusive#判断分类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 = 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))    #classifyNB进行分类    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)  
运行实际效果如下:

>>> import bayes>>> bayes.testingNB()['love', 'my', 'dalmation'] classified as:  0['stupid', 'garbage'] classified as:  1

以上的处理,是将每个词出现与否作为一个特征,setOfWords2Vec()这个函数实现了这个功能,这被称为是词集模式;

它不能统计每个词出现的次数,因此可能会损失一定的准确率,因此使用词袋模型来构建垃圾邮件分类器,bagOfWords2VecMN()实现该功能

#文档词袋模型,每个单词可以出现多次def bagOfWords2VecMN(vocabList, inputSet):    returnVec = [0]*len(vocabList)    for word in inputSet:        if word in vocabList:            returnVec[vocabList.index(word)] += 1    return returnVec
要过滤垃圾邮件,首先要获取邮件文本,将文本转成词汇,这要用到split()方法,有的单词会出现大写字母,这会造成匹配的困难,因此需要统一成小写或者大写形式,

需要使用lower()或者upper()方法

def textParse(bigString):        import re     listOfTokens = re.split(r'\W*', bigString) #用正则表达式切分(具体用法见另一博文)    return [tok.lower() for tok in listOfTokens if len(tok) > 2] # 返回单词长度大于2的小写形式
然后用spamTest()函数对贝叶斯垃圾分类器自动化处理。产生一个测试集和一个训练集,一共有50封邮件,随机选择10封邮件作为测试集,另外的40封邮件作为训练集,这个就是留存交叉验证,为了得到更高的准确率,可以进行多次迭代,然后求平均值;

#垃圾自动分类器   def spamTest():    docList=[]; classList = []; fullText =[]    for i in range(1,26):        wordList = textParse(open('email/spam/%d.txt' % i).read())        docList.append(wordList) #每个spam中的单词作为一个子列表添加到docList        fullText.extend(wordList)#每个spam中的单词添加到docList        classList.append(1) #classList里面加个1(类标签)        wordList = textParse(open('email/ham/%d.txt' % i).read())        docList.append(wordList)        fullText.extend(wordList)        classList.append(0)    vocabList = createVocabList(docList)#创建包含所有不重复的词的vocabList    trainingSet = range(50); testSet=[]           #创建测试集    for i in range(10): #随机选出10封邮件作为测试集        randIndex = int(random.uniform(0,len(trainingSet)))        testSet.append(trainingSet[randIndex])        del(trainingSet[randIndex]) #从训练集中删除已经选择的项目     trainMat=[]; trainClasses = []     for docIndex in trainingSet:#训练分类器        trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))        trainClasses.append(classList[docIndex])    p0V,p1V,pSpam = trainNB0(array(trainMat),array(trainClasses))    errorCount = 0    for docIndex in testSet:        #测试错误率        wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])        if classifyNB(array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:#classifyNB产生的类标签和真实的类标签不同            errorCount += 1            print "classification error",docList[docIndex]    print 'the error rate is: ',float(errorCount)/len(testSet) #错误率
以上的代码只是进行一次迭代,为了更加准确的分类,我将上述程序迭代了10次,然后求得平均错误率,代码基本是一样的,只是加入了几条语句,

我测试了3次,三次的结果如下:

>>> import bayes>>> bayes.spamTest()the error rate is:  0.04>>> ================================ RESTART ================================>>> >>> import bayes>>> bayes.spamTest()the error rate is:  0.08>>> ================================ RESTART ================================>>> >>> import bayes>>> bayes.spamTest()the error rate is:  0.03>>> 















0 0