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

朴素贝叶斯分类器训练函数 基本原理

p(ci|w)=p(w|ci)p(ci)p(w) p ( c i | w ) = p ( w | c i ) p ( c i ) p ( w )

w w 表 示 向 量 由 多 个 值 组 成

即核心问题:量化为在向量w发生时 属于 ci c i 的概率比较问题

条件概率中 分母是一致的 因此不需要考虑吧

分母表示在属于类别 ci c i 时发生,向量 w w 发生的概率

这里假设所有词都互相独立,该假设也称作条件独立性假设(例如 A 和 B 两个人抛骰子,概率是互不影响的,也就是相互独立的,A 抛 2点的同时 B 抛 3 点的概率就是 1/6 * 1/6),它意味着可以使用 p(w0 | ci)p(w1 | ci)p(w2 | ci)…p(wn | ci) 来计算上述概率,这样就极大地简化了计算的过程。

问题量化: ci,w 在 类 别 c i 发 生 时 , w 发 生 的 可 能 性

即: i,w 属 于 侮 辱 性 类 别 i 时 , 向 量 w 发 生 的 可 能 性

引入到向量 i, 在 i 类 别 时 , 每 个 单 词 出 现 的 概 率

Project:社区留言板侮辱性言论屏蔽
Project Readme:

    构建一个快速过滤器来屏蔽在线社区留言板上的侮辱性言论.
    如果某条留言使用了负面或者侮辱性的语言,那么就将该留言标识为内容不当。
    对此问题建立两个类别: 侮辱类和非侮辱类,使用 1 和 0 分别表示
def loadDataSet():
    """
    训练数据集
    :return: 单词列表postingList, 所属类别classVec
    """
    logging.info("read dataSet")
    postingList = [['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'], #[0,0,1,1,1......]
                   ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
                   ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
                   ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
                   ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
                   ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
    classVec = [0, 1, 0, 1, 0, 1]  # 1 is abusive, 0 not
    return postingList, classVec

step1 建立分词集合

def createWordsSet(dataSet):
    logging.info("create words Set")
    # print(np.shape(dataSet)) (6,)

    wordsSet = set([])

    for words in dataSet:
        wordsSet = wordsSet | set(words)
    return list(wordsSet)

step2 转化数据为向量

def transferToVec(data, wordsSet):

    logging.info("transfer words to vec")
    # print(len(wordsSet)) output 32
    vec = [0] * len(wordsSet)
    print(vec, len(vec))

    for i in data:
        if i in wordsSet:
            print("词汇存在于集合中")
            vec[wordsSet.index(i)] += 1
        else:
            print("词汇不存在与集合中:%s"% i)

    # print(vec)
    return vec

step3 先验概率及其条件概率计算

def tranNBMaxLike(vecDataSet, classLable):
    """
        param vecDataSet: 输入空间的向量表示形式
        param classLable: 输出空间的类标记集合

        return p1Abusive: 先验概率
        return p0Abusive: 先验概率
        return p1Con: 条件概率 1类别 每个单词出现次数的占比
        return p0Con: 条件概率 0类别 每个单词出现次数的占比
    """
    logging.info("Maximum likelihood estimate p1Con p0Con")
    classLen = len(classLable)
    vecLen = len(vecDataSet[0])

    print(classLen, vecLen)
    # 先验概率
    p1Abusive = sum(classLable) / float(classLen)
    print('侮辱言论概率', p1Abusive)
    p0Abusive = 1 - p1Abusive
    print('非侮辱言论概率', p0Abusive)

    # 针对nxj维度的数据集 遵循条件独立性假设
    # 构造记录单词出现次数的列表
    p1Vec = np.zeros(vecLen)
    p0Vec = np.zeros(vecLen)

    # 条件概率下 每个类别出现单词的频数
    p0Nums = 0
    p1Nums = 0

    # 条件概率
    for i in range(classLen):
        # 在侮辱言论发生的条件下
        if classLable[i] == 1:
            # 单词出现的频数 -- 以向量形式记录
            p1Vec += vecDataSet[i]
            # 该类别时 出现的单词总频数
            p1Nums += sum(vecDataSet[i])
        # 在非侮辱言论发生的条件下
        if classLable[i] == 0:
            p0Vec += vecDataSet[i]
            p0Nums += sum(vecDataSet[i])

    # print(p1Vec)
    # print(p0Vec)
    # print(sum(p1Vec), p1Nums)
    # print(sum(p0Vec), p0Nums)

    # 计算条件概率
    # 类别1,即侮辱性文档的[P(F1|C1),P(F2|C1),P(F3|C1),P(F4|C1),P(F5|C1)....]列表
    # 即 在1类别下,每个
    # 单词出现次数的占比
    p1Con = p1Vec / p1Nums
    # print('p1Con', p1Con)
    # 类别0,即正常文档的[P(F1|C0),P(F2|C0),P(F3|C0),P(F4|C0),P(F5|C0)....]列表
    # 即 在1类别下,每个单词出现次数的占比
    p0Con = p0Vec / p0Nums
    # print('p0Con', p1Con)

    # print(p1)
    # print(p0)

    return p1Con, p0Con, p1Abusive, p0Abusive

step4 朴素贝叶斯分类

def classifyNB(testVec, p1Con, p0Con, p1Abusive, p0Abusive):
    """
        param: testVec 待测数据集的向量表示形式
        param p1Con: 类别1,即侮辱性文档的[P(F1|C1),P(F2|C1),P(F3|C1),P(F4|C1),P(F5|C1)....]列表
        param p0Con: 类别1,即侮辱性文档的[P(F1|C1),P(F2|C1),P(F3|C1),P(F4|C1),P(F5|C1)....]列表
        param p1Abusive: 即 在1类别下,每个单词出现次数的占比
        param p0Abusive: 即 在0类别下,每个单词出现次数的占比

        return: 0 or 1 类别

    """
    """
        计算方法:解决下溢出的问题 将乘积问题转化为相加
                对乘积去自然对数 ln(a * b) = ln(a) + ln(b)
                求对数可以避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失
    """
    logging.info("Bayes classify")
    testVec = np.array(testVec)
    p1Con = np.array(p1Con)
    p0Con = np.array(p0Con)

    """
        注意:numpy array进行向量运算时 若存在无效值将会使得 运算结果为nan
            需要设置 np.seterr(divide='ignore', invalid='ignore')
            即使设置之后 计算np.sum 还是保存 弃用此方法
    """
    pp = np.log(p1Con)
    print('pp', pp)
    print(sum(testVec * pp))
    print(sum(testVec * p1Con))
    p1 = sum(testVec * p1Con) + p1Abusive
    p0 = sum(testVec * p0Con) + p0Abusive

    print('p1', p1)
    print('p0', p0)

    if p1 > p0:
        return 1
    return 0

算法检测

def main():
    dataSet, classLable = loadDataSet()
    # print(dataSet)
    # print(classLable)
    wordsSet = createWordsSet(
        dataSet)
    vecDataSet = []
    for i in dataSet:
        vecDataSet.append(transferToVec(i, wordsSet))
    print(vecDataSet)
    p1Con, p0Con, p1Abusive, p0Abusive = tranNBMaxLike(vecDataSet, classLable)
    test = ['love', 'my', 'dalmation'] # 0
    # test = ['stupid', 'garbage']  # 1
    testVec = transferToVec(test, wordsSet)
    print(testVec)

    result = classifyNB(testVec, p1Con, p0Con, p1Abusive, p0Abusive)
    print(result)


if __name__ == '__main__':
    main()

参考文献
机器学习实战
ApacheCN-github

你可能感兴趣的:(MachineLearning)