朴素贝叶斯

来源:互联网 发布:淘宝收货地址 编辑:程序博客网 时间:2024/06/04 20:04

本博客是用jupyter notebook写的,在CSDN上是用其转换的markdown,想要获取本篇博客的笔记与源码,请访问:我的github
最近在看《机器学习实战》,里面有一个朴素贝叶斯理论,这个理论并不难,不过书上说得挺绕,网上有个博客挺好,简单明了,可以看一下:朴素贝叶斯分类器的应用。通过病人分类和账号分类的例子,应该就比较清楚朴素贝叶斯分类器是干啥的了,算出在哪个特征(Feature)的前提下,分类(Category)的概率最高,就划分到那个分类中就行了。我们使用书上狗狗论坛的例子,有如下几段文字,将其分类为侮辱性和非侮辱性,0代表正常言论,1代表侮辱言论:

import numpy as npdef 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 is abusive, 0 not    return postingList,classVec
listOPosts,listClasses = loadDataSet()print(listOPosts)print(listClasses)
[['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']][0, 1, 0, 1, 0, 1]

接下来就是统计有哪些单词,这些单词将会组成我们的特征(Feature)

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

在书中,使用了词向量这一概念,这样做的好处是一切向量化,好处理,不过也不太适合初学者理解,所以我的博客中想尝试不用词向量,这样看起来更直观。现在开始统计每个单词的在侮辱性和非侮辱性语句中出现的概率也就是统计P(|c=0)P(|c=1)

def getProb(postingList,classVec):    classSet=set(classVec)#统计有多少种类别    classProb=dict((x,classVec.count(x)/len(classVec)) for x in  classVec)    feature=[]    for c in classSet:        feature.append([y for x,i in zip(postingList,classVec) if i==c for y in x])    allCount=[len(x) for x in feature]    prob={}    for w,c,cs in zip(feature,allCount,classSet):        prob[cs]=dict((x, w.count(x)/c) for x in w)#统计侮辱性语句中每个单词出现的数目    return [prob,classProb]
[featureProb,classProb] = getProb(listOPosts,listClasses)print(featureProb)print(classProb)
{0: {'so': 0.041666666666666664, 'flea': 0.041666666666666664, 'cute': 0.041666666666666664, 'problems': 0.041666666666666664, 'how': 0.041666666666666664, 'to': 0.041666666666666664, 'steak': 0.041666666666666664, 'licks': 0.041666666666666664, 'dalmation': 0.041666666666666664, 'I': 0.041666666666666664, 'dog': 0.041666666666666664, 'my': 0.125, 'ate': 0.041666666666666664, 'love': 0.041666666666666664, 'is': 0.041666666666666664, 'please': 0.041666666666666664, 'stop': 0.041666666666666664, 'help': 0.041666666666666664, 'mr': 0.041666666666666664, 'has': 0.041666666666666664, 'him': 0.08333333333333333}, 1: {'food': 0.05263157894736842, 'park': 0.05263157894736842, 'maybe': 0.05263157894736842, 'worthless': 0.10526315789473684, 'stupid': 0.15789473684210525, 'posting': 0.05263157894736842, 'stop': 0.05263157894736842, 'dog': 0.10526315789473684, 'take': 0.05263157894736842, 'quit': 0.05263157894736842, 'garbage': 0.05263157894736842, 'to': 0.05263157894736842, 'not': 0.05263157894736842, 'buying': 0.05263157894736842, 'him': 0.05263157894736842}}{0: 0.5, 1: 0.5}

这样我们就知道了所有的单词在某一类别的概率了,为了让大家更直观,我们这里手动算1个:
P(dog|c=0)的计算:dog在正常语句中出现了1次,正常语句一共有24个(重复单词也重复计算),所以P(dog|c=0)=124=0.0417
P(dog|c=1)的计算:dog在侮辱语句中出现了2次,正常语句一共有19个,所以P(dog|c=0)=219=0.1052

现在我想看这句“stop posting stupid worthless garbage”是不是侮辱人的话,其实就是要算P(c=0|stop,posting,stupid,worthless,garbage)P(c=1|stop,posting,stupid,worthless,garbage)哪个概率高,根据贝叶斯理论,我们可以:
P(c=C|stop,posting,stupid,worthless,garbage)=P(stop,posting,stupid,worthless,garbage|c=C)P(c=C)P(stop,posting,stupid,worthless,garbage)
由于分母都一样,所以可以只求P(stop,posting,stupid,worthless,garbage|c=C)P(c=C),看看是c=0高还是c=1高,这样还不行,还要继续化简:
P(stop,posting,stupid,worthless,garbage|c=C)P(c=C)=
P(stop|c=C)P(posting|c=C)P(stupid|c=C)P(worthless|c=C)P(garbage|c=C)P(c=C)
好了,这回所有需要的概率我们都有了,直接算完比较大小就行了。

def classifyProb(data, featureProb, classProb):    classes=classProb.keys()    prob={}    for c in classes:        p=1        for d in data:            #找到对应单词的概率,没有的话说明概率为0            try:                ptmp = featureProb[c][d]            except:                ptmp = 0            p=p*ptmp        prob[c]=p*classProb[c]    return prob
for words in listOPosts:    prob=classifyProb(words,featureProb,classProb)    print(prob)
{0: 3.270488053519375e-10, 1: 0.0}{0: 0.0, 1: 1.7664137924166456e-10}{0: 2.7254067112661458e-11, 1: 0.0}{0: 0.0, 1: 1.2115832202185774e-06}{0: 1.1355861296942274e-12, 1: 0.0}{0: 0.0, 1: 1.2753507581248183e-07}

注意

好了,经过试验我们也成功得到了我们想要的结果,不过还有几个地方要注意(按照书上来的):
1. 几个概率连续相乘之后,数特别小,咱们计算机小数精度都是有限的,这样子运算误差会很大,因此可以在getProb()这个函数里给概率套上log或者ln函数。
2. 我们在classifyProb()这个函数中遇到一个单词就把这个单词的概率乘上,这叫做文档词袋模型,而另一种模型叫做词集模型,就是句子里面重复的单词只算一次概率。我个人觉得文档词袋模型更好,比如一句话里面出现七八个 stupid,那肯定比只出现一个stupid更像是骂人的话吧……当然,这是我的猜想,也没有进行数学推导。

总结

朴素贝叶斯其实非常简单,因为假设每个特征之间互相独立,所以可以直接用贝叶斯来逆推概率,只要记得贝叶斯公式,自己也能手推概率。

原创粉丝点击