朴素贝叶斯法实现 --基于贝叶斯估计(垃圾邮件分类)

实现朴素贝叶斯的两个缺点

1.在利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,即计算 p(w0|1) * p(w1|1) * p(w2|1)。如果其中一个概率值为 0,那么最后的乘积也为 0。为降低这种影响,可以将所有词的出现数初始化为 1,并将分母初始化为 2 (取1 或 2 的目的主要是为了保证分子和分母不为0,大家可以根据业务需求进行更改)。

2.另一个遇到的问题是下溢出,这是由于太多很小的数相乘造成的。当计算乘积 p(w0|ci) * p(w1|ci) * p(w2|ci)… p(wn|ci) 时,由于大部分因子都非常小,所以程序会下溢出或者得到不正确的答案。(用 Python 尝试相乘许多很小的数,最后四舍五入后会得到 0)。一种解决办法是对乘积取自然对数。在代数中有 ln(a * b) = ln(a) + ln(b), 于是通过求对数可以避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失。

下图给出了函数 f(x) 与 ln(f(x)) 的曲线。可以看出,它们在相同区域内同时增加或者减少,并且在相同点上取到极值。它们的取值虽然不同,但不影响最终结果。

朴素贝叶斯法实现 --基于贝叶斯估计(垃圾邮件分类)_第1张图片

因此使用贝叶斯估计 采用拉普拉斯平滑 优化算法
优化实现

# 先验概率计算及条件概率计算
def tranNBBayes(vecDataSet, classLabel):
    """

    :param vecDataSet: 输入数据集
    :param classLabel: 输出空间类标记
    :return: p1Con 条件概率
    :return: p0Con 条件概率
    :return: probDict 先验概率
    """
    # 先验概率计算
    classCount = dict()
    for vote in classLabel:
        classCount[vote] = classCount.get(vote, 0) + 1
    # print(classCount)

    classNums = len(classLabel)
    probDict = {}
    for key, value in classCount.items():
        probDict[key] = value / classNums

    # print(probDict)

    # 条件概率计算
    # 在类别n条件下, 即侮辱性文档的[P(F1|Cn),P(F2|Cn),P(F3|Cn),P(F4|Cn),P(F5|Cn)....]列表
    classStastic = list(i for i in probDict.keys()) # 就两个类别
    p1Vec = np.ones(len(vecDataSet[0]))
    p0vec = np.ones(len(vecDataSet[0]))

    p1Nums = len(classStastic)
    p0Nums = len(classStastic)

    for i in range(len(classLabel)):
        if classLabel[i] == classStastic[0]:
            p1Vec += vecDataSet[i]
            p1Nums += sum(vecDataSet[i])
        else:
            p0vec += vecDataSet[i]
            p0Nums += sum(vecDataSet[i])

    # 条件概率计算
    p1Con = np.log(p1Vec / p1Nums)
    p0Con = np.log(p0vec / p0Nums)

    print(p1Con)
    print(p0Con)
    print(probDict)
    return p1Con, p0Con, probDict

def classify(testVec, p1Con, p0Con, probDict):
    """

    :param testVec: 数据转向量
    :param p1Con: 条件概率 类别1 即侮辱性文档的[P(F1|Cn),P(F2|Cn),P(F3|Cn),P(F4|Cn),P(F5|Cn)....]列表
    :param p0Con: 条件概率 类别0 即侮辱性文档的[P(F1|Cn),P(F2|Cn),P(F3|Cn),P(F4|Cn),P(F5|Cn)....]列表
    :param probDict: 先验概率
    :return: 分类 0 or 1
    """

    p1 = np.abs(np.sum(np.array(testVec) * p1Con) + np.log(probDict[1]))
    p0 = np.abs(np.sum(np.array(testVec) * p0Con) + np.log(probDict[0]))
    print(p1)
    print(p0)
    if p1 > p0:
        return 1
    return 0

参考文献
机器学习实战
ApacheCN

你可能感兴趣的:(MachineLearning)