Machine Learning in Action - Naive Bayes

Posted by Qing on April 24, 2018

概述

朴素贝叶斯(Naive Bayes)是基于贝叶斯公式与特征条件独立的建设,估计后验概率,根据后验概率进行分类的监督学习方法。
对于给定的训练数据集,首先基于特征条件独立的假设学习输入/输出的联合概率分布;然后基于此模型,对给定的输入,利用贝叶斯公式求出后验概率最大的输出

原理

贝叶斯定理

其中,是先验概率;$$P(x c)P(x)xP(x)P(c x)P(c)P(x c)$$。

朴素贝叶斯

类先验概率表达了样本占空间中各类样本所占的比例,根据大数定理,当训练集中包含充足的独立同分布样本时,可以通过各类样本的频率进行估计。这里其实是极大似然估计的思想。

对**类条件概率$$P(x c)xd2^d$$种可能的取值。很多样本取值在训练集中根本没有出现,直接用频率来估计是不可行的。为避开这个障碍,“朴素贝叶斯分类器”采用了“属性条件独立性假设”:对已知的类别,假设所有属性相互独立。换言之,假设每个属性独立地对分类结果产生影响。
基于条件独立性的假设,$$P(c x)$$可重写为:

其中为属性数目,在第个属性上的取值。

根据以上判别准则分别求出给定样本对每个分类的后验概率,其中最大的后验概率所对应的类即为该样本的分类。 显然,朴素贝叶斯分类器的训练过程就是基于训练集来估计类先验概率,并为每个属性估计条件概率

表示训练集中第类样本组成的集合,若有充足的独立同分布样本,则可容易地估计出类先验概率

对离散属性而言,令表示中在第个属性上取值为的样本组成的集合,则条件概率$$P(x_i c)$$可估计为
对连续属性可考虑概率密度函数,假定$$p(x_i c) \sim N(\mu_{c,i}, \sigma_{c,i}^2)\mu_{c,i}\sigma_{c,i}^2ci$$个属性上取值的均值和方差,则有

项目案例

这里使用周志华-机器学习里面的西瓜数据集-使用朴素贝叶斯分类器判断西瓜的好坏。 具体的数据集如下:

编号 色泽 根蒂 敲声 纹理 脐部 触感 密度 含糖率 好瓜
1 青绿 蜷缩 浊响 清晰 凹陷 硬滑 0.697 0.46
2 乌黑 蜷缩 沉闷 清晰 凹陷 硬滑 0.774 0.376
3 乌黑 蜷缩 浊响 清晰 凹陷 硬滑 0.634 0.264
4 青绿 蜷缩 沉闷 清晰 凹陷 硬滑 0.608 0.318
5 浅白 蜷缩 浊响 清晰 凹陷 硬滑 0.556 0.215
6 青绿 稍蜷 浊响 清晰 稍凹 软粘 0.403 0.237
7 乌黑 稍蜷 浊响 稍糊 稍凹 软粘 0.481 0.149
8 乌黑 稍蜷 浊响 清晰 稍凹 硬滑 0.437 0.211
9 乌黑 稍蜷 沉闷 稍糊 稍凹 硬滑 0.666 0.091
10 青绿 硬挺 清脆 清晰 平坦 软粘 0.243 0.267
11 浅白 硬挺 清脆 模糊 平坦 硬滑 0.245 0.057
12 浅白 蜷缩 浊响 模糊 平坦 软粘 0.343 0.099
13 青绿 稍蜷 浊响 稍糊 凹陷 硬滑 0.639 0.161
14 浅白 稍蜷 沉闷 稍糊 凹陷 硬滑 0.657 0.198
15 乌黑 稍蜷 浊响 清晰 稍凹 软粘 0.36 0.37
16 浅白 蜷缩 浊响 模糊 平坦 硬滑 0.593 0.042
17 青绿 蜷缩 沉闷 稍糊 稍凹 硬滑 0.719 0.103

根据色泽、根蒂、敲声、纹理、脐部、触感、密度、含糖率这些特征,对测试例1进行分类。

编号 色泽 根蒂 敲声 纹理 脐部 触感 密度 含糖率 好瓜
测1 青绿 蜷缩 浊响 清晰 凹陷 硬滑 0.697 0.46

首先估计类先验概率,显然有

然后,为每个属性类估计条件概率$$P(x_i c)$$:

于是,对测试例1有:

由于,因此,朴素贝叶斯分类器将测试样本例1判别为好瓜

纯手打代码

import numpy as np
from io import StringIO
import pandas as pd
import math

def createDataSet():
    ''' 数据读入 '''
    rawData = StringIO(
    """编号,色泽,根蒂,敲声,纹理,脐部,触感,密度,含糖率,好瓜
      1,青绿,蜷缩,浊响,清晰,凹陷,硬滑,0.697,0.46,是
      2,乌黑,蜷缩,沉闷,清晰,凹陷,硬滑,0.774,0.376,是
      3,乌黑,蜷缩,浊响,清晰,凹陷,硬滑,0.634,0.264,是
      4,青绿,蜷缩,沉闷,清晰,凹陷,硬滑,0.608,0.318,是
      5,浅白,蜷缩,浊响,清晰,凹陷,硬滑,0.556,0.215,是
      6,青绿,稍蜷,浊响,清晰,稍凹,软粘,0.403,0.237,是
      7,乌黑,稍蜷,浊响,稍糊,稍凹,软粘,0.481,0.149,是
      8,乌黑,稍蜷,浊响,清晰,稍凹,硬滑,0.437,0.211,是
      9,乌黑,稍蜷,沉闷,稍糊,稍凹,硬滑,0.666,0.091,否
      10,青绿,硬挺,清脆,清晰,平坦,软粘,0.243,0.267,否
      11,浅白,硬挺,清脆,模糊,平坦,硬滑,0.245,0.057,否
      12,浅白,蜷缩,浊响,模糊,平坦,软粘,0.343,0.099,否
      13,青绿,稍蜷,浊响,稍糊,凹陷,硬滑,0.639,0.161,否
      14,浅白,稍蜷,沉闷,稍糊,凹陷,硬滑,0.657,0.198,否
      15,乌黑,稍蜷,浊响,清晰,稍凹,软粘,0.36,0.37,否
      16,浅白,蜷缩,浊响,模糊,平坦,硬滑,0.593,0.042,否
      17,青绿,蜷缩,沉闷,稍糊,稍凹,硬滑,0.719,0.103,否
    """)

    df = pd.read_csv(rawData, sep=",")
    return df

df = createDataSet()

df.head()

def st_norm(x,uci,eci):
    return 1.0/(math.sqrt(2*math.pi)*eci)*math.e**(-(x - uci)**2/(2*eci**2))

def naive_bayes(data, feature):
    # 估计类先验概率
    N = len(data)
    res = {}
    P_pre = data['好瓜'].value_counts().apply(lambda x:'{0:.3f}'.format(x/N))
    res['好瓜'] = P_pre.values
    # 估计类条件概率
    for key, val in feature.items():
        if data[key].dtype.name == 'object':
            raw = data[data[key] == val]
            temp = raw.groupby('好瓜')[key].count().apply(lambda x:'{0:.3f}'.format(x/len(raw)))
            res[key] = temp.values
        else:
            raw = data
            temp = raw.groupby('好瓜')[key].apply(lambda x: st_norm(val ,x.mean(),x.std()))
            res[key] = temp.values
    # 输出最大概率分类
    df = pd.DataFrame(res, index=['好瓜:否', '好瓜:是'])
    return df.prod(axis=1).idxmax()
naive_bayes(df, {'色泽':'青绿', '根蒂':'蜷缩', '敲声':'浊响', '纹理':'清晰', '脐部':'凹陷', '触感':'硬滑', '密度':0.697, '含糖率':0.460})

sklearn实现代码

from sklearn.preprocessing import LabelEncoder
from sklearn import datasets
from sklearn.naive_bayes import GaussianNB
# re-encoding
for col in ['色泽', '根蒂', '敲声', '纹理', '脐部', '触感']:
    df[col] = LabelEncoder().fit_transform(df[col])

gnb = GaussianNB()
y_pred = gnb.fit(df[['色泽', '根蒂', '敲声', '纹理', '脐部', '触感', '密度', '含糖率']], df['好瓜']).predict(df[['色泽', '根蒂', '敲声', '纹理', '脐部', '触感', '密度', '含糖率']])
print('Number of mislabeled points out of a total {0} points : {1}'.format(len(y_pred), sum(y_pred != df['好瓜'])))
>> Number of mislabeled points out of a total 17 points : 3

拉普拉斯修正

注意,若某个属性值在训练集中没有与某个类同时出现过,则直接基于按频率估计概率,再进行判别会出现问题。例如在上述西瓜数据集训练朴素贝叶斯分类器时,对一个“敲声=清脆”的测试例,有

由于连乘之后计算出的概率为零,无论该样本的其他属性是什么,哪怕其他属性明显像好瓜,分类的结果都将是“好瓜=否”,这样显然不合理。

为了避免其他属性携带的信息被训练集中未出现的属性值“抹去”,在估计概率值时通常进行“平滑”,常用“拉普拉斯修正”。具体来说,令表示训练集中可能的类别数,表示第个属性可能的取值数,修正公式为:

例如,在上面的例子中,类先验概率可估计为

文章的代码都在machine_learning_in_action这个仓库里面。

参考文档

机器学习-周志华
第4章 基于概率论的分类方法:朴素贝叶斯
统计学习方法-李航