机器学习算法与Python实践之(八)朴素贝叶斯



模型概述

朴素贝叶斯方法,是指
朴素:特征条件独立
贝叶斯:基于贝叶斯定理

根据贝叶斯定理,对一个分类问题,给定样本特征x,样本属于类别y的概率是

p(y|x)=p(x|y)p(y)p(x) 1 

在这里,x是一个特征向量,将设x维度为M。因为朴素的假设,即特征条件独立,根据全概率公式展开,公式(1)可以表达为
p(y=c k |x)= M i=1 p(x i |y=c k )p(y=c k ) k p(y=c k ) M i=1 P(x i |y=c k ) 2 

这里,只要分别估计出,特征 x i   在每一类的条件概率就可以了。类别y的先验概率可以通过训练集算出,同样通过训练集上的统计,可以得出对应每一类上的,条件独立的特征对应的条件概率向量。
如何统计,就是下一部分——学习——所关心的内容。

学习(参数估计)

下面介绍如何从数据中,学习得到朴素贝叶斯分类模型。概述分类方法,并提出一个值得注意的问题。

学习

训练集TrainingSet= {(x 1 ,y 1 ),(x 2 ,y 2 ),...,(x N ,y N )}  包含N条训练数据,其中 x i =(x (1) i ,x (2) i ,...,x (M) i ) T   是M维向量, y i {c 1 ,c 2 ,...c K }  属于K类中的一类。

学习 1.首先,我们来计算公式(2)中的 p(y=c k ) 

p(y=c k )= N i=1 Iy i =c k N 3 

其中 I(x)  为指示函数,若括号内成立,则计1,否则为0。

学习 2.接下来计算分子中的条件概率,设 M  维特征的第 j  维有 L  个取值,则某维特征的某个取值 a jl   ,在给定某分类 c k   下的条件概率为:

p(x j =a jl |y=c k )= N i=1 I(x j i =a jl ,y i =c k ) N i=1 I(y i =c k ) 4 

经过上述步骤,我们就得到了模型的基本概率,也就完成了学习的任务。

分类

通过学到的概率,给定未分类新实例 X  ,就可以通过上述概率进行计算,得到该实例属于各类的后验概率 p(y=c k |X)  ,因为对所有的类来说,公式(2)中分母的值都相同,所以只计算分子部分即可,具体步骤如下:
分类 1.计算该实例属于 y=c k   类的概率
p(y=c k |X)=p(y=c k ) j=1 n p(X (j) =x (j) |y=c k )5 

分类 2.确定该实例所属的分类 y 
y=argmax c k  p(y=c k |X)6 

于是我们得到了新实例的分类结果

拉普拉斯平滑

到这里好像方法已经介绍完了,实则有一个小问题需要注意,在公式(3)(4)中,如果从样本中算出的概率值为0该怎么办呢?
下面介绍一种简单方法,给学习步骤中的两个概率计算公式,分子和分母都分别加上一个常数,就可以避免这个问题。更新过后的公式如下:
p(y=c k )= N i=1 Iy i =c k +λN+Kλ 7 

K  是类的个数
p(x j =a jl |y=c k )= N i=1 I(x j i =a jl ,y i =c k )+λ N i=1 I(y i =c k )+L j λ 8 

L j   是第 j  维特征的最大取值

可以证明,改进以后的(7)(8)仍然是概率。平滑因子 λ=0  即为(3)(4)实现的最大似然估计,这时会出现在本节开始时提到的0概率问题;而 λ=1  则避免了0概率问题,这种方法被称为拉普拉斯平滑

实现:朴素贝叶斯下的文本分类

根据上面的算法流程,在这里实现一个句子极性划分的例子。所谓句子极性是指,句子所表达的情感色彩,例如积极/消极,这里(书里)使用的是侮辱性/非侮辱性。其实是什么类别不重要,只要给定有标签的训练数据,就可以得到分类模型。
下面简述实现思想和流程,给出代码。

算法思想和流程

给定的训练集是标定了 侮辱性/非侮辱性 的句子(因为是英语句子,所以基本视分词为已经解决的问题,如果是汉语,则要先进行分词),我们认为特征就是句子中的单个词语。单个词语有极性表征,整个句子所包含的单词的极性表征就是句子的极性。
由以上的基础,应用朴素贝叶斯分类,就变成了这样的问题
初始化步,构建可以表征句子的特征向量(词汇表)。并根据这个特征向量,把训练集表征出来。从训练集中分离部分数据作为测试集。
学习步,计算 类的先验概率特征向量对应每一类的条件概率向量

分类步, 计算测试集中待分类句子在每一类的分类后验概率,取最大值作为其分类,并与给定标签比较,得到误分类率。


Python实现:

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']]
     classVec = [0,1,0,1,0,1] # 1代表侮辱性文字,0代表正常言论
     return postingList,classVec


''' 创建一个空集 '''
def createVocabList(dataSet):
    vocabSet = set([])
    for document in dataSet:
        vocabSet = vocabSet | set(document)  #创建两个集合的并集,创建不重复的单词向量
    return list(vocabSet)


''' 创建一个其中所含元素都为0的向量 '''
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


''' trainMatrix=[0,1,0,0,0,....,0,1,0,1],trainCategory=[0,1,0,1,0,1] '''
''' 朴素贝叶斯分类器训练函数 '''
def trainNB0(trainMatrix,trainCategory):
    numTrainDocs = len(trainMatrix)
    numWords = len(trainMatrix[0])
    pAbusive = sum(trainCategory)/float(numTrainDocs)
    p0Num = ones(numWords); p1Num = ones(numWords)      #change to ones() 
    p0Denom = 2.0; p1Denom = 2.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])
    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))
    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)



def bagOfWords2Vec(vocabList,inputSet):
    returnVec = [0] * len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1
    return returnVec


''' 文件解析及完整的垃圾邮件测试函数 '''
def textParse(bigString):
     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)
     ''' 定义训练样本和测试样本,以便进行交叉验证 '''
     trainingSet = range(50); testSet = []
     ''' 随机选取10个电子邮件作为测试集 '''
     for i in range(10):
          ''' 在0~50之间随机选取一个数 '''
          randIndex = int(random.uniform(0,len(trainingSet)))
          ''' 将选取的值放在测试集testSet中 '''
          testSet.append(trainingSet[randIndex])
          ''' 在训练集合中删除作为测试集的部分 '''
          del(trainingSet[randIndex])
     trainMat = []; trainClasses = []
     for docIndex in trainingSet:
          ''' 输入参数为词汇表及其某个文档,输出的是文档向量,向量的每一个元素是1或0, 分别表示词汇表中的单词在输入文档中是否出现'''
          trainMat.append(setOfWords2Vec(vocabList, docList[docIndex]))
          trainClasses.append(classList[docIndex])
     ''' trainMat是文档矩阵,trainClasses是每篇文章类别标签所构成的向量 '''
     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 'the error rate is: ',float(errorCount)/len(testSet)

你可能感兴趣的:(python,机器学习,朴素贝叶斯)