美文网首页
机器学习实战-朴素贝叶斯

机器学习实战-朴素贝叶斯

作者: 又双叒叕苟了一天 | 来源:发表于2019-06-03 09:49 被阅读0次

    对于特征向量\boldsymbol w_i,拥有标签c_i,c_i\in\{c_1, c_2,...\},即分类个数。我们对每一类标签计算概率:
    p(c_i|\boldsymbol w)=\frac{p(\boldsymbol w|c_i)p(c_i)}{p(\boldsymbol w)}
    然后取概率最大的那个标签作为我们的分类。

    为了实现这个思路,我们首先需要构造特征向量\boldsymbol w_i

    拿文本分类的任务举例,我们构造一个数据集:

    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
    
    postingList, classVec = loadDataSet()
    

    一共有6条文本,classVec对每条文本的分类进行了标注。

    接着我们通过词袋的方式把这些文本转化成向量的形式,先统计所有文本中出现过的词汇,构成词汇表:

    def createVocabList(dataSet):
        vocabSet = set([])
        for document in dataSet:
            vocabSet = vocabSet | set(document)
        return list(vocabSet)
    
    vocabList = createVocabList(postingList)
    print(vocabList)
    '''
    ['worthless', 'is', 'I', 'not', 'flea', 'maybe', 'steak', 'help', 'quit', 'stop', 'so', 'garbage', 'stupid', 'mr', 'dalmation', 'dog', 'park', 'please', 'to', 'cute', 'him', 'my', 'take', 'licks', 'buying', 'food', 'posting', 'how', 'love', 'ate', 'has', 'problems']
    '''
    

    之后统计每个文本的每个单词的出现次数,构成一个稀疏表示的向量:

    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
    
    vec = setOfWords2Vec(vocabList, postingList[0])
    print(vec)  
    # [0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]
    

    上面是第一个文本的向量表示,0就表示词汇表中该位置的单词在这个文本中出现了1次,0表示没有出现过,当然也可能出现多次,那个位置就会使出现的次数n。

    然后我们计算p(\boldsymbol w|c_i)p(c_i),其中p(c_i)是这类文本占所有样本的比例。p(\boldsymbol w|c_i)=p(w_1,w_2,...,w_n|c_i)。假设所有单词的出现都独立,则p(\boldsymbol w|c_i)=p(w_0|c_i)p(w_1|c_i)...p(w_n|c_i)。而每个p(w|c_i)只需要计算这个wc_i这类文本中出现的次数除以c_i这类文本单词的总数就可以得到。我们通过以下代码得到p(w|c_i)p(c_i)

    def trainNB0(trainMatrix, trainCategory):
        numTrainDocs = len(trainMatrix)  # post个数
        numWords = len(trainMatrix[0])  # 词汇表大小
        pAbusive = sum(trainCategory) / float(numTrainDocs)  # 侮辱性的post占所有post的比例
        p0Num, p1Num = zeros(numWords), zeros(numWords)  # 初始化词汇表大小的两个0向量
        p0Denom, p1Denom = 0.0, 0.0
        for i in range(numTrainDocs):
            # 统计所有侮辱性post中每个单词的出现次数和侮辱性post的总长度
            if trainCategory[i] == 1:  # 侮辱性post
                p1Num += trainMatrix[i]
                p1Denom += sum(trainMatrix[i])
            # 统计所有非侮辱性post中每个单词的出现次数和非侮辱性post的总长度
            else:  # 非侮辱性post
                p0Num += trainMatrix[i]
                p0Denom += sum(trainMatrix[i])
        p1Vect = p1Num / p1Denom  # 词汇表中每个单词在侮辱性post中出现的概率
        p0Vect = p0Num / p0Denom  # 词汇表中每个单词在非侮辱性post中出现的概率
        return p0Vect, p1Vect, pAbusive
    
    trainMat = []
    for postinDoc in postingList:
        trainMat.append(setOfWords2Vec(vocabList, postinDoc))
    p0V, p1V, pAb = trainNB0(trainMat, classVec)
    print(p0V)
    # [0.         0.04166667 0.04166667 0.         0.04166667 0.
    #  0.04166667 0.04166667 0.         0.04166667 0.04166667 0.
    #  0.         0.04166667 0.04166667 0.04166667 0.         0.04166667
    #  0.04166667 0.04166667 0.08333333 0.125      0.         0.04166667
    #  0.         0.         0.         0.04166667 0.04166667 0.04166667
    #  0.04166667 0.04166667]
    print(p1V)
    # [0.10526316 0.         0.         0.05263158 0.         0.05263158
    #  0.         0.         0.05263158 0.05263158 0.         0.05263158
    #  0.15789474 0.         0.         0.10526316 0.05263158 0.
    #  0.05263158 0.         0.05263158 0.         0.05263158 0.
    #  0.05263158 0.05263158 0.05263158 0.         0.         0.
    #  0.         0.        ]
    print(pAb)  # 0.5
    

    我们可以看到在三个侮辱性文本中都出现的单词stupid,当文本是侮辱性的时候他出现的概率也最高,有0.157,即p1V下标12的单词。

    有了这些数据,我们就可以计算p(\boldsymbol w|c_i)p(c_i)=p(w_0|c_i)p(w_1|c_i)...p(w_n|c_i)p(c_i)了。但是这样还会存在问题,p(w|c_i)中只要存在一个为0,那么乘积就会等于0。所以我们初始化时假设每个单词已经都出现过了一次,就避免了这个问题。并且,因为每个p(w|c_i)的数值可能非常的小,他们之间的乘积p(w_0|c_i)p(w_1|c_i)...p(w_n|c_i)p(c_i)就也会非常的小,几个非常的小的结果就不太好比较大小,并且数值上也容易出错。所以就通过连续单调递增函数log计算\log p(\boldsymbol w|c_i)p(c_i)=\log p(w_0|c_i)+\log p(w_1|c_i)+...+\log p(w_n|c_i)+ \log p(c_i),比较不同类别之间\log p(\boldsymbol w|c_i)p(c_i)的大小。分母p(\boldsymbol w)对于不同类别的计算来说都相等,我们就不对它进行统计,只需比较分子的大小。

    改进后的代码如下

    def trainNB(trainMatrix, trainCategory):
        numTrainDocs = len(trainMatrix)  # post个数
        numWords = len(trainMatrix[0])  # 词汇表大小
        pAbusive = sum(trainCategory) / float(numTrainDocs)  # 侮辱性的post占所有post的比例
        p0Num, p1Num = ones(numWords), ones(numWords)  # 初始化词汇表大小的两个0向量
        p0Denom, p1Denom = 2.0, 2.0
        for i in range(numTrainDocs):
            # 统计所有侮辱性post中每个单词的出现次数和侮辱性post的总长度
            if trainCategory[i] == 1:  # 侮辱性post
                p1Num += trainMatrix[i]
                p1Denom += sum(trainMatrix[i])
            # 统计所有非侮辱性post中每个单词的出现次数和非侮辱性post的总长度
            else:  # 非侮辱性post
                p0Num += trainMatrix[i]
                p0Denom += sum(trainMatrix[i])
        p1Vect = log(p1Num / p1Denom)  # 词汇表中每个单词在侮辱性post中出现的概率
        p0Vect = log(p0Num / p0Denom)  # 词汇表中每个单词在非侮辱性post中出现的概率
        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 = trainNB(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))
    
    testingNB()
    # ['love', 'my', 'dalmation'] classified as:  0
    # ['stupid', 'garbage'] classified as:  1
    

    相关文章

      网友评论

          本文标题:机器学习实战-朴素贝叶斯

          本文链接:https://www.haomeiwen.com/subject/pjvqxctx.html