【机器学习】朴素贝叶斯基本介绍+代码实现

版权声明:转载请注明 https://blog.csdn.net/u013166817/article/details/84128601

1. 基本概念

根据先验概率和似然函数来求后验概率。一般用于分类任务。

P(c_i|w) = \frac{P(w|c_i)P(c_i)}{P(w)}

先验概率:P(c_i)

似然函数:P(w|c_i)

后验概率:P(c_i|w)

根据条件独立性假设:

P(w|c_i) = P(w_1|c_i)P(w_2|c_i)...P(w_n|c_i)

目标函数:即求解使后验概率最大的类。

训练过程:即求各个单词的条件概率,和类别的先验概率。

测试过程:根据已经得到的条件概率和先验概率,计算不同类别的后验概率,取最大的类。

2. 优缺点

优点:简单,易于实现。

缺点:由于条件独立性假设,使得分类性能不是很好。并且对输入数据格式有限制。

3. 应用题(影评分类)

训练影评数据集
样本 标签
Good movie. 1
I like it. 1
So amazing. 1
Great movie. 1
Bad movie. 0
I hate it. 0
So boring. 0
Boring movie. 0

求影评“I hate bad movie.”的类别(1代表好的影评,0代表坏的。)

令 w = “I hate bad movie.”

c = argmax_{k} P(c=k|w),k\in \{1,0\}

P(c=1|w) = \frac{P(w|c=1)P(c=1)}{P(w)} = \frac{P(c=1)}{P(w)}\prod_{i=1}^{4}P(w_i|c=1)

P(c=0|w) = \frac{P(w|c=0)P(c=0)}{P(w)} = \frac{P(c=0)}{P(w)}\prod_{i=1}^{4}P(w_i|c=0)


P(I|c=1) = \frac{count(I|c=1)}{9}= \frac{1}{9},

 P(hate|c=1) =\frac{count(hate|c = 1)}{9}\xrightarrow[smoothing]{ } \frac{0 + 1}{9 + 2} = \frac{1}{11}

P(bad|c=1) =\frac{count(bad|c = 1)}{9}\xrightarrow[smoothing]{ } \frac{0 + 1}{9 + 2} = \frac{1}{11}

P(movie|c=1) =\frac{count(movie|c = 1)}{9} = \frac{2}{9}

P(c=1) = \frac{1}{2}

因此:

P(c=1|w) = \frac{1}{P(w)}*\frac{1}{9} * \frac{1}{11} *\frac{1}{11}*\frac{2}{9}*\frac{1}{2} = \frac{1}{9801}*\frac{1}{P(w)}


P(I|c=0) = \frac{count(I|c=0)}{9}= \frac{1}{9}

P(hate|c=0) =\frac{count(hate|c = 0)}{9}= \frac{1}{9}

P(bad|c=0) =\frac{count(bad|c = 0)}{9}= \frac{1}{9}

P(movie|c=0) =\frac{count(movie|c = 0)}{9} = \frac{2}{9}

P(c=0) = \frac{1}{2}

因此:

P(c=0|w) =\frac{1}{P(w)} * \frac{1}{9} * \frac{1}{9} *\frac{1}{9}*\frac{2}{9}*\frac{1}{2} = \frac{1}{6561}*\frac{1}{P(w)}

P(c=0|w) > P(c=1|w), 则为“坏评价”。


总结:

1. 朴素贝叶斯常用模型:多项式模型,伯努利模型,高斯模型

多项式模型:基于单词出现次数

先验概率:P(c) = 类c下所有文档的单词总数 / 所有文档单词总数

条件概率:P(wi|c) = (类c下单词wi出现的次数 + 1)/ (类c下所有文档的单词总数 + 类别数)

伯努利模型:基于单词是否出现(二项特征)

先验概率:P(c) = 类c下文档数 / 总文档数

条件概率指示函数:P(i|c) = 类c下单词wi出现过的文档数 / 类c下所有文档数

条件概率: P(wi|c) = P(i|c)wi + (1-P(i|c))(1-wi)

因此多项式模型通过smoothing同等对待未出现单词,而伯努利模型则显示的对待(反向作用)。

混合模型:既考虑词频但是又限制词语出现次数的模型为,其通过类别的分类不均来计算一个权重。

文本分类时一般可选择多项式模型和伯努利模型,当句子较短时,伯努利的效果可能会更好。

高斯模型:特征值符合高斯分布的连续型变量,比如说人的身高,物体的长度。

2. 对于未出现词,比如‘hate’ 在正例里未出现,为了防止其条件概率为0,可使用smoothing方法,比如add-1(laplacian smoothing)。

3. 上述条件概率乘积太小,为了防止向下溢出,可以使用自然对数log,将乘法转化为加法。

4. 上述例子仅为一个参考。当训练集十分庞大时(比如烂番茄评论数据库),则我们可以剔除“停用词”,例如:the, a, I ......,只选择形容词,如:good,bad......

5. 词袋模型,仅考虑单词出现次数,不考虑前后顺序。对应上述的多项式模型。词集模型:仅考虑单词是否出现,若出现记为1,不计数。词袋模型要优于词集模型。


4. 编程实现(垃圾邮件分类)

参考:《机器学习实战》

源码地址以及数据:https://github.com/JieruZhang/MachineLearninginAction_src

手写python朴素贝叶斯:

import re
import random
from numpy import *

#tokenization,分词
def textParse(s):
    tokens = re.split(r'\W*', s)
    #转化成小写,且只取长度大于2的单词
    return [tok.lower() for tok in tokens if len(tok) > 2]

#通过set来创建无重复单词的字典
def createVocab(fullText):
    return list(set(fullText))

#将一个单词list转化为向量表示, 某单词存在,则把vocabs中对应位置赋为1(此处应用的是伯努利模型,即不考虑次数,只考虑是否出现)
def words2Vec(vocabs, words):
    vec = [0 for _ in range(len(vocabs))]
    for word in words:
        if word in vocabs:
            vec[vocabs.index(word)] = 1
    return vec
        
#训练过程
def trainNB(trainMat, trainClasses):
    numDocs = len(trainMat)
    numWords = len(trainMat[0])
    #垃圾邮件的概率
    pSpam = sum(trainClasses)/float(numDocs)
    #分子p0是类别为0的概率
    p0num = zeros(numWords)
    p1num = zeros(numWords)
    #分母
    p0denom = 0.0
    p1denom = 0.0
    for i in range(numDocs):
        #计算1类的分子分母,0类的分子分母
        if trainClasses[i] == 1:
            p1num += trainMat[i]
            p1denom += sum(trainMat[i])
        else:
            p0num += trainMat[i]
            p0denom += sum(trainMat[i])
    #计算概率,p1,p0,这里两者均为向量表示,每个位置时该位置对应的单词的概率p1[i] = p(wi|c=1), p0[i] = p(wi|c=0)
    #取自然对数是为了转化乘法为加法,防止向下溢出
    p1 = log(p1num/p1denom)
    p0 = log(p0num/p0denom)
    return p0, p1, pSpam

#分类过程,传入的概率p0和p1都是取了自然对数的,pSpam没有取
def classifyNB(wordvec,p0vec,p1vec,pSpam):
    p1 = sum(wordvec*p1vec) + log(pSpam)
    p0 = sum(wordvec*p0vec) + log(1.0-pSpam)
    #返回概率大的类别
    if p1 > p0:
        return 1
    else:
        return 0

    
def spamTest():
    docs = []
    classes = []
    fullText = []
    #总共有25个正例,25个反例
    for i in range(1,26):
        #每封邮件作为一个大字符串,使用textParse分词放入list
        #docs存放每一封分词过后的邮件,fullText存放所有的单词,classes存放类别(spam中是正类)
        words = textParse(open('email/spam/%d.txt'%i).read())
        docs.append(words)
        fullText.extend(words)
        classes.append(1)
        #同理求负例
        words = textParse(open('email/ham/%d.txt'%i,encoding='gbk').read())
        docs.append(words)
        fullText.extend(words)
        classes.append(0)
    #构建字典
    vocabs = createVocab(fullText)
    #总共50封邮件,随机选择10封作为测试集,剩余40封为训练集,trainIndex和testIndex存的是选取的邮件的index
    trainIndex = [i for i in range(50)]
    testIndex = []
    for i in range(10):
        randIndex = int(random.uniform(0,len(trainIndex)))
        testIndex.append(trainIndex[randIndex])
        del trainIndex[randIndex]
    #对于训练集,将每封邮件的单词列表转化成向量表示, 并存入相应的list
    trainMat = []
    trainClasses = []
    for index in trainIndex:
        trainMat.append(words2Vec(vocabs, docs[index]))
        trainClasses.append(classes[index])
    #训练模型,得到条件概率向量,以及先验概率pSpam
    p0, p1, pSpam = trainNB(array(trainMat),array(trainClasses))
    #在测试集上测试
    errorCount = 0
    for index in testIndex:
        wordvec = words2Vec(vocabs, docs[index])
        if classifyNB(array(wordvec), p0, p1, pSpam) != classes[index]:
            errorCount += 1
    print('error rate is:', float(errorCount)/len(testIndex))
    
spamTest()

使用sklearn包实现:

#使用sklearn工具包进行分类
from sklearn.naive_bayes import MultinomialNB

def spamNBsklearn():
    #数据准备过程同上
    docs = []
    classes = []
    fullText = []
    for i in range(1,26):
        words = textParse(open('email/spam/%d.txt'%i).read())
        docs.append(words)
        fullText.extend(words)
        classes.append(1)
        #同理求负例
        words = textParse(open('email/ham/%d.txt'%i,encoding='gbk').read())
        docs.append(words)
        fullText.extend(words)
        classes.append(0)
    #构建字典
    vocabs = createVocab(fullText)
    #总共50封邮件,随机选择10封作为测试集,剩余40封为训练集,trainIndex和testIndex存的是选取的邮件的index
    trainIndex = [i for i in range(50)]
    testIndex = []
    for i in range(10):
        randIndex = int(random.uniform(0,len(trainIndex)))
        testIndex.append(trainIndex[randIndex])
        del trainIndex[randIndex]
    #对于训练集,将每封邮件的单词列表转化成向量表示, 并存入相应的list
    trainMat = []
    trainClasses = []
    for index in trainIndex:
        trainMat.append(words2Vec(vocabs, docs[index]))
        trainClasses.append(classes[index])
    #对于测试集,将每封邮件的单词列表转化成向量表示, 并存入相应的list
    testMat = []
    testClasses = []
    for index in testIndex:
        testMat.append(words2Vec(vocabs, docs[index]))
        testClasses.append(classes[index])
    #使用sklearn包训练(使用多项式模型)
    clf = MultinomialNB()
    clf.fit(trainMat, trainClasses)
    #test, clf.score 输出对测试样本的预测准确率平均值
    score = clf.score(testMat, testClasses)
    print('error rate is:', 1-score)

spamNBsklearn()

猜你喜欢

转载自blog.csdn.net/u013166817/article/details/84128601
今日推荐