一、原理
贝叶斯模型用于分类,考虑给定样本情况下给出可能性最大的类别,即条件概率 p ( c ∣ x ) p(c|x) p(c∣x)最大:
h ( x ) = a r g max c ∈ Y p ( c ∣ x ) h(x)=arg\max_{c∈Y}p(c|x) h(x)=argc∈Ymaxp(c∣x)。
又因为 p ( c ∣ x ) = p ( x , c ) p ( x ) p(c|x)=\frac{p(x,c)}{p(x)} p(c∣x)=p(x)p(x,c)。
由贝叶斯定理可得 p ( c ∣ x ) = p ( c ) p ( x ∣ c ) p ( x ) p(c|x)=\frac{p(c)p(x|c)}{p(x)} p(c∣x)=p(x)p(c)p(x∣c)。
朴素贝叶斯模型假设:对已知属性,所有属性相互独立。所以上式可以改写为
p ( c ∣ x ) = p ( c ) p ( x ∣ c ) p ( x ) = p ( c ) p ( x ) ∏ i d p ( x i ∣ c ) p(c|x)=\frac{p(c)p(x|c)}{p(x)}=\frac{p(c)}{p(x)}\prod_i^dp(x_i|c) p(c∣x)=p(x)p(c)p(x∣c)=p(x)p(c)i∏dp(xi∣c)
其中 d d d为数据的属性数。对于所有类别 p ( x ) p(x) p(x)相同,所以有
h ( x ) = a r g max c ∈ Y p ( c ) ∏ i d p ( x i ∣ c ) h(x)=arg\max_{c∈Y}{p(c)}\prod_i^dp(x_i|c) h(x)=argc∈Ymaxp(c)i∏dp(xi∣c)。
那么对于如何计算上式概率。
p ( c ) = ∣ D c ∣ ∣ D ∣ p(c)=\frac{|D_c|}{|D|} p(c)=∣D∣∣Dc∣
p ( x i ∣ c ) = ∣ D x i , c ∣ ∣ D c ∣ p(x_i|c)=\frac{|D_{x_i,c}|}{|D_c|} p(xi∣c)=∣Dc∣∣Dxi,c∣。
D D D, D C D_C DC及 D x i , c D_{x_i,c} Dxi,c分别为训练集,类别为 c c c的集合及 D c D_c Dc中第 i i i个属性上取值为 x i x_i xi的集合。这些集合的大小都是可以通过从训练集中的到。在实际应用中,由于在某些类别为 c c c的样本在第 i i i个属性上取值为 x i x_i xi的样本数目为0,或者某类别数目为0,而导致 p ( c ∣ x ) p(c|x) p(c∣x)为0,这显然是不合理的,所以要对概率值进行平滑:
p ( c ) = ∣ D c ∣ + 1 ] D ∣ + N p(c)=\frac{|D_c|+1}{]D|+N} p(c)=]D∣+N∣Dc∣+1
p ( x i ∣ c ) = ∣ D c , x i ∣ + 1 ∣ D c ∣ + N i p(x_i|c)=\frac{|D_{c,x_i}|+1}{|D_c|+N_i} p(xi∣c)=∣Dc∣+Ni∣Dc,xi∣+1。
二、用贝叶斯模型对评论进行分类
我们用贝叶斯模型将网上对某一事物的评论分为积极评论和消极评论进行辨别。首先,我们提取训练集中所有评论中的词,组成一个词表。然后,我们将每个评论进行数值化。最后我们可以利用训练集中的概率对新样本进行预测。
1、导入模块
import numpy as np
2、构建词表
1>首先,我们构造一个可以把评论转化为词组的程序。
def text2word(text):
import re
wordlist = re.split(r'\W+',text)
wordList = [word.lower() for word in wordlist if len(word)>2]
return wordList
这里我们用正则表达式过滤掉非字符,保留字符长度大于等于2的词。
比如我们有评论:
text = 'This book is the best book on Python or M.L I have ever laid eyes upon'
我们利用上面的函数进行转化:
print(text2word(text))
得到:
['this', 'book', 'is', 'the', 'best', 'book', 'on', 'python', 'or', 'have', 'ever', 'laid', 'eyes', 'upon']
2>将数组数值化
假如我们的到了如下的词组列表,以及其标记(1代表消极评论,0代表积极评论):
wordData = [['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']]
yLabel = [0,1,0,1,0,1]
我们的想法是对每一条评论创建一个大小为 d d d的数组, d d d为我们训练集中词汇的总数。例如我们这个wordData
有32个词,那么我们对一条评论创建一个大小为32的词组,词组的每个位置代表了一个词汇,如果这条评论中出现了love
这个词那么这条评论对应的数组的love
这个词对应的位置的数字为1,否则为0。也就是表示评论中是否出现了某个词。
这里,还提一下我这里只有6条评论,实际上我们需要比这多得多的训练集,如果训练集中样本数目不够,会使这个程序无法正常运行:训练样本数目不够的化,在预测样本中可能会出现训练样本中没有出现过的词汇,这实际上会使程序无法运行;当然如果谨慎考虑,你应该让词组中的词为拥有固定数目的具有代表性的词汇,这样就可以避免上述问题。
下面是创建词组的函数:
def createVocaList(wordData):
VocaList=[]
for word_data in wordData:
for voca in word_data:
if voca not in VocaList:
VocaList.append(voca)
return VocaList
这里由于涉及的词汇并不多,我们直接考虑将所有不重复词汇包括在这个词组中,运行函数:
vocaList=createVocaList(wordData)
print(vocaList)
得到:
['my', 'dog', 'has', 'flea', 'problems', 'help', 'please', 'maybe', 'not', 'take', 'him', 'to', 'park', 'stupid', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'stop', 'posting', 'worthless', 'garbage', 'mr', 'licks', 'ate', 'steak', 'how', 'quit', 'buying', 'food']
2>数值化
我们把把评论转化为列表形式是不够的,因为这还不是可以直接处理的数值型数据,下面我们把评论转化为我们1>中提到的表示某个词是否出现的01数组:
def word2Vect(wordData,vocaList): #列表——>数组
wordVect = np.zeros(len(vocaList))
wordSet = list(set(wordData))
for word in wordData:
if word in wordSet:
wordVect[vocaList.index(word)] = 1
else:
wordVect[vocaList.index(word)] = 0
return wordVect
运行程序将wordData
中第一条表示评论的词组转化为数组形式:
wordVect0 = word2Vect(wordData[0],vocaList)
print(wordVect0)
得到:
[1. 1. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0.]
接着我们可以把所有评论对应词组转化为数组形式:
for i in range(dataNumber):
wordVect[i,:] = word2Vect(wordData[i],vocaList)
print(wordVect)
得到:
[[1. 1. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0.]
[0. 1. 0. 0. 0. 0. 0. 1. 1. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 1. 1. 1. 1. 1. 1. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 1. 1. 1. 1.
0. 0. 0. 0. 0. 0. 0. 0.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0.
1. 1. 1. 1. 1. 0. 0. 0.]
[0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.
0. 0. 0. 0. 0. 1. 1. 1.]]
由于我们有6条评论,vocaList
中有32个词汇,所以上面数组大小实际上为6*32。这就是可以直接处理的数据形式。
3>计算相关概率
上面已经提过我们需要计算 p ( c ) p(c) p(c)和 p ( x i ∣ c ) p(x_i|c) p(xi∣c),也就是说在这个问题中,我们只需要计算积极/消极评论的概率 p ( 1 ) / p ( 0 ) p(1)/p(0) p(1)/p(0),以及不同类别下词 x x x出现的概率 p ( x ∣ 1 ) , p ( x ∣ 0 ) p(x|1),p(x|0) p(x∣1),p(x∣0)。
实际上我们把所有类别为1的数组对应位置相加,得到的数组的就是关于 ∣ D 1 , i ∣ |D_{1,i}| ∣D1,i∣的数组。再除以样本成为1的样本词汇总数,就是关于 p ( x i ∣ 1 ) p(x_i|1) p(xi∣1)的数组。当 y = 0 y=0 y=0时同理可得:
def caculateConditionalPro(wordVect,yLabel): #计算条件概率,即每个词在不同情况下出现概率
yLabel = np.array(yLabel)
wordVect1 = wordVect[np.where(yLabel == 1)[0],:]
wordVect0 = wordVect[np.where(yLabel == 0)[0],:]
p1Vect = np.log((np.sum(wordVect1,axis=0)+1) / (np.sum(wordVect1)+2)) #为了防止概率为0的情况,为分子加上1,分母加上N_i(属性i的取值个数),log函数为了防止概率趋于0
p0Vect = np.log((np.sum(wordVect0,axis=0)+1) / (np.sum(wordVect0)+2))
pAbusive = np.sum(yLabel) / len(yLabel)
return pAbusive,p1Vect,p0Vect
pA,p1Vect,p0Vect = caculateConditionalPro(wordVect,yLabel)
这里我们进行了平滑处理,而对概率进行函数np.log
处理是由于防止原概率过小而带来的问题,由于np.log
是单调函数,所以我们比较概率 p p p的大小可以转化为比较 l o g ( p ) log(p) log(p)的大小。pAbusive,p1Vect,p0Vect
分别表示类别为1的概率、 p ( x ∣ 1 ) p(x|1) p(x∣1)的数组以及 p ( x ∣ 0 ) p(x|0) p(x∣0)的数组。
3>计算概率
得到了pAbusive,p1Vect,p0Vect
我们就可以利用公式对新样本进行分类:
def NBclassification(pA,p1Vect,p0Vect,wordToClassify,vocaList):
wordVect = word2Vect(wordToClassify,vocaList)
p1 = np.sum(np.multiply(wordVect,p1Vect))+np.log(pA)
p0 = np.sum(np.multiply(wordVect,p0Vect))+np.log(1-pA)
if p1>p0:
return 1
else:
return 0
比如我们有预测样本:
worddata0 = ['love','my','dalmation']
worddata1 = ['stupid','garbage']
带入函数进行预测:
print(NBclassification(pA,p1Vect,p0Vect,worddata0,vocaList))
print(NBclassification(pA,p1Vect,p0Vect,worddata1,vocaList))
得到:
0
1
即预测worddata0
为积极评论,worddata1
为消极评论。