进行拉普拉斯平滑运算后,我们运行程序,仍然得出了两个测试样本均属于非侮辱类的结果,这是为什么呢?
我们查看最终计算出的p0和p1会发现,他们的结果都是0,这又是为什么呢?
这是因为出现了另一个问题--下溢出
我们的概率运算中,所有参与运算的概率都太小了,小数相乘会使运算的积进一步减小,最终结果向下溢出超出了计算机浮点数的精度,就都会变成0
解决办法很自然的可以想到--将乘法运算转换为加法运算,但如何在保证算法正确性的前提下进行转换呢?
在代数中,ln(a*b)=ln(a)+ln(b),同时,自然对数可以保证运算趋势的正确性:
因此我们通过对数运算优化训练函数trainNB0与测试函数classifyNB:
def trainNB0(trainMap, results):
"""
朴素贝叶斯分类器训练函数
:param trainMap: 训练文档矩阵
:param results: 训练类别标签向量
:return:
p0Vect - 侮辱类的条件概率数组
p1Vect - 非侮辱类的条件概率数组
pAbusive - 文档属于侮辱类的概率
"""
dataListNum = len(trainMap)
vocabularysNum = len(trainMap[0])
""" 计算文档属于侮辱词概率 """
pAbusive = sum(results) / float(dataListNum)
p0Num = np.ones(vocabularysNum)
p1Num = np.ones(vocabularysNum)
p0Denom = 2.0
p1Denom = 2.0
""" 将所有行按是否是侮辱类分别叠加,统计各个词出现的次数 """
for i in range(dataListNum):
if results[i] == 1:
p1Num += trainMap[i]
p1Denom += sum(trainMap[i])
else:
p0Num += trainMap[i]
p0Denom += sum(trainMap[i])
""" 计算概率 """
p1Vect = np.log(p1Num / p1Denom)
p0Vect = np.log(p0Num / p0Denom)
return p0Vect, p1Vect, pAbusive
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
"""
朴素贝叶斯分类器分类函数
:param vec2Classify: 待分类的词条数组
:param p0Vec: 侮辱类的条件概率数组
:param p1Vec: 非侮辱类的条件概率数组
:param pClass1: 文档属于侮辱类的概率
:return: 是否属于侮辱类,0. 不属于,1. 属于
"""
p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
print("p0: ", p0)
p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)
print("p1: ", p1)
if p1 > p0:
return 1
else:
return 0
最终我们得到了正确的结果:
p0: -7.694848072384611
p1: -9.826714493730215
['love', 'my', 'dalmation'] 属于非侮辱类
p0: -7.20934025660291
p1: -4.702750514326955
['stupid', 'garbage'] 属于侮辱类