朴素贝叶斯是一个概率模型, 在数学上能用概率解释的模型一般被认为是好模型.
先介绍几个基础概念.
1. 概率
设 x 为符合某种特征的样本, H 为某个假设, 比如假设 x 属于类别 c, 那分类就是求这个假设发生的概率, 即 P(H|x) 的大小.
P(H|X) 是后验概率, 或者说在条件 X 下, H 的后验概率, 就是在已知样本符合某种特征时, H 发生的概率
P(H) 是先验概率, 或者说 H 的先验概率, 就是所有样本中 H 发生的概率
P(X|H),P(X) 同理
2. 条件概率
- P(A|B)=P(AB)/P(B)
- P(B|A)=P(AB)/P(A)
- P(A|B)=P(B|A)P(A)/P(B)
经典案例
我们想计算含有单词 drugs 的邮件为垃圾邮件的概率
A 为 "这是封垃圾邮件".P(A) 也被称为先验信念 (prior belief).
计算方法是, 统计训练集中垃圾邮件的比例.
如果我们的数据集每 100 封邮件有 30 封垃圾邮件, P(A) 为 30/100 或 0.3.
B 表示 "该封邮件含有单词 drugs".P(B) 也被称为先验信念 (prior belief).
类似地, 我们可以通过计算数据集中含有单词 drugs 的邮件数量得到 P(B).
如果每 100 封邮件中有 10 封邮件包含单词 drugs, 那么 P(B) 就为 10/100 或 0.1.
P(B|A) 指的是垃圾邮件中含有单词 drugs 的概率, 计算起来也很容易, 统计训练集中所有垃圾邮件的数量以及其中含有单词 drugs 的数量.
30 封垃圾邮件中, 如果有 6 封含有单词 drugs, 那么 P(B|A) 就为 6/30 或 0.2.
现在, 我们根据贝叶斯定理就能计算出 P(A|B), 得到含有 drugs 的邮件为垃圾邮件的概率.
把上面求出来的各项代入前面的贝叶斯公式, 得到结果 0.6.
这表明如果邮件中含有 drugs 这个词, 那么该邮件为垃圾邮件的概率为 60% .
算法原理
1. 朴素贝叶斯之所以朴素, 是其认为样本中属性间相互独立, 体现在概率论上就是彼此是独立事件, 故 P(AB)=P(A)P(B), 所以文本就可以表示为 P(X)=P(x1)P(x2)P(x3)...P(xk)
2. P(C|X)=P(X|C)P(C)/P(X)
P(X) 表示样本集中含有某特征的样本的比例, 是个常数, 且对所有样本都一样, 故可省略
P(C) 其实也是个常数, 但是要计算样本属于不同类别时要用不同类别的 P(C), 故不可省略
3. 计算出每个类别的概率, 取概率最大对应的类别即可
粘张图吧, 懒得写了
示例代码
- import numpy as np
- def loadDataSet():
- # 样本
- postingList=[['my','dog','has','flea','problem','help','please'],
- ['maybe','not','take','him','to','dog','park','stupid'],
- ['my','dalmation','is','so','cute','I','love','him'],
- ['stop','posting','ate','my','steak','how','to','stop','him'],
- ['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
- def createVocabList(dataSet):
- # 词向量集合
- vocabSet=set([])
- for document in dataSet:
- vocabSet=vocabSet|set(document)
- return list(vocabSet)
- def setOfWords2Vec(vocabList,inputSet):
- # word2vec 只有 0 1
- returnVec=[0]*len(vocabList) # 每个文档的大小与词典保持一致, 此时 returnVec 是空表
- for Word in inputSet:
- if Word in vocabList:
- returnVec[vocabList.index(Word)]=1 # 当前文档中有某个词条, 则根据词典获取其位置并赋值 1
- else:print "the word :%s is not in my vocabulary" %Word
- return returnVec
- def bagOfWords2Vec(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 trainNB(trainMatrix,trainCategory):
- # 训练模型 trainMatrix---X, trainCategory--->Y
- numTrainDocs=len(trainMatrix) # 样本数量
- numWords=len(trainMatrix[0]) # 属性集, 次向量长度
- pAbusive=sum(trainCategory)/float(numTrainDocs) # 统计侮辱性文档的总个数, 然后除以总文档个数 P(A)
- #p0Num=zeros(numWords);p1Num=zeros(numWords)
- #p0Denom=0.0;p1Denom=0.0
- p0Num=np.ones(numWords)
- p1Num=np.ones(numWords)
- p0Denom=2.0
- p1Denom=2.0
- for i in range(numTrainDocs):
- # 遍历样本
- if trainCategory[i]==1:
- # 标签为 1, 用于计算该类别下词的概率, P(X|A)
- p1Num+=trainMatrix[i] # 把属于同一类的文本向量相加, 实质是统计某个词条在该类文本中出现频率
- p1Denom+=sum(trainMatrix[i]) # 把侮辱性文档向量的所有元素加起来
- else:
- # 标签为 0, 用于计算该类别下词的概率, P(X|B)
- p0Num+=trainMatrix[i]
- p0Denom+=sum(trainMatrix[i])
- # p1Vec=p1Num/float(p1Denom)
- # p0Vec=p0Num/float(p0Denom)
- p1Vec=np.log(p1Num/p1Denom) # 统计词典中所有词条在侮辱性文档中出现的概率
- p0Vec=np.log(p0Num/p0Denom) # 统计词典中所有词条在正常文档中出现的概率
- return pAbusive,p1Vec,p0Vec
- ### 注意: 被注释掉的代码代表不太好的初始化方式, 在那种情况下某些词条的概率值可能会非常非常小, 甚至约
- ### 等于 0, 那么在不同词条的概率在相乘时结果就近似于 0
- def classifyNB(vec2classify,p0Vec,p1Vec,pClass1):
- # 预测
- # 参数 1 是测试文档向量, 参数 2 和参数 3 是词条在各个类别中出现的概率, 参数 4 是 P(C1)
- p1=sum(vec2classify*p1Vec)+np.log(pClass1) # 这里没有直接计算 P(x,y|C1)P(C1), 而是取其对数这样做也是防止概率之积太小, 以至于为 0
- p0=sum(vec2classify*p0Vec)+np.log(1.0-pClass1) # 取对数后虽然 P(C1|x,y) 和 P(C0|x,y) 的值变了, 但是不影响它们的大小关系.
- if p1>p0:
- return 1
- else:
- return 0
- if __name__ == '__main__':
- postingList,classVec = loadDataSet()
- vocab = createVocabList(postingList)
- setvec = [setOfWords2Vec(vocab, i) for i in postingList]
- bagvec = [bagOfWords2Vec(vocab, i) for i in postingList]
- pAbusive,p1Vec,p0Vec = trainNB(bagvec, classVec)
- for i in setvec:
- print classifyNB(i,p0Vec,p1Vec,pAbusive)
只是个示例, 拿训练样本测试的.
注意在预测时, 计算概率用了加法, 本身应该是乘法的, 但是因为取了 log, 乘法变加法, 注意在计算 P(xk) 时直接取了 log, 所以在预测时直接 sum 了.
log 避免了因概率因子远小于 1 而连乘造成的下溢出.
还有一种避免 0 概率的方法叫拉普拉斯平滑, 其实很简单, 就是在每个类别的样本数上 + 1, 那么样本最少是 1, 所以不会是 0 概率, 注意每个类别加 1, 总样本要加类别数.
总结
朴素贝叶斯对小规模数据集表现很好, 适合多分类, 但是如果属性间相关性较强, 表现不好.
参考资料:
- https://blog.csdn.net/amds123/article/details/70173402
- https://blog.csdn.net/li8zi8fa/article/details/76176597
朴素贝叶斯
来源: http://www.bubuko.com/infodetail-3012874.html