机器学习实战笔记--朴素贝叶斯(实例代码)

#coding:utf-8
from numpy import *
#加载文档词向量数据以及相应文档类别,0表示正常言论,1表示侮辱性文字
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]
    return postingList,classVec


def createVocabList(dataSet):
    '''
功能:将已知类别文档中出现的词汇 存入到 所有词汇集合,相当于字典 返回其list类型

输入数据类型:列表类型(列表中存储的是列表元素,每篇文档的词汇集合)
输出数据类型:列表类型(字典)
'''
    vocabSet = set([])
    for document in dataSet:
        vocabSet = vocabSet | set(document)
    return list(vocabSet)


def setOfWords2Vec(vocabList,inputSet):
    '''
功能:将输入的文档转成词向量形式,即在字典中所有词汇出现的词频数,出现为1,未出现为0.
输入数据类型:列表类型(字典,存储所有词汇)
              字符串(将要预测的留言)
输出数据类型:列表类型(词向量)

'''
    returnVec =[0]*len(vocabList)
    for word in inputSet:
        #在此函数中有个小疑问:当词向量在字典中出现,则将该文档的词向量对应词索引处置为1,这里假设每条留言中出现的词汇不重复。但如果该词出现多次呢?不是该相加么。。
        if word in vocabList:
            returnVec[vocabList.index(word)] = 1
        else:
            print "the word : %s is not in my Vocabulary!" % word
    return returnVec

def trainNB0(trainMatrix, trainCategory):
    '''
功能:计算属于1类对应下的所有词汇出现的词频数(词向量形式),用p1Num存放。0类一样。
      计算属于1类的所有词汇总数,用p1Denom存放。0类一样。
      返回字典词汇的条件概率p0Vect,p1Vect以及文档属于1类的概率pAbusive。p0Vect= [p(w1|c1),p(w2|c1)...p(wn|c1)]
      
输入的数据类型:array列表类型(样本词向量)
                array列表类型(样本对应的类别)
输出的数据类型:列表类型(字典词汇的条件概率0)
                列表类型(字典词汇的条件概率1)
                浮点数(文档属于1类的概率)
'''
    numTrainDocs = len(trainMatrix)
    numWords = len(trainMatrix[0])
    pAbusive = sum(trainCategory)/float(numTrainDocs)
    p0Num = ones(numWords)
    p1Num = ones(numWords)
    p0Denom = 2.0
    p1Denom = 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 = p1Num/p1Denom
    p0Vect = p0Num/p0Denom
    return p0Vect,p1Vect,pAbusive

def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    '''
功能:计算贝叶斯概率公式,忽略分母(因为每个类别的分母相等)。计算技巧是分子变形,使相乘的n个数转换为log每个数相加
      返回留言板的预测类别。
      
输入数据类型:列表(词向量形式)
              列表(字典词汇的条件概率0)
              列表(字典词汇的条件概率1)
              浮点数(文档属于1类的概率)
输出数据类型:整数(预测类别)
'''
    p1 = sum(vec2Classify * p1Vec) + log(pClass1)
    p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
    if p1>p0:
        return 1
    else:
        return 0

def tesingNB():
    '''
功能:调用以上定义的功能函数,训练分类器然后测试两条数据
'''
    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)
    #以下这个例子可以作为感兴趣的点。贝叶斯中的0类,1类正负抵消。并不是出现侮辱性词就判定为侮辱类

    testEntry = ['love','my','dalmation','stupid']
    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
    print testEntry,'Classified as :',classifyNB(thisDoc,p0V,p1V,pAb)
朴素贝叶斯实例

   朴素贝叶斯算法之所以称为朴素是因为有个前提假设条件:对于给定样本各属性相互条件独立。也就是说给定一句话 d1=“苹果很好吃”,我们来判定这句话是食物类还是器材类,那如果用朴素贝叶斯算法,就假设了这句话里的每个词(特征)w1=“苹果”,w2=“很”,w3=”好吃”出现的概率是相互独立的,没有依赖的。但我们知道实际上这条假设并不能真正反映现实的规律,因为“苹果”后面往往有很大概率会出现“好吃”,也就是 p(苹果,好吃) = p(苹果)* p(好吃 | 苹果),但我们现在就假设 p(苹果,好吃) = p(苹果)* p(好吃) 。

  朴素贝叶斯分类的目标就是实际上已知训练数据,求出它属于哪个类的概率值最大就预测为哪个类。就拿上面的例子所说,食物类是C1,器材类是C2,P(C1|d1)>P(C2|d1) 则把d1判定为C1类,反之。利用贝叶斯公式可知 P(C1|d1) = P(d1|C1)P(C1)/P(d1),目标是比较P(C1|d1)和P(C2|d1)大小,分母是相同的可以忽略。近似比较P(d1|C1)P(C1)和P(d1|C2)P(C2),两个类别的先验概率P(C1)和P(C2)可以根据训练数据集求出,由于以上的假设得 P(d1|C1)=P(w1|C1)P(w2|C1)P(w3|C1),而已知类别下的各个特征条件概率也可以根据训练数据集统计出来,比如P(w1|C1)具体计算就是在C1类别下w1出现的次数/C1类别下出现所有词的总次数。这样就可以通过每项的计算来进行近似比较了。那如果w1在某类下出现的次数为0呢?岂不是条件概率P(d1|C1)就为0了,别急,下面会介绍拉普拉斯估计。下面给出形式化的数学表述:

  (据听说数学是世界上最好的语言之一)

       设训练样本集分为k类,记为 C= {C1,C2 ,…, Ck},则每个类的先验概率为P(Ci),i=1,2,...,k, 其值为Ci类的样本数除以训练集总样本数n。对于新样本d,其属于Ci类的条件概率是P(d|Ci)。根据贝叶斯定理,Ci类的后验概率为:

  P(d)对于所有类均为同一常数,可以忽略。为避免P(Ci)等于0,采用拉普拉斯概率估计:

  其中|C|为训练集中类的数目,|Dci|为训练集中属于类Ci的文档数,|Dc|为训练集包含的总文档数。

  对于待分类文本文档d,本文采用向量空间模型,其基本思想是将每一个文本表示为一个向量d= (w1,...,wm),m是d的特征词个数。 贝叶斯算法假设各特征词相互独立。

  朴素贝叶斯分类器将未知样本归于哪类的依据,如下:

   

 

posted @ 2017-02-21 15:52  白白毛狗  阅读(811)  评论(0编辑  收藏  举报