机器学习实战教程(四):朴素贝叶斯基础篇之论过滤器(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代表非侮辱类。

机器学习实战教程(四):朴素贝叶斯基础篇之论过滤器(b)_第1张图片
免费视频教程: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向量化的词条向量。

机器学习实战教程(四):朴素贝叶斯基础篇之论过滤器(b)_第2张图片
免费视频教程: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就是先验概率。

机器学习实战教程(四):朴素贝叶斯基础篇之论过滤器(b)_第3张图片
免费视频教程: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()函数,用朴素贝叶斯公式,计算词条向量属于侮辱类和非侮辱类的概率。运行结果如下:

机器学习实战教程(四):朴素贝叶斯基础篇之论过滤器(b)_第4张图片
免费视频教程:www.mlxs.top      

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

四、总结

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

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

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

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

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

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

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

你可能感兴趣的:(机器学习实战教程(四):朴素贝叶斯基础篇之论过滤器(b))