本系列代码托管于:https://github.com/chintsan-code/machine-learning-tutorials
本篇使用的项目为:bayes_class
在上一篇中,我们使用朴素贝叶斯时做了一个假设: 假设所有词都互相独立,该假设也称作条件独立性假设,它意味着可以使用 \( p(w_0|c_i)p(w_1|c_i)p(w_2|c_i)…p(w_N|c_i) \) 来计算\(p(w|c_i)\)。单词相互独立怎么理解呢?比如”I am handsome.”这句话,我们可以假设的就是I后面出现am或出现handsome的概率是一样的,这样就叫做单词相互独立,即I后面出现的单词和I本身没有关系,实际上当然不可能,I后面出现am比出现handsome的概率高多了。
既然如此我们的分类器应该这样写:
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
p1 = pClass1 * multi(vec2Classify * p1Vec) # multi:累乘
p0 = (1.0 - pClass1) * multi(vec2Classify * p0Vec)
if p1 > p0:
return 1
else:
return 0
其中:
$$pClass1=p(c_1)$$
$$ p(w|c_1) = p(w_0|c_i)p(w_1|c_i)p(w_2|c_i)…p(w_N|c_i) = multi(vec2Classify * p1Vec)$$
$$p(c_0|w)=p(c_0)\frac{ (p(w|c_0) }{p(w)}$$
$$p(c_1|w)=p(c_1)\frac{ (p(w|c_0) }{p(w)}$$
由于对同一句话来说,\( p(w) \)都是一样的,因此我们计算的时候可以简化掉,也就是只需要计算并比较下面两个概率的大小即可:
$$p0= p(c_0)p(w|c_0)=pClass1 * multi(vec2Classify * p1Vec)$$
$$p1= p(c_1)(p(w|c_1)=(1.0 – pClass1) * multi(vec2Classify * p1Vec)$$
在上一篇中,我们在最后说到构造的贝叶斯分类器有一个缺陷,本篇就是介绍缺陷是什么以及如何进行改进的。
既然我们准备使用 \( p(w_0|c_i)p(w_1|c_i)p(w_2|c_i)…p(w_N|c_i) \) 来计算\(p(w|c_i)\) ,那么就要意识到下面两个问题:
1. 如果有任意一个\( p(w_k|c_i) \) 的概率值为0,那么最后的乘积也为0;
2. 如果太小的数值相乘,有可能导致下溢出。(可以用Python尝试相乘许多很小的数,最后四舍五入后会得到0)
针对上面这两个问题,我们可以通过下面的方法进行改进:
1. 可以将所有词的出现数初始化为1,并将分母初始化为2;
2. 可以对乘积取自然对数。在代数中有ln(a*b) = ln(a)+ln(b),于是通过求对数可以 避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失。
下面的改进后的贝叶斯分类器的代码:
def trainNB0mprovement(trainMatrix, trainCategory):
numTrainDocs = len(trainMatrix)
numWords = len(trainMatrix[0])
pAbusive = sum(trainCategory) / float(numTrainDocs)
# 要计算多个概率的乘积以获得文档属于某个类别的概率不为0,故用1初始化
p0Num = ones(numWords);
p1Num = ones(numWords) # change to ones()
p0Denom = 2.0
p1Denom = 2.0 # 将分母的初始值改为2
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 = log(p1Num / p1Denom) # change to log()
p0Vect = log(p0Num / p0Denom) # change to log()
return p0Vect, p1Vect, pAbusive
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
p1 = sum(vec2Classify * p1Vec) + log(pClass1) # 元素相乘转为对数相加,p1Vec在trainNB0mprovement中已经取对数,因此在这里不用再做一次
p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
if p1 > p0:
return 1
else:
return 0
评论 (0)