使用一般的朴素贝叶斯分类器训练函数存在以下两个问题:
(1)其中一个词语在文本中没出现概率为0导致整组数据概率值为0。
利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,即计算p(w0|1)p(w1|1)p(w2|1)。如果其中一个概率值为0,那么最后的乘积也为0。
如下图:两组数据都出现了一个概率为0的情况。
(2)下溢出。这是由于太多很小的数相乘造成的。当计算乘积p(w0|ci)p(w1|ci)p(w2|ci)...p(wN|ci)时,由于大部分因子都非常小,所以程序会下溢出或者得到不正确的答案。
解决方案:
(1)为降低其中一个概率为0导致结果概率为0的影响,可以将所有词的出现数初始化为1,并将分母初始化为2。
对以下四行代码进行修改:
p0Num = np.zeros(numWords) p1Num = np.zeros(numWords) # 创建numpy.zeros数组,词条出现数初始化为0 p0Denom = 0.0 p1Denom = 0.0 # 分母初始化为0修改为:
# 将所有词的出现次数初始化为1,分母则初始化为2 p0Num = np.ones(numWords) p1Num = np.ones(numWords) p0Denom = 2.0 p1Denom = 2.0(2)为解决下溢出,可以对乘积取自然对数。在代数中有ln(a*b) = ln(a)+ln(b),于是通过求对数可以避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失。
修改以下两行代码:
p1Vect = p1Num / p1Denom p0Vect = p0Num / p0Denom修改为:
p1Vect = log(p1Num / p1Denom) p0Vect = log(p0Num / p0Denom)
在主函数编写如下代码测试:
if __name__ == "__main__": postingList, classVec = loadDataSet() myVocabList = createVocabList(postingList) print(myVocabList) trainMat = [] for postinDoc in postingList: trainMat.append(setWordsToVec(myVocabList, postinDoc)) # trainMat类似[1, 0, 1, 0, 0, 0, ...],[0, 1, 0, 0, 0, 0, ...], ... # print('trainMat:\n', trainMat) p0V, p1V, pAb = trainNB0(trainMat, classVec) testEntry1 = ['love', 'my', 'dalmation'] thisDoc = np.array(setWordsToVec(myVocabList, testEntry1)) if classifyByNB(thisDoc, p0V, p1V, pAb): print(testEntry1, '侮辱类') else: print(testEntry1, '非侮辱类') testEntry2 = ['stupid', 'garbage'] thisDoc = np.array(setWordsToVec(myVocabList, testEntry2)) if classifyByNB(thisDoc, p0V, p1V, pAb): print(testEntry2, '侮辱类') else: print(testEntry2, '非侮辱类')运行结果如下: