贝叶斯定理

贝叶斯定理实际上就是计算“条件概率”的公式。“条件概率”就是指在事件B发生的情况下,事件A发生的概率,用 $P(A|B)$ 表示

bayes

由文氏图看出 事件A事件B 发生的前提下发生的概率为:把 事件B 看做全概率,事件A 发生的概率,也就有如下式:

​ $ P(A|B) = \frac{P(A\cap B))}{P(B))} $ ①

因此有

​ $ P(A\cap B) = P(A|B)P(B) $ ②

同理

​ $P(B|A) = \frac {P(A\cap B)} {P(B)} $ ③

把②式带入③中,在 事件A事件B 相互独立的前提下于是就有了:

​ $$ P(A|B) = \frac {P(B|A)P(A)} {P(B)} $$

事件A事件B相互独立,指的是,事件A事件B 发生的概率不会相互影响。举个例子: 事件A 发生的概率,不会因为 事件B 发生而变大,反之也成立。

基于朴素贝叶斯的分类算法

在看完这个公式的时候,我也没有意识到这一公式的具体使用,直到我使用 朴素贝叶斯 实现了一个分类器才算了解到它的精妙之处。所以请耐心看完示例,一定会有豁然开朗的感觉。

1.数据集

使用红酒数据集 做分类 demo。不过数据集比较小,预测结果的波动比较大,实际应用中应该换更大的数据集做实验。

数据集分为14列,第一列表示红酒的种类,第二 ~ 十四列表示红酒中各种成分含量(显然,红酒的种类和酒中各种成分含量有一定关系,而且可以结合酒中各成分的含量大致预估出是哪种葡萄酒)。

2.算法步骤

假设用 $P(C_{i})$ 表示各种类葡萄酒的占比

$P(A_{i}|C_{i})$ 表示确认为 $C_{i}$ 类葡萄酒后,$A_{I}$ 成分的占比(其实是一条件概率)

$ P(A_{i}) $ 表示$A_{I}$ 成分占比。

① 将数据集分为训练集和测试集

② 统计训练集中各种葡萄酒的占比(实际上计算的是 $P(C_{i})$)

③ 对酒中13种成分做归一化处理(酒中成分是连续的数字,为了好处理,需要根据酒中成分含量大小划分等级,具体划分办法是:每种成分最大值 - 最小值之差除以划分的等级数。当然还有其它划分方式)

④ 计算每个分类下,每个属性程度的概率(属性程度是指,那十三个属性分成了若干等分,这其实计算的是$P(A_{I}|C_{I})$ 。 举个例子:当确认为种类1的葡萄酒,$A1$成分的第一等占比多少,第二等占比多少...,$A2$成分的第一等占比多少,...)

⑤ 计算各个属性各等级的占比情况

⑥ 经过上述步骤,模型就算训练完成了,其实训练过程就是一个统计的过程

⑦ 测试准确性,针对测试集中的数据,根据红酒中各种成分含量分别预测各个红酒的可能性,取概率最大的就可以了。

3.梳理

再重复一下贝叶斯公式:

​ $$ P(A|B) = \frac {P(B|A)P(A)} {P(B)} $$

训练过程

训练过程实际就是通过对训练集的各种统计,求 $P(B|A)$ 、$P(A)$ 、 $P(B)$ 的过程,而分类实际是求 $P(A|B)$ 的过程。

需要注意的是,朴素贝叶斯分类器的使用前提是——属性条件独立性,即:每个属性独立的对分类结果产生影响。

python 实现过程

#!/usr/bin/env python3.7
# -*- encoding=utf-8 -*-
import random

"""
朴素贝叶斯——3种红酒分类

数据集:
    第 1    列:红酒分类(1,2,3)三种分类
    第 2~14 列:分别表示13中成分(Alcohol,Malicacid,Ash,Alcalinity of ash,Magnesium,Total phenols,Flavanoids,Nonflavanoid phenols,Proanthocyanins,Color intensity,Hue,OD280/OD315 of diluted wines,Proline
"""

if __name__ == '__main__':
    dataSet = set()
    dataSetLen = 0              # 数据总数
    trainingSet = dict()        # 训练集
    testSet = dict()            # 测试集
    priorProbability = dict()   # 先验概率计算结果
    attrRange = dict()          # 属性范围
    normTrainingSet = dict()    # 归一化之后的训练集
    attrRangeProb = dict()      # 每个属性条件概率
    attrProb = dict()           # 每个属性发生的概率

    # 读取数据集
    with open('./wine.data', 'r') as fr:
        for line in fr.readlines():
            line = line.strip()
            dataSet.add(line)
    dataSetLen = len(dataSet)
    print('数据集:' + str(dataSetLen), end=',\t')

    # 随机拆分数据集
    for i in range(len(dataSet)):
        if random.randint(0, 10) <= 7:
            trainingSet[len(trainingSet)] = dataSet.pop().split(',')
        else:
            testSet[len(testSet)] = dataSet.pop().split(',')
    print('训练集:' + str(len(trainingSet)), end=',\t')
    print('测试集:' + str(len(testSet)), end='\n\n')

    # 计算先验概率
    for ik, iv in trainingSet.items():
        cateName = 'cate' + iv[0]
        if cateName in priorProbability:
            priorProbability[cateName] += 1.0
        else:
            priorProbability[cateName] = 1.0
    for ik, iv in priorProbability.items():
        priorProbability[ik] = (priorProbability[ik], priorProbability[ik] / len(trainingSet))

    # 输出先验概率
    for ik, iv in priorProbability.items():
        print(ik + '\t' + str(iv))
    print('')

    # 分析各个属性范围
    for ik, iv in trainingSet.items():
        for i in range(1, len(iv)):
            attrName = 'attr' + str(i)
            tmp = float(iv[i])
            if attrName in attrRange:
                maxt, mint = attrRange[attrName]
                if maxt <= tmp:
                    maxt = tmp
                if mint >= tmp:
                    mint = tmp
                attrRange[attrName] = (maxt, mint)
            else:
                attrRange[attrName] = (tmp, tmp)

    # 查看每个属性的范围
    for ik, iv in attrRange.items():
        print(str(ik) + '\t\t' + str(iv[1]) + ' ~ ' + str(iv[0]))
    print('')

    # 归一化每个属性,并将每个属性分为 四档,并划分档次
    for ik, iv in trainingSet.items():
        norm = list()
        norm.append(iv[0])                                              # 分类结果
        for i in range(1, len(iv)):
            attrName = 'attr' + str(i)
            tmp = float(iv[i])
            maxt, mint = attrRange[attrName]
            stept = (maxt - mint) / 4
            for j in range(1, 5):
                if (tmp >= mint) and (tmp <= stept * j + mint):
                    tmp = j
                    break
            norm.append(tmp)
        normTrainingSet[ik] = norm

    # 打印归一化后的数据
    for ik, iv in normTrainingSet.items():
        print(ik, end=',')
        for i in iv:
            print(i, end=',')
        print('')
    print('')

    # 每个属性发生的条件概率
    for ik, iv in normTrainingSet.items():
        for i in range(1, len(iv)):
            attrName = 'attr' + str(i)
            attrResult = attrName + '|' + iv[0]
            attrValue = iv[i]
            v1 = 0.0
            v2 = 0.0
            v3 = 0.0
            v4 = 0.0
            if attrValue == 4:
                v4 = 1.0
            elif attrValue == 3:
                v3 = 1.0
            elif attrValue == 2:
                v2 = 1.0
            elif attrValue == 1:
                v1 = 1.0
            if attrName in attrProb:
                v1t, v2t, v3t, v4t = attrProb[attrName]
                attrProb[attrName] = (v1 + v1t, v2 + v2t, v3 + v3t, v4 + v4t)
            else:
                attrProb[attrName] = (v1, v2, v3, v4)
            if attrResult in attrRangeProb:
                v1t, v2t, v3t, v4t = attrRangeProb[attrResult]
                attrRangeProb[attrResult] = (v1 + v1t, v2 + v2t, v3 + v3t, v4 + v4t)
            else:
                attrRangeProb[attrResult] = (v1, v2, v3, v4)

    # 计算因子
    for ik, iv in attrRangeProb.items():
        arr = ik.split('|')
        attrName = arr[0]
        cate = 'cate' + str(arr[1])
        cateNum = priorProbability[cate][0]
        v1, v2, v3, v4 = iv
        attrRangeProb[ik] = (float(v1)/cateNum, float(v2)/cateNum, float(v3)/cateNum, float(v4)/cateNum)

    for ik, iv in attrRangeProb.items():
        print(ik, end='\t')
        for i in iv:
            print(str(i), end=',')
        print('')

    for ik, iv in attrProb.items():
        print(ik, end='\t')
        for i in iv:
            print(str(i), end=',')
        print('')

    # 测试训练结果
    right = 0
    wrong = 0
    total = len(testSet)
    for ik, iv in testSet.items():
        cate1 = 0.0
        cate2 = 0.0
        cate3 = 0.0
        trueCate = iv[0]
        calcCate = ''
        maxCate1 = 0.0
        maxCate2 = 0.0
        maxCate3 = 0.0
        for i in range(1, len(iv)):
            # 属性归一化
            attr = float(iv[i])
            attrName = 'attr' + str(i)
            maxt, mint = attrRange[attrName]
            stept = float(maxt - mint) / 4
            norm = 0
            if attr < float(mint) + stept * 1:
                norm = 1
            elif attr < float(mint) + stept * 2:
                norm = 2
            elif attr < float(mint) + stept * 3:
                norm = 3
            elif attr >= float(mint) + stept * 3:
                norm = 4
            # 该属性的概率
            attrP = attrProb[attrName][norm - 1]
            # 该属性下类别的概率
            for j, jv in priorProbability.items():
                tf, gl = jv                                                     # gl 表示该分类结果的先验概率
                res = j.replace('cate', '')                                     # 获得结果名
                a1t, a2t, a3t, a4t = attrRangeProb[attrName + '|' + res]        # 条件概率p(b|c)
                if norm == 1:
                    at = a1t
                elif norm == 2:
                    at = a2t
                elif norm == 3:
                    at = a3t
                else:
                    at = a4t
                cateRes = at * gl / attrP
                if res == '1' and maxCate1 <= cateRes:
                    maxCate1 = cateRes
                elif res == '2' and maxCate2 <= cateRes:
                    maxCate2 = cateRes
                elif res == '3' and maxCate3 <= cateRes:
                    maxCate3 = cateRes
        mm = max(maxCate1, maxCate2, maxCate3)
        if mm == maxCate1:
            calcCate = '1'
        elif mm == maxCate2:
            calcCate = '2'
        elif mm == maxCate3:
            calcCate = '3'
        if calcCate == trueCate:
            right += 1
        else:
            wrong += 1

    # 计算预测的正确率
    print()
    print("数据集总数:" + str(dataSetLen))
    print("训练集:" + str(len(trainingSet)))
    print("测试集:" + str(len(testSet)))
    print("正确率:" + str(float(right) / len(testSet)))
    print("错误率:" + str(float(wrong) / len(testSet)))

    exit(0)

最后

本着分享快乐的原则,如果有错误请提出来,一起学习,一起进步!

文章目录