一、概述
本文是《统计学习方法》的第四章,包含朴素贝叶斯分类器的原理与python实现。希望自己能坚持下去,完成整本书的学习
二、朴素贝叶斯算法
由于我们需要学习的参数为
P
(
x
,
y
)
P(x,y)
P(x,y),而
P
(
X
=
x
,
Y
=
y
)
=
P
(
X
=
x
∣
Y
=
y
)
P
(
Y
=
y
)
P(X=x,Y=y)=P(X=x|Y=y)P(Y=y)
P(X=x,Y=y)=P(X=x∣Y=y)P(Y=y),其中
P
(
X
=
x
∣
Y
=
y
)
P(X=x|Y=y)
P(X=x∣Y=y)的参数是指数级的。当维度较大时,会发送维度灾难。
具体地说,X和Y的组合很多,假设
x
j
xj
xj可能取值
S
j
Sj
Sj个,Y可能取值有
K
K
K个,那么参数的个数是参数个数
K
∗
∏
j
=
0
n
S
j
K*\prod_{j=0}^nSj
K∗∏j=0nSj。特别地,取
x
j
=
S
xj=S
xj=S,那么参数个数为
K
S
n
KS^n
KSn,当维数
n
n
n很大的时候,就会发生维数灾难。
基于维度灾难的问题,朴素贝叶斯对特征作了独立性假设,即假设所有特征之间相互独立,不存在相关性:
也就是各个维度的特征在类确定的情况下都是独立分布的。这一假设简化了计算,也牺牲了一定的分类准确率。计算出每个类别的以上条件概率,选择条件概率最大的类别
C
k
Ck
Ck作为分类器的预测值。
三、朴素贝叶斯的学习与分类
先从训练数据中计算先验概率和条件概率,然后对于给定的实例计算最大的条件概率,输出该条件对应的类别。书中的形式化的描述如下:
以上公式虽然较多,但实际原理其实较为简单,理解之后会发现原理非常通俗易懂。
不过,朴素贝叶斯会有一个问题,那就是当训练集中不存在某种参数与类别的组合时,会得出该条件概率为0,但是不能保证测试集中不存在这样的参数组合。解决办法是平滑概率为0的组合,此时采用贝叶斯估计进行平滑:
四、条件概率的贝叶斯估计:
其中,
λ
\lambda
λ是介于0到1的平滑因子。当其值为0,等同于极大似然估计;当其值等于1,为拉普拉斯平滑。
S
j
Sj
Sj是
X
X
X的取值个数。有了以上平滑方法,则可以解决因参数组合在训练集中不存在而造成的条件概率为0的情况了。下面,本文将参考《snownlp》,利用拉普拉斯平滑的朴素贝叶斯分类器完成一个情感分类器的建模。
五、python实现朴素贝叶斯情感分类器
下面使用python3来实现朴素贝叶斯情感分类器:
from math import log,exp
class LaplaceEstimate(object):
def __init__(self):
self.d={}
self.total=0.0#全部词的词频
self.none=1#当一个词不存在时,它的词频为拉普拉斯平滑后分子为一的数
def exists(self,key):
return key in self.d
def getsum(self):
return self.total
def get(self,key):
if not self.exists(key):
return False,self.none
return True,self.d[key]
def getprob(self,key):
return float(self.get(key)[1])/self.total
def samples(self):
return self.d.keys()
def add(self,key,value):
self.total+=value
if not self.exists(key):
self.d[key]=1
self.total+=1
self.d[key]+=value
class Bayes(object):
def __init__(self):
self.d={}#d存储词链表和标签
self.total=0
def train(self,data):
for d in data:
c=d[1]
if c not in self.d:
self.d[c]=LaplaceEstimate()
for word in d[0]:
self.d[c].add(word,1)
self.total=sum(map(lambda x:self.d[x].getsum(),self.d.keys()))
def classify(self,x):
tmp={}
for c in self.d:
tmp[c]=log(self.d[c].getsum())-log(self.total)#P(Y=ck)
for word in x:
tmp[c]+=log(self.d[c].getprob(word))#P(Xj=xj|Y=ck)
ret,prob=0,0
for c in self.d:
now=0
try:
for otherc in self.d:
now+=exp(tmp[otherc]-tmp[c])
now=1/now
except OverflowError:
now=0
if now>prob:
ret,prob=c,now
return (ret,prob)
class Sentiment(object):
def __init__(self):
self.classifier=Bayes()
def segment(self,sent):
words=sent.split(' ')
return words
def train(self,neg_docs,pos_docs):
data=[]
for sents in neg_docs:
data.append([self.segment(sents),u'neg'])
for sents in pos_docs:
data.append([self.segment(sents),u'pos'])
self.classifier.train(data)
def classify(self,sent):
return self.classifier.classify(self.segment(sent))
s=Sentiment()
s.train([u'糟糕', u'好 差劲'], [u'优秀', u'很 好'])
print(s.classify(u'好 优秀'))
上述代码的输出如下:
可见,预测为积极的概率为0.667。朴素贝叶斯分类器得到了正确的答案。
五、总结
朴素贝叶斯是一种较为简单的分类器,其没有显示的学习过程,直接利用训练集的条件概率来进行建模,并对特征进行了独立性假设。这简化了计算,同时牺牲了一定的分类精度。