您的位置:首页 > 编程语言

机器学习实战笔记(3.2)-朴素贝叶斯算法(贝努力模型代码实现)

2017-10-20 16:49 525 查看
机器学习实战笔记(3)-朴素贝叶斯算法(代码实现)

 

书中第一个例子给的样本如下: 6句话,三句脏话,三句好话。

 

[['my', 'dog', 'has', 'flea', 'problems', 'help','please'] -- Good words

 

['maybe', 'not', 'take', 'him', 'to', 'dog', 'park','stupid'] -- Shit words

 

['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love','him'] -- Good words

 

['stop', 'posting', 'stupid', 'worthless', 'garbage'] --Shit words

 

['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop','him'] -- Good words

 

['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]-- Shit words

 

目前采用的算法是贝努力类型的朴素贝叶斯,就是对于文本分析,我们只考虑词是否出现,而不考虑出现的次数。

也就是所有的词都是一个权重!模型虽然简单,但是效果还是不错地。

 

首先是所有出现单词的列表:这里是只看出现不出现,不看频次。

 

['cute', 'love','help', 'garbage', 'quit', 'I', 'problems', 'is', 'park', 'stop', 'flea','dalmation', 'licks', 'food', 'not', 'him', 'buying', 'posting', 'has','worthless', 'ate', 'to', 'maybe', 'please', 'dog', 'how', 'stupid',
'so','take', 'mr', 'steak', 'my']

>>> 

 

然后根据每个句子的单词,生成对应句子的向量,就是单词是否出现。

句子矩阵:

 

[0, 0, 1, 0, 0, 0,1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1] --Good words

[0, 0, 0, 0, 0, 0,0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0] -- Shitwords

[1, 1, 0, 0, 0, 1,0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1] --Good words

[0, 0, 0, 1, 0, 0,0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] -- Shitwords

[0, 0, 0, 0, 0, 0,0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1] --Good words

[0, 0, 0, 0, 1, 0,0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0] -- Shitwords

 

P(Shitwords) = 3/6 = 50%

P(Goodwords) = 3/6 = 50%

如果我有句话My dog likes eating, 怎么算是脏话还是好话?

先过滤,不在样本里面的词被抛弃。

>>>bayes.setOfWords2Vec(myVocabList,['my','dog','likes','eating']

... )

the word: likes is not in my Vocabulary!

the word: eating is not in my Vocabulary!

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1]

>>> 

我们只需要计算:

p("my dog likes eating"/goodwords) =p(my/goodwords)*p(dog/goodwords)*p(my/goodwords)的结果。

下面就是用循环来计算的过程了:

 

我们对下面的矩阵做分析计算:

[0, 0, 1, 0, 0, 0,1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1] --Good words

[0, 0, 0, 0, 0, 0,0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0] -- Shitwords

[1, 1, 0, 0, 0, 1,0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1] --Good words

[0, 0, 0, 1, 0, 0,0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] -- Shitwords

[0, 0, 0, 0, 0, 0,0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1] --Good words

[0, 0, 0, 0, 1, 0,0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0] -- Shitwords

 

 

   listOPosts, listClasses = loadDataSet()  :
数据载入

    trainCategory = [0, 1, 0, 1, 0,1] :样本的分类

    myVocabList =createVocabList(listOPosts)

    trainMatrix = []

    for postinDoc in listOPosts:

           trainMatrix.append(setOfWords2Vec(myVocabList,postinDoc)) :生成前面所述的所有句子和单词出现否的向量表。

 

[0, 0, 1, 0, 0, 0,1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1] --Good words

[0, 0, 0, 0, 0, 0,0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0] -- Shitwords

[1, 1, 0, 0, 0, 1,0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1] --Good words

[0, 0, 0, 1, 0, 0,0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] -- Shitwords

[0, 0, 0, 0, 0, 0,0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1] --Good words

[0, 0, 0, 0, 1, 0,0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0] -- Shitwords

 

    numTrainDocs = len(trainMatrix)

    print"numTrainDocs=",numTrainDocs

    numWords = len(trainMatrix[0]) : 总的单词数,不考虑重复,只看是否出现。

    print "words = ",numWords

    pAbusive =sum(trainCategory)/float(numTrainDocs) :垃圾话的比例,其实就是数组求和,类型1都是垃圾话!

    print "pAbusive =",pAbusive

#    p0Num = ones(numWords); p1Num =ones(numWords)       

p0Num = zeros(numWords); p1Num = zeros(numWords)       :向量置零!

p1Num  [ 0. 0.  0.  0. 0.  0.  0. 0.  0.  0. 0.  0.  0. 0.  0.  0. 0.  0.

 0.  0.  0. 0.  0.  0. 0.  0.  0. 0.  0.  0. 0.  0.]

    print "p0Num ", p0Num

    print "p1Num ", p1Num

    p0Denom = 0.0; p1Denom = 0.0                        #对于分类0和分类1,算该类总数的词量,先设置为0(注意这个地方后面会有变数)

 

    for i in range(numTrainDocs):  这里巧妙的用了向量的加法,统计了本类中某个词累计出现的次数,很巧妙的用了数组加法!

        if trainCategory[i] == 1:

            p1Num += trainMatrix[i]

            p1Denom +=sum(trainMatrix[i]): 算本类单词的总数

           print "p1Num =", p1Num,"p1Denom =", p1Denom

        else:

            p0Num += trainMatrix[i]

            p0Denom +=sum(trainMatrix[i])

           print "p0Num =", p0Num,"p0Denom =", p0Denom

 

遍历后,我们看看什么结果:

 

p0Num = [ 1.  1. 1.  0.  0.  1.  1. 1.  0.  1. 1.  1.  1. 0.  0.  2. 0.  0.

1.     0. 1.  1.  0. 1.  1.  1. 0.  1.  0. 1.  1.  3.]  本类每个单词出现的次数。

p0Denom = 24.0: 本类总共的单词出现数量。

p1Num = [ 0.  0.  0. 1.  1.  0. 0.  0.  1. 1.  0.  0. 0.  1.  1. 1.  1.  1.

  0. 2.  0.  1. 1.  0.  2. 0.  3.  0. 1.  0.  0.  0.]p1Denom = 19.0

算分类的概率吧:

 

    p1Vect = p1Num/p1Denom          #change to log()

p0Vect = p0Num/p0Denom         #change to log()

 

(array([ 0.04166667, 0.04166667,  0.04166667,  0.       ,  0.        ,

        0.04166667,  0.04166667, 0.04166667,  0.        , 0.04166667,

        0.04166667,  0.04166667, 0.04166667,  0.        , 0.        ,

        0.08333333,  0.       ,  0.        , 0.04166667,  0.        ,

        0.04166667,  0.04166667, 0.        ,  0.04166667, 0.04166667,

        0.04166667,  0.       ,  0.04166667,  0.       ,  0.04166667,

        0.04166667,  0.125    ]),

 

array([ 0.        ,  0.       ,  0.        , 0.05263158,  0.05263158,

        0.        , 0.        ,  0.       ,  0.05263158,  0.05263158,

        0.        , 0.        ,  0.       ,  0.05263158,  0.05263158,

        0.05263158,  0.05263158, 0.05263158,  0.        , 0.10526316,

        0.        , 0.05263158,  0.05263158,  0.       ,  0.10526316,

        0.        , 0.15789474,  0.        , 0.05263158,  0.        ,

        0.        , 0.        ]),

 

对应['cute', 'love','help', 'garbage', 'quit', 'I', 'problems', 'is', 'park', 'stop', 'flea','dalmation', 'licks', 'food', 'not', 'him', 'buying', 'posting', 'has','worthless',
'ate', 'to', 'maybe', 'please', 'dog', 'how', 'stupid', 'so','take', 'mr', 'steak', 'my']可以看到每个词在对应的分类里面的概率:

概率最大的是 stupid= 0.15789474 在脏话里面!也合乎只觉得概率统计。同时在好话里面, 概率stupid= 0

这个算法还真是挺巧妙地啊。简单的向量运算,就可以分类了!

以上是最原始的朴素贝叶斯算法。这个算法对文本分析有2点缺陷:

1.   如果某个属性在某个分类没有出现过,就会导致条件概率为零。这个时候必须引入修正的方法。常见做法是假定每个句子所有的词汇都出现一次,

就是矩阵初始化就是1,然后统计的时候分子分母都变化了。当然这个时候样本空间也大了。这个就是拉普拉斯修正。我看到别的例子有不同的修正方法。

有的是分母加上单词总数,分子只是在没有出现的属性+1. 不过从统计结果看,应该分类效果差不离。

 

p0Num = [ 2.  2.  2. 1.  1.  2. 2.  2.  1. 2.  2.  2. 2.  1.  1. 3.  1.  1.

1.  2.  2. 1.  2.  2.  2.  1. 2.  1.  2. 2.  4.]

p0Denom = 26.0

p1Num = [ 1.  1.  1. 2.  2.  1. 1.  1.  2. 2.  1.  1. 1.  2.  2. 2.  2.  2.

3.  1.  2.  2.  1.  3.  1. 4.  1.  2. 1.  1.  1.]

p1Denom = 21.0

 

 缺陷2: 对概率log计算,可以避免概率太低导致的下溢出。因为我们比较的是2个概率计算结果,所以都做log计算既可以避免计算溢出,又不影响比较大小。

 

对以上的缺陷修正后,有如下的计算演示:确实能分辨脏话和好话。

 

 

['love', 'my', 'dalmation'] classified as:  0

 

['stupid', 'garbage'] classified as:  1

 

['I', 'am', 'good', 'boy', 'life'] classifiedas:  0

 

['You', 'are', 'bad', 'boy', 'fool'] classifiedas:  0

 

以上就是基于贝努力朴素贝叶斯的简单分类方法,合适对不定长的文本做简单分类。因为这个方法不统计词频,所以分类精度受限。

词频才是甄别作者和标题最重要的属性。如果是定长的向量,就不用做拉普拉斯平滑了,因为每个属性都有数值。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐