机器学习实战读书笔记-朴素贝叶斯

来源:互联网 发布:js自动计算日期时间差 编辑:程序博客网 时间:2024/06/07 00:26

朴素贝叶斯

朴素贝叶斯是一种基于概率论的分类方法

条件概率公式

P(Y|X)=P(X|Y)P(Y)P(X)

P(Y|X)表示,X条件下Y的概率。

举例说明:假设夫妻双方血型,丈夫为O型,妻子为AB型血。两人结合生下的孩子是什么血型?

丈夫血型 妻子血型 孩子的血型 aa AB ?

P(A型血|(aa,AB))表示孩子是A型血的概率,其中(丈夫为O型,妻子为AB型血即为条件)

其中P(X|Y)在各特征独立且权重一样的的情况下可以拆分成:

P(X|Y)=P((x1,x2...xn)|Y)=P((x1)|Y)P((x2)|Y)...P((xn)|Y)

注意X为特征向量,可以有多个特征。如下

x1 x2 x3 x4 y 0 1 1 0 yes 0 2 0 0 yes 1 1 1 0 no 2 1 1 0 yes

X表示每一行的(x1,x2,x3,x4), Y表示每一行的y

这就很清楚了,

P((x1,x2...xn)|Y)=P((x1)|Y)P((x2)|Y)...P((xn)|Y)

左边表示x1,x2…xn同时满足,右边则是拆分满足x1的情况,再满足x2等等。所以上述等式成立是显而易见的了。

根据以上的概率知识,只要获得给定X的情况下,P(Y|X)概率最大的,那个Y就是我们需要的分类了。

算法实现步骤

  • 收集数据(初始化数据)
  • 获取训练集中,所有不同的词列表(多少个不同的词)
  • 转化为训练集矩阵形式
  • 训练算法,计算不同的独立词中各种分类情况下的概率
  • 给定某一串文字,进行分类得出结果

下面以文本分类作为例子进行说明(以在线社区的留言板为例):

具体实现

def testingNB():    listOPosts, listClasses = loadDataSet() #初始化数据集及分类集    myVocabList = createVocabList(listOPosts)  # 获得训练集中,所有不同的词列表即词汇表    # 转化训练集为矩阵    trainMat = []    for postinDoc in listOPosts:        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))    p0V, p1V, pAb = AlgorithmUtil.trainNBO(array(trainMat), array(listClasses))    # testEntry = ['love', 'my', 'dalmation']    testEntry = ['dog', 'stupid', 'him']    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))    print testEntry,'classified as: ',AlgorithmUtil.classifyNB(thisDoc,p0V,p1V,pAb)

==========以下为一些子函数

#初始化训练数据def loadDataSet():    postingLis = [['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 postingLis, classVec

上面为初始训练集。当然实际情况不可能这么简单,这么少的。当然这不影响算法的实现步骤。

#获得包含在所有文档中出现的不重复词的列表(也就是获取词汇表)def createVocabList(dataSet):    #参数说明,dataSet可以看作上一函数中的postingLis,就是初始训练集    vocabSet = set([])    for document in dataSet:        vocabSet = vocabSet | set(document) #并集    return list(vocabSet)
#转化为词向量,向量的每一元素为1或0,分别表示词汇表中的单词在输人文档中是否出现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
#朴素贝叶斯分类器训练函数(只有俩类的情况)def trainNBO(trainMatrix,trainCategory):#参数说明,trainMatrix    numTrainDocs = len(trainMatrix) #行    numWords = len(trainMatrix[0]) #列    pAbusive = sum(trainCategory) / float(numTrainDocs) #整个训练集中,侮辱性言论的比例,或者概率。P(Y),即P(侮辱性言论)    p0Num = ones(numWords); p1Num = ones(numWords)    # 1 代表侮辱性文字,0代表正常言论    #p0Num表示训练集中,正常言论里面包含每一个词的个数。是一个向量(包括词汇表中每一个词)    #p1Num表示训练集中,侮辱性言论里面包含每一个词的个数是一个向量(包括词汇表中每一个词)    p0Denom = 2.0; p1Denom = 2.0    #p0Denom表示训练集中,正常言论里面总的词汇数(有可能重复)    #p1Denom表示训练集中,侮辱性言论里面总的词汇数(有可能重复)    for i in range(numTrainDocs):        if trainCategory[i] == 1:            p1Num += trainMatrix[i]            p1Denom += sum(trainMatrix[i])        else:            p0Num += trainMatrix[i]            p0Denom += sum(trainMatrix[i])    p1Vect = np.log(p1Num/p1Denom) #概率 分类1时,各词出现的概率    p0Vect = np.log(p0Num/p0Denom) #概率 分类0时,各词出现的概率    #加log,是为一些数学到考虑,下面会提到    return p0Vect, p1Vect, pAbusive

为什么p0Num,p1Num初始化为[1,1,….,1],不为[0,0,…,0]呢?
为什么p0Denom,p1Denom初始化为2呢?

是因为:利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率。从上面的函数我们可以看到,其实求到的是每一个词汇对应类别的概率。如果其中一个概率值为0,那么最后的乘积也为0。所以为降低 这种影响,可以将所有词的出现数初始化为1,并将分母初始化为2(分母不能为0吧)。这不影响结果,因为我们是要比较哪种分类的概率大

为什么要加得到的概率要log?

是因为:同样到要计算多个概率的乘积以获得文档属于某个类别的概率。由于大部分因子都非常小,所以程序会下溢出或者 得到不正确的答案。(很多很小到数相乘,四舍五入后会=0)。log不影响结果,因为我们是要比较哪种分类的概率大,log大的,还是大,趋势一致。(这应该好理解吧。。。)其实后面分解也会有用到。

#具体分类def classifyNB(vec2Classify, pOVec, plVec, pClassl):    #参数说明     #vec2Classify 测试文本(转换为向量后的)    #pOVec 训练集中,类别为0即正常言论里,各词的概率    #plVec 训练集中,类别为1即侮辱言论里,各词的概率    #pClassl 训练集中,侮辱言论的概率    pl = sum(vec2Classify * plVec) + log(pClassl)    p0 = sum(vec2Classify * pOVec) + log(1.0 - pClassl)    if pl > p0:        return 1    else: return 0

上面pl = sum(vec2Classify * plVec) + log(pClassl)的含义:

P(Y|X)=P(X|Y)P(Y)P(X)

根据P(X|Y)=P((x1,x2...xn)|Y)=P((x1)|Y)P((x2)|Y)...P((xn)|Y)

因为X是相同的,分母相同,取log,只要比较分子的大小即可。
log一下,就得到了(简单,自己推导一下)

原创粉丝点击