机器学习实战教程(四):朴素贝叶斯基础篇之论过滤器(b)

三、动手实战

说了这么多,没点实践编程怎么行?

以在线社区留言为例。为了不影响社区的发展,我们要屏蔽侮辱性的言论,所以要构建一个快速过滤器,如果某条留言使用了负面或者侮辱性的语言,那么就将该留言标志为内容不当。过滤这类内容是一个很常见的需求。对此问题建立两个类型:侮辱类和非侮辱类,使用1和0分别表示。

我们把文本看成单词向量或者词条向量,也就是说将句子转换为向量。考虑出现所有文档中的单词,再决定将哪些单词纳入词汇表或者说所要的词汇集合,然后必须要将每一篇文档转换为词汇表上的向量。简单起见,我们先假设已经将本文切分完毕,存放到列表中,并对词汇向量进行分类标注。编写代码如下:

# -*- coding: UTF-8 -*-

"""

函数说明:创建实验样本

Parameters:

    无

Returns:

    postingList - 实验样本切分的词条

    classVec - 类别标签向量

Author:

    Jack Cui

Modify:

    2017-08-11

"""

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

if __name__ == '__main__':

    postingLIst, classVec = loadDataSet()

    for each in postingLIst:

        print(each)

    print(classVec)

从运行结果可以看出,我们已经将postingList是存放词条列表中,classVec是存放每个词条的所属类别,1代表侮辱类 ,0代表非侮辱类。

免费视频教程:www.mlxs.top      

继续编写代码,前面我们已经说过我们要先创建一个词汇表,并将切分好的词条转换为词条向量。

# -*- coding: UTF-8 -*-

"""

函数说明:创建实验样本

Parameters:

    无

Returns:

    postingList - 实验样本切分的词条

    classVec - 类别标签向量

Author:

    Jack Cui

Modify:

    2017-08-11

"""

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

"""

函数说明:根据vocabList词汇表,将inputSet向量化,向量的每个元素为1或0

Parameters:

    vocabList - createVocabList返回的列表

    inputSet - 切分的词条列表

Returns:

    returnVec - 文档向量,词集模型

Author:

    Jack Cui

Modify:

    2017-08-11

"""

def setOfWords2Vec(vocabList, inputSet):

    returnVec = [0] * len(vocabList)                                    #创建一个其中所含元素都为0的向量

    for word in inputSet:                                                #遍历每个词条

        if word in vocabList:                                            #如果词条存在于词汇表中,则置1

            returnVec[vocabList.index(word)] = 1

        else: print("the word: %s is not in my Vocabulary!" % word)

    return returnVec                                                    #返回文档向量

"""

函数说明:将切分的实验样本词条整理成不重复的词条列表,也就是词汇表

Parameters:

    dataSet - 整理的样本数据集

Returns:

    vocabSet - 返回不重复的词条列表,也就是词汇表

Author:

    Jack Cui

Modify:

    2017-08-11

"""

def createVocabList(dataSet):

    vocabSet = set([])                      #创建一个空的不重复列表

    for document in dataSet:             

        vocabSet = vocabSet | set(document) #取并集

    return list(vocabSet)

if __name__ == '__main__':

    postingList, classVec = loadDataSet()

    print('postingList:\n',postingList)

    myVocabList = createVocabList(postingList)

    print('myVocabList:\n',myVocabList)

    trainMat = []

    for postinDoc in postingList:

        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))

    print('trainMat:\n', trainMat)

        从运行结果可以看出,postingList是原始的词条列表,myVocabList是词汇表。myVocabList是所有单词出现的集合,没有重复的元素。词汇表是用来干什么的?没错,它是用来将词条向量化的,一个单词在词汇表中出现过一次,那么就在相应位置记作1,如果没有出现就在相应位置记作0。trainMat是所有的词条向量组成的列表。它里面存放的是根据myVocabList向量化的词条向量。

免费视频教程:www.mlxs.top      

我们已经得到了词条向量。接下来,我们就可以通过词条向量训练朴素贝叶斯分类器。

# -*- coding: UTF-8 -*-

import numpy as np

"""

函数说明:创建实验样本

Parameters:

    无

Returns:

    postingList - 实验样本切分的词条

    classVec - 类别标签向量

Author:

    Jack Cui

Modify:

    2017-08-11

"""

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

"""

函数说明:根据vocabList词汇表,将inputSet向量化,向量的每个元素为1或0

Parameters:

    vocabList - createVocabList返回的列表

    inputSet - 切分的词条列表

Returns:

    returnVec - 文档向量,词集模型

Author:

    Jack Cui

Modify:

    2017-08-11

"""

def setOfWords2Vec(vocabList, inputSet):

    returnVec = [0] * len(vocabList)                                    #创建一个其中所含元素都为0的向量

    for word in inputSet:                                                #遍历每个词条

        if word in vocabList:                                            #如果词条存在于词汇表中,则置1

            returnVec[vocabList.index(word)] = 1

        else: print("the word: %s is not in my Vocabulary!" % word)

    return returnVec                                                    #返回文档向量

"""

函数说明:将切分的实验样本词条整理成不重复的词条列表,也就是词汇表

Parameters:

    dataSet - 整理的样本数据集

Returns:

    vocabSet - 返回不重复的词条列表,也就是词汇表

Author:

    Jack Cui

Modify:

    2017-08-11

"""

def createVocabList(dataSet):

    vocabSet = set([])                      #创建一个空的不重复列表

    for document in dataSet:

        vocabSet = vocabSet | set(document) #取并集

    return list(vocabSet)

"""

函数说明:朴素贝叶斯分类器训练函数

Parameters:

    trainMatrix - 训练文档矩阵,即setOfWords2Vec返回的returnVec构成的矩阵

    trainCategory - 训练类别标签向量,即loadDataSet返回的classVec

Returns:

    p0Vect - 非侮辱类的条件概率数组

    p1Vect - 侮辱类的条件概率数组

    pAbusive - 文档属于侮辱类的概率

Author:

    Jack Cui

Modify:

    2017-08-12

"""

def trainNB0(trainMatrix,trainCategory):

    numTrainDocs = len(trainMatrix)                            #计算训练的文档数目

    numWords = len(trainMatrix[0])                            #计算每篇文档的词条数

    pAbusive = sum(trainCategory)/float(numTrainDocs)        #文档属于侮辱类的概率

    p0Num = np.zeros(numWords); p1Num = np.zeros(numWords)    #创建numpy.zeros数组,词条出现数初始化为0

    p0Denom = 0.0; p1Denom = 0.0                            #分母初始化为0

    for i in range(numTrainDocs):

        if trainCategory[i] == 1:                            #统计属于侮辱类的条件概率所需的数据,即P(w0|1),P(w1|1),P(w2|1)···

            p1Num += trainMatrix[i]

            p1Denom += sum(trainMatrix[i])

        else:                                                #统计属于非侮辱类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)···

            p0Num += trainMatrix[i]

            p0Denom += sum(trainMatrix[i])

    p1Vect = p1Num/p1Denom                                     

    p0Vect = p0Num/p0Denom       

    return p0Vect,p1Vect,pAbusive                            #返回属于侮辱类的条件概率数组,属于非侮辱类的条件概率数组,文档属于侮辱类的概率

if __name__ == '__main__':

    postingList, classVec = loadDataSet()

    myVocabList = createVocabList(postingList)

    print('myVocabList:\n', myVocabList)

    trainMat = []

    for postinDoc in postingList:

        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))

    p0V, p1V, pAb = trainNB0(trainMat, classVec)

    print('p0V:\n', p0V)

    print('p1V:\n', p1V)

    print('classVec:\n', classVec)

    print('pAb:\n', pAb)

          运行结果如下,p0V存放的是每个单词属于类别0,也就是非侮辱类词汇的概率。比如p0V的倒数第6个概率,就是stupid这个单词属于非侮辱类的概率为0。同理,p1V的倒数第6个概率,就是stupid这个单词属于侮辱类的概率为0.15789474,也就是约等于15.79%的概率。我们知道stupid的中文意思是蠢货,难听点的叫法就是傻逼。显而易见,这个单词属于侮辱类。pAb是所有侮辱类的样本占所有样本的概率,从classVec中可以看出,一用有3个侮辱类,3个非侮辱类。所以侮辱类的概率是0.5。因此p0V存放的就是P(him | 非侮辱类) = 0.0833,P(is | 非侮辱类) = 0.0417,一直到P(dog | 非侮辱类) = 0.0417,这些单词的条件概率。同理,p1V存放的就是各个单词属于侮辱类的条件概率。pAb就是先验概率。

免费视频教程:www.mlxs.top      

已经训练好分类器,接下来,使用分类器进行分类。

# -*- coding: UTF-8 -*-

import numpy as np

from functools import reduce

"""

函数说明:创建实验样本

Parameters:

    无

Returns:

    postingList - 实验样本切分的词条

    classVec - 类别标签向量

Author:

    Jack Cui

Modify:

    2017-08-11

"""

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                                                                #返回实验样本切分的词条和类别标签向量

"""

函数说明:将切分的实验样本词条整理成不重复的词条列表,也就是词汇表

Parameters:

    dataSet - 整理的样本数据集

Returns:

    vocabSet - 返回不重复的词条列表,也就是词汇表

Author:

    Jack Cui

Modify:

    2017-08-11

"""

def createVocabList(dataSet):

    vocabSet = set([])                      #创建一个空的不重复列表

    for document in dataSet:               

        vocabSet = vocabSet | set(document) #取并集

    return list(vocabSet)

"""

函数说明:根据vocabList词汇表,将inputSet向量化,向量的每个元素为1或0

Parameters:

    vocabList - createVocabList返回的列表

    inputSet - 切分的词条列表

Returns:

    returnVec - 文档向量,词集模型

Author:

    Jack Cui

Modify:

    2017-08-11

"""

def setOfWords2Vec(vocabList, inputSet):

    returnVec = [0] * len(vocabList)                                    #创建一个其中所含元素都为0的向量

    for word in inputSet:                                                #遍历每个词条

        if word in vocabList:                                            #如果词条存在于词汇表中,则置1

            returnVec[vocabList.index(word)] = 1

        else: print("the word: %s is not in my Vocabulary!" % word)

    return returnVec                                                    #返回文档向量

"""

函数说明:朴素贝叶斯分类器训练函数

Parameters:

    trainMatrix - 训练文档矩阵,即setOfWords2Vec返回的returnVec构成的矩阵

    trainCategory - 训练类别标签向量,即loadDataSet返回的classVec

Returns:

    p0Vect - 非侮辱类的条件概率数组

    p1Vect - 侮辱类的条件概率数组

    pAbusive - 文档属于侮辱类的概率

Author:

    Jack Cui

Modify:

    2017-08-12

"""

def trainNB0(trainMatrix,trainCategory):

    numTrainDocs = len(trainMatrix)                            #计算训练的文档数目

    numWords = len(trainMatrix[0])                            #计算每篇文档的词条数

    pAbusive = sum(trainCategory)/float(numTrainDocs)        #文档属于侮辱类的概率

    p0Num = np.zeros(numWords); p1Num = np.zeros(numWords)    #创建numpy.zeros数组,

    p0Denom = 0.0; p1Denom = 0.0                            #分母初始化为0.0

    for i in range(numTrainDocs):

        if trainCategory[i] == 1:                            #统计属于侮辱类的条件概率所需的数据,即P(w0|1),P(w1|1),P(w2|1)···

            p1Num += trainMatrix[i]

            p1Denom += sum(trainMatrix[i])

        else:                                                #统计属于非侮辱类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)···

            p0Num += trainMatrix[i]

            p0Denom += sum(trainMatrix[i])

    p1Vect = p1Num/p1Denom                                    #相除       

    p0Vect = p0Num/p0Denom         

    return p0Vect,p1Vect,pAbusive                            #返回属于侮辱类的条件概率数组,属于非侮辱类的条件概率数组,文档属于侮辱类的概率

"""

函数说明:朴素贝叶斯分类器分类函数

Parameters:

    vec2Classify - 待分类的词条数组

    p0Vec - 侮辱类的条件概率数组

    p1Vec -非侮辱类的条件概率数组

    pClass1 - 文档属于侮辱类的概率

Returns:

    0 - 属于非侮辱类

    1 - 属于侮辱类

Author:

    Jack Cui

Modify:

    2017-08-12

"""

def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):

    p1 = reduce(lambda x,y:x*y, vec2Classify * p1Vec) * pClass1                #对应元素相乘

    p0 = reduce(lambda x,y:x*y, vec2Classify * p0Vec) * (1.0 - pClass1)

    print('p0:',p0)

    print('p1:',p1)

    if p1 > p0:

        return 1

    else:

        return 0

"""

函数说明:测试朴素贝叶斯分类器

Parameters:

    无

Returns:

    无

Author:

    Jack Cui

Modify:

    2017-08-12

"""

def testingNB():

    listOPosts,listClasses = loadDataSet()                                    #创建实验样本

    myVocabList = createVocabList(listOPosts)                                #创建词汇表

    trainMat=[]

    for postinDoc in listOPosts:

        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))                #将实验样本向量化

    p0V,p1V,pAb = trainNB0(np.array(trainMat),np.array(listClasses))        #训练朴素贝叶斯分类器

    testEntry = ['love', 'my', 'dalmation']                                    #测试样本1

    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))                #测试样本向量化

    if classifyNB(thisDoc,p0V,p1V,pAb):

        print(testEntry,'属于侮辱类')                                        #执行分类并打印分类结果

    else:

        print(testEntry,'属于非侮辱类')                                        #执行分类并打印分类结果

    testEntry = ['stupid', 'garbage']                                        #测试样本2

    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))                #测试样本向量化

    if classifyNB(thisDoc,p0V,p1V,pAb):

        print(testEntry,'属于侮辱类')                                        #执行分类并打印分类结果

    else:

        print(testEntry,'属于非侮辱类')                                        #执行分类并打印分类结果

if __name__ == '__main__':

    testingNB()

我们测试了两个词条,在使用分类器前,也需要对词条向量化,然后使用classifyNB()函数,用朴素贝叶斯公式,计算词条向量属于侮辱类和非侮辱类的概率。运行结果如下:

免费视频教程:www.mlxs.top      

你会发现,这样写的算法无法进行分类,p0和p1的计算结果都是0,显然结果错误。这是为什么呢?下一篇文章继续讲解~

四、总结

朴素贝叶斯推断的一些优点:

生成式模型,通过计算概率来进行分类,可以用来处理多分类问题。

对小规模的数据表现很好,适合多分类任务,适合增量式训练,算法也比较简单。

朴素贝叶斯推断的一些缺点:

对输入数据的表达形式很敏感。

由于朴素贝叶斯的“朴素”特点,所以会带来一些准确率上的损失。

需要计算先验概率,分类决策存在错误率。免费视频教程:www.mlxs.top    

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,875评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,569评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,475评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,459评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,537评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,563评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,580评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,326评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,773评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,086评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,252评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,921评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,566评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,190评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,435评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,129评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,125评论 2 352