朴素贝叶斯从原理到算法的实现

基于概率论的分类方法:朴素贝叶斯

经典举例:已知学校总人数N,男生占3/5,都穿长裤,女生占2/5,一半穿长裤,一般穿裙子。

(1).穿长裤的人数:N*3/5 + N*2/5*1/2

(2).当你看到一个人穿长裤,不知性别时,问该人是女生的概率?

  P(女生|长裤) = 女生穿长裤的人数/穿长裤总人数  =   P(女)*P(长裤|女)/P(长裤)

经典贝叶斯公式:

下面通过对文本进行分类来理解贝叶斯模型:

1、准备数据

其中列表中的每一项来自斑点犬爱好者留言板,这些留言文本被切分成一系列的词条集合,标点符号从文本中去掉,留言的文本被分为两类,分为侮辱性和非侮辱性。

def loadDataSet():
    dataSet = [['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']]
    classLabel = [0,1,0,1,0,1] #1代表侮辱性,0代表正常言论   
    return dataSet,classLabel 

2、创建一个在所有文档中出现的不重复的词表

def vocabularyTable(dataSet):
    vocabSet = set([])
    for document in dataSet:
        vocabSet = vocabSet | set(document)
    return list(vocabSet)

3、构建词条向量

对于每一个文档,即每一条评论,生成一个文档向量。首先创建一个和词表长度一样长的向量,并将其元素都置为0,接着遍历文档中的所有单词,如果在词表中出现,则将文档向量中对应值设为1。

#vocabSet为词表,document为文档即评论
def doc2vec(vocabSet,document):
    docVec = [0]*len(vocabSet)
    for word in document:
        if (word in vocabSet):
            docVec[vocabSet.index(word)] = 1
    return docVec     

4.训练贝叶斯分类器模型

前面介绍了如何将一组单词转换为一组数字,接下来看看如何使用这些数字计算概率。现在已经知道一个词是否出现在一篇文档中,也知道该文档所属的类别。利用经典贝叶斯公式:

其中表示一个向量 ,即它由多个词组成,表示w属于i类的概率。

具体如何计算呢:首先通过类别i(侮辱性或非侮辱性留言)中文档数除以总的文档数来计算概率,接下来计算,这里就要用到朴素贝叶斯假设。如果将w展开为一个个独立特征,那么就可以将上述概率写成。这里假设所有词都相互独立,该假设也称作条件独立假设,它意味着可以用

**...来计算上述概率,这就极大简化了计算的过程。

#trainVec为文档矩阵,由每条文档的词条向量构成,classLabel为分类标签
import numpy as np
def trainBayes(trainVec,classLabel):
    numData = len(trainVec)
    numWords = len(trainVec[0])
    pAbusive = sum(classLabel)/float(numData)
    p0num = np.zeros(numWords); p1num = np.zeros(numWords)
    p0sum = 0.0; p1sum = 0.0
    for i in range(numData):
        if(classLabel[i]==1):
            p1num += trainVec[i]
            p1sum += sum(trainVec[i])
        else:
            p0num += trainVec[i]
            p0sum += sum(trainVec[i])
    p1Vect = p1num/p1sum
    p0Vect = p0num/p0sum
    return pAbusive,p1Vect,p0Vect

trainVec = []
for document in dataSet:
    trainVec.append(doc2vec(vocabSet,document))

pAbusive,p1Vect,p0Vect =  trainBayes(trainVec,classLabel)

pAbusive
>>0.5
p1Vect
>>array([0.10526316, 0.        , 0.05263158, 0.        , 0.05263158,
       0.        , 0.05263158, 0.05263158, 0.        , 0.        ,
       0.05263158, 0.10526316, 0.05263158, 0.        , 0.05263158,
       0.        , 0.        , 0.05263158, 0.        , 0.15789474,
       0.05263158, 0.        , 0.05263158, 0.        , 0.05263158,
       0.05263158, 0.        , 0.        , 0.        , 0.        ,
       0.        , 0.        ])
p0Vect 
>>array([0.        , 0.04166667, 0.08333333, 0.04166667, 0.04166667,
       0.04166667, 0.        , 0.        , 0.04166667, 0.04166667,
       0.        , 0.04166667, 0.        , 0.125     , 0.04166667,
       0.04166667, 0.04166667, 0.        , 0.04166667, 0.        ,
       0.        , 0.04166667, 0.        , 0.04166667, 0.        ,
       0.        , 0.04166667, 0.04166667, 0.04166667, 0.04166667,
       0.04166667, 0.04166667])
#我们可看到倒数第三个概率分别是0与0.04166667,词汇表的倒数第三个单词是cute,其在类别0中出现1次,在类别1中未出现,说明该计算是正确的
#我们找到所有概率中的最大值,该值出现在p1Vect的第20个位置,大小为0.15789474,词汇表的第20个位置单词是stupid,这意味着stupid是最能表征类别1(侮辱性文档类)的单词

5.根据现实情况修改分类器

利用贝叶斯分类器进行文档分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,即计算***...。如果其中一个概率值为 0,那么最后的乘积也为0。为降低这种影响,可以将所有词的出现初始化为1,并将分母初始化为2。还有一个问题是由于太多小数相乘可能造成下溢出,这种解决办法是取自然对数。修改代码如下:

import numpy as np
import math
def trainBayes(trainVec,classLabel):
    numData = len(trainVec)
    numWords = len(trainVec[0])
    pAbusive = sum(classLabel)/float(numData)
    p0num = np.ones(numWords); p1num = np.ones(numWords)
    p0sum = 2; p1sum = 2
    for i in range(numData):
        if(classLabel[i]==1):
            p1num += trainVec[i]
            p1sum += sum(trainVec[i])
        else:
            p0num += trainVec[i]
            p0sum += sum(trainVec[i])
    for i in range(numWords):
        p1Vect[i] = math.log(p1num[i]/p1sum)
        p0Vect[i] = math.log(p0num[i]/p0sum)
    return pAbusive,p1Vect,p0Vect

6.朴素贝叶斯分类函数

def classify(docVec,pClass1,p1Vect,p0Vect):
    p1 = sum(docVec * p1Vect)+math.log(pClass1)
    p0 = sum(docVec *p0Vect) +math.log(1-pClass1)
    if(p1>p0):
        return 1
    else:
        return 0

#进行测试
test = ['love','my','dalmation']
testVec = doc2vec(vocabSet,test)
pClass1,p1Vect,p0Vect = trainBayes(trainVec,classLabel)
classify(testVec,pClass1,p1Vect,p0Vect)
>>0
test = ['stupid','garbage']
testVec = doc2vec(vocabSet,test)
pClass1,p1Vect,p0Vect = trainBayes(trainVec,classLabel)
classify(testVec,pClass1,p1Vect,p0Vect)
>>1
#对训练集进行测试
test = dataSet[0]
testVec = doc2vec(vocabSet,test)
pClass1,p1Vect,p0Vect = trainBayes(trainVec,classLabel)
classify(testVec,pClass1,p1Vect,p0Vect)
>>0

7.词袋模型

上面那个例子非常简单,但是它展示了朴素贝叶斯的工作原理。接下来,我们会对代码稍加修改,使分类器工作得更好。

目前为止,我们将每个词的出现与否作为一个特征,这可以被描述为词集模型(set-of-words model)。如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现在文档中所不能表达的某种信息,这种方法称为词袋模型(bag-of-words model)。在词袋中,每个文档即每个评论中每个单词可以出现多次,而在词集中每个词只能出现一次,为适应词袋模型,需要对doc2vec稍加修改。与词集模型的代码一模一样,唯一不同的是每当遇到一个单词时,它会增加词向量中的对应值,而不只是将对应的数值设为1。

def bagOfWordsDoc2Vec(vocabSet,document):
    docVec = [0]*len(vocabSet)
    for word in document:
        if (word in vocabSet):
            docVec[vocabSet.index(word)] += 1
    return docVec    

猜你喜欢

转载自blog.csdn.net/qq_24946843/article/details/84192067
今日推荐