贝叶斯分类器


核心思想

贝叶斯决策理论的核心思想,即选择具有最高概率的决策。

背景:假定p1(x,y)表示点(x,y)属于类别1的概率,p2(x,y)表示点(x,y)
属于类别2的概率,那么对于一个新数据点(x,y),可以采用下面的规则来判断它的类别:

  • 若p1(x,y)>p2(x,y),那么类别为1;
  • 若p1(x,y)<p2(x,y),那么类别为2.

理论基础

“属性条件独立性假设”:对于已知类别,假定所有属性相互独立,换言之,假设
每个属性独立地对分类结果发生影响。这也是‘朴素’的来源。
另一层意思是:每个属性同等重要。

基于属性条件独立性假设,贝叶斯公式可以改写为:

\[{\rm{P}}(c|{\bf{x}}) = \frac{{P(c)P({\bf{x}}|c)}}{{p({\bf{x}})}} = \frac{{p(c)}}{{p({\bf{x}})}}\prod\limits_{i = 1}^d {P({x_i}|c)} \]

其中d为属性数目,\(x_i\)为x在第i个属性上的取值。

由于对所有类别来说P(x)相同,因此基于贝叶斯准则有

\[{h_{NB}}({\bf{x}}) = \mathop {\arg \max }\limits_{c \in \gamma } P(c)\prod\limits_{i = 1}^d {P({x_i}|c)} \]

朴素贝叶斯分类器的训练过程就是基于训练集D来估计类先验概率p(c),并为每个属性估计条件概率\(P(x_i|c)\).

  • 离散属性:条件概率计算:\(p({x_i}|c) = \frac{{\left| {{D_{c,{x_i}}}} \right|}}{{|{D_c}|}}\).
    \(D_{c,x_i}\)表示\(D_c\)中在第\(i\)个属性上取值为\(x_i\)的样本组成的集合。

  • 连续属性:考虑概率密度函数,假定\(p({x_i}|c) \sim {\rm N}({\mu _{c,i}},\sigma _{c,i}^2)\),其中\(\mu _{c,i},\sigma _{c,i}^2\)分别是c类样本在第\(i\)个属性上取值的均值和标准差,则有

\[p({x_i}|c) = \frac{1}{{\sqrt {2\pi } {\sigma _{c,i}}}}\exp ( - \frac{{{{({x_i} - {\mu _{c,i}})}^2}}}{{2\sigma _{c,i}^2}}) \]

当某个属性值在训练集中没有与某个类同时出现过,将使得\(P(x_i|c)=0\),使得后续的判别出现问题。所以为了避免其它属性携带的信息被训练集中未出现的属性值'抹去',在估计概率值时通常要进行'平滑'。

针对P(x_i|c)计算的不同,朴素贝叶斯分类器出现了不同的变体


1. 自己动手算

以决策树中的'贷款数据表为例',对下列样本进行预测:

Age 	Work 	House 	Loan 	Class
中年 	否 	是 	一般 	?

import pandas as pd
DATA=pd.read_excel(r'F:\Python_processing\Python_Jupyter脚本集\机器学习\loan.xlsx')
#1.首先待预测样本不再已知样本中,年龄是‘中年’
DATA[DATA['Age']=='中年']
#2. 计算P(c):即分别计算class为是和否的概率
Age Work House Loan Class
5 中年 一般
6 中年
7 中年
8 中年 非常好
9 中年 非常好
def getResults(dataframe):
    p_c_dict=dataframe.value_counts()
    p_c=dict([ (c_i,round(c_value/p_c_dict.values.sum(),3)) for c_i,c_value in zip(p_c_dict.index,p_c_dict.values)])
    return p_c 
#特征

columns=[i for i in DATA.columns[:-1]]
data1=DATA[DATA['Class']=='是'][columns]
data2=DATA[DATA['Class']=='否'][columns]
print('class # 是')
for i in columns:
    #划分数据
    temp_data=data1[i]
    #开始统计结果
    print(i,getResults(temp_data))
    
print(''.center(50,"="))
    
print('class # 否')
for i in columns:
    #划分数据
    temp_data=data2[i]
    #开始统计结果
    print(i,getResults(temp_data))

        

输出结果:
class # 是
Age {'老年': 0.444, '中年': 0.333, '青年': 0.222}
Work {'是': 0.556, '否': 0.444}
House {'是': 0.667, '否': 0.333}
Loan {'好': 0.444, '非常好': 0.444, '一般': 0.111}
==================================================
class # 否
Age {'青年': 0.5, '中年': 0.333, '老年': 0.167}
Work {'否': 1.0}
House {'否': 1.0}
Loan

p_c=getResults(DATA['Class'])
p_c

{'是': 0.6, '否': 0.4}

  • class:是

\[p(class=是)*p(中年|class=是)*p(Work=否|class=是)*p(House=是|class=是)*p(loan=一般|class=是) =0.6 * 0.333* 0.444*0.667*0.111=0.006567911114400001\]

  • class:否

\[p(class=否)*p(中年|class=否)*p(Work=否|class=否)*p(House=是|class=否)*p(loan=一般|class=否) =0.4 * 0.333* 1.0*0*0.667=0\]

所以预测该person的贷款类型为 “是”.

(该样本是基于person 9修改loan后得到,根据决策树节的分支可知,loan在众多属性特征重要性中属于最后,所以上述预测结果可以接受!!)

关于连续变量的案例,见周志华《机器学习》。


2. 调用Sklearn库

Sklearn.naive_bayes官方帮助文档

高斯朴素贝叶斯

假设\(p(x_i|c)或p(x_i|y)\)服从高斯分布。

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import GaussianNB

X,y=load_iris(return_X_y=True)
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.5,random_state=0)
gnb=GaussianNB().fit(X_train,y_train)
y_pred=gnb.predict(X_test)
print("Number of mislabeled points out of a total %d points:%d"
     %(X_test.shape[0],(y_test!=y_pred).sum()))

Number of mislabeled points out of a total 75 points:4

from sklearn.metrics import confusion_matrix

confusion_matrix(y_test,y_pred)

array([[21, 0, 0],
[ 0, 30, 0],
[ 0, 4, 20]], dtype=int64)

多项式朴素贝叶斯

MultinomialNB实现了对多个分布数据的朴素贝叶斯算法,是文本分类中使用的两个经典朴素Bayes变体之一(其中数据通常表示为字向量计数,尽管TF-ID向量在实践中也很好地工作)。

\(p(x_i|c)或p(x_i|y)\)采用平滑处理:$$p({x_i}|c) = \frac{{|{D_{ci}}| + \alpha }}{{|{D_c}| + \alpha *n}}$$

\(n\)表示特征数量;\(\alpha>=0\);。

from sklearn.naive_bayes import MultinomialNB
import numpy as np
rng = np.random.RandomState(1)
X = rng.randint(5, size=(6, 100))
y = np.array([1, 2, 3, 4, 5, 6])
from sklearn.naive_bayes import MultinomialNB
clf = MultinomialNB()
clf.fit(X, y)
print(clf.predict(X[2:3]))

[3]

补码朴素贝叶斯

补码朴素贝叶斯(ComplementNB,cNB)是标准多项式朴素贝叶斯(MNB)算法的一种自适应算法,特别适用于不平衡的数据集。
具体内容见论文:Tackling the Poor Assumptions of Naive Bayes Text Classifiers

论文提出(多项式)朴素贝叶斯自身的系统错误以及在文本分类上的缺陷:

  • 在有偏数据集上,权重的决策边界偏向样本数较多的类。
  • 朴素贝叶斯对不符合独立性假设的(特征所在的)类影响较大。

多项式朴素贝叶斯
假定有固定的类\(c \in \{ 1,2,...,m\}\),每一个对应预定的多项式参数集合,类\(c\)对应的参数为:
\({{\bf{\theta }}_c} = \{ {\theta _{c1}},{\theta _{c2}},...,{\theta _{cn}}\}\),其中\(n\)代表词袋大小,
\(\sum\nolimits_i {{\theta _{ci}} = 1,} {\theta _{ci}}\)表示单词\(i\)在类\(c\)中出现的概率。文本分类中文档的似然函数可以
表示为$$p(d|{{\bf{\theta }}_c}) = \frac{{(\sum\nolimits_i {{f_i}} )!}}{{\prod\limits_i {{f_i}} }}\prod\limits_i {{{({\theta _{ci}})}^{{f_i}}}} $$
\(f_i\)表示文档\(d\)中单词\(i\)出现的次数。
多项式朴素贝叶斯分布的目标优化函数为:

\[\begin{array}{c} l(d) = \mathop {\arg \max }\limits_c [\log p({{\bf{\theta }}_c}) + \sum\limits_i {{f_i}\log {\theta _{ci}}} ] \\ = \mathop {\arg \max }\limits_c [{b_c} + \sum\limits_i {{f_i}{w_{ci}}} ] \\ \end{array}\]

\(b_c\)是阈值项(中括号里面第一项是个常数),\(w_{ci}\)是单词\(i\)在类\(c\)中的权重。
这些值是决策边界的自然参数。 对于二进制分类,这尤其容易看到,其中通过将正类参数和负类参数之间的差设置为零来定义边界,

\[({b_ + } - {b_ - }) + \sum\limits_i {{f_i}({w_{ + i}} - {w_{ - i}}) = 0} \]

基于多项式分布于狄利克雷分布共轭,可以估计出参数\(\theta_{ci}\),

\[{\mathop \theta^\^_{ci}} = \frac{{{N_{ci}} + {\alpha _i}}}{{{N_c} + \alpha }} \]

从而可以得到多项式NB的优化条件:

\[{l_{MNB}}(d) = \mathop {\arg \max }\limits_c [\log \mathop {p({\theta _c})}\limits^\^ + \sum\limits_i {{f_i}\log (\frac{{{N_{ci}} + {\alpha _i}}}{{{N_c} + \alpha }})} ] \]

\(\alpha = \sum\limits_i {{\alpha _i}} ,\alpha_i\)表示一种先验
为了简单,使用均匀分布作为先验,多项式NB的决策边界转为为对下列式子的估计:

\[\mathop {{w_{ci}}}\limits^\^ = \log \mathop {{\theta _{ci}}}\limits^\^ \]

Complement NB
针对在有偏数据集上,权重的决策边界偏向样本数较多的类的问题。论文
提出了Complement Naive Bayes(CNB),与MNB不同的是,CNB的估计为:

\[\mathop {{\theta _{^ \~ ci}}}\limits^\^ = \frac{{{N_{_{^ \~ ci}}} + {\alpha _i}}}{{{N_{^ \~ c}} + \alpha }} \]

CNB的优化目标为:

\[{l_{CNB}}(d) = \mathop {\arg \max }\limits_c [\log p({{\bf{\theta }}_c}) - \sum\limits_i {{f_i}\frac{{{N_{^ \~ ci}} + {\alpha _i}}}{{{N_{^ \~ c}} + \alpha }}} ] \]

\({N_{^ \~ c}}\)表示除过\(c\)以外的类出现的次数。

注:
负号表示我们要分配给与complement参数估计值不匹配的c类文档的事实。

这个主要应用在文本分类上,论文提出了具体的处理方案:

伯努利朴素贝叶斯

BernoulliNB实现了根据多元Bernoulli分布分布的数据的朴素贝叶斯训练和分类算法,即可能有多个特征,但每一个特征都被假定为一个二元值(Bernoulli,boole)变量。因此,这个类需要将样本表示为二进制值的特征向量;如果传递任何其他类型的数据,则BernoulliNB实例可以对其输入进行二进制化(取决于binarize参数)。

Bernoulli朴素贝叶斯的决策规则是基于

\[p({x_i}|y) = p(i|y){x_i} + (1 - p(i|y))(1 - {x_i}) \]

关于\(p(i|y)\)的理解:
它不同于多项式NB的规则,因为它明确地惩罚一个特征i的不出现,它是y类的指示,其中多项变量会简单地忽略一个未发生的特征。

它与多项NB规则不同之处在于它明确地惩罚了一个特征的不出现。这是班级的指标,其中多项式变体会忽略一个未发生的特性。

在文本分类的情况下,可以使用单词出现向量(而不是字数向量)来训练和使用该分类器。BernoulliNB在某些数据集上,特别是那些文档较短的数据集中,性能可能会更好。如果时间允许,最好对这两种模式进行评估。

Categorical Naive Bayes(类朴素贝叶斯)

CategoricalNB对分类分布的数据实施分类朴素贝叶斯算法。 它假定由索引描述的每个特征都有其自己的分类分布。

对于训练集中的每个特征 \(X\),CategoricalNB估计以类y为条件的X的每个特征i的分类分布。 样本的索引集定义为\(J={1,...,m}\)\(m\)作为样本数。

给定c类的特征i中t类(属性值)的概率估计为:$$p({x_i}{\rm{ = }}t|y = c;\alpha ) = \frac{{{N_{tic}} + \alpha }}{{{N_c} + \alpha {n_i}}}$$

\(N_{tic} = |\{j \in J \mid x_{ij} = t, y_j = c\}|\)是属性值\(t\)出现特征\(x_i\),样本,属于属于'c'的次数;

\(N_{c} = |\{ j \in J\mid y_j = c\}|\) 属于类c的样本数,\(n_i\)是可用的特征数。

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import CategoricalNB

X,y=load_iris(return_X_y=True)
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.5,random_state=0)
cat=CategoricalNB().fit(X_train,y_train)
y_pred=cat.predict(X_test)
print("Number of mislabeled points out of a total %d points:%d"
     %(X_test.shape[0],(y_test!=y_pred).sum()))

Number of mislabeled points out of a total 75 points:7

#列名
column=DATA.columns
##House后面有空格
DATA.columns=[i.strip() for i in column]
column=DATA.columns
column

Index(['Age', 'Work', 'House', 'Loan', 'Class'], dtype='object')

from sklearn.preprocessing import OrdinalEncoder
ord_encoder=OrdinalEncoder().fit(DATA[column[:-1]])
ord_encoder2=OrdinalEncoder().fit(DATA[['Class']])
ord_encoder.categories_

[array(['中年', '老年', '青年'], dtype=object),
array(['否', '是'], dtype=object),
array(['否', '是'], dtype=object),
array(['一般', '好', '非常好'], dtype=object)]

X=pd.DataFrame(ord_encoder.transform(DATA[column[:-1]]),columns=column[:-1])
y=pd.DataFrame(ord_encoder2.transform(DATA[['Class']]),columns=["Class"])
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.5,random_state=0)
cat2=CategoricalNB().fit(X_train,y_train)
y_pred=cat2.predict(X_test)
y_pred

array([0., 0., 1., 1., 0., 0., 1., 1.])

mat=confusion_matrix(y_test,y_pred)
mat

array([[4, 0],
[0, 4]], dtype=int64)

print("准确率:%3.2f"%(
    (np.eye(2)*mat).sum()/mat.sum()*100))

准确率:100.00

#对上面动手算的结果进行验证
raw_test=['中年','否','是','一般']
test=ord_encoder.transform([raw_test])
cat2.predict(test)

array([1.])

#对上面的预测结果进行还原
a=ord_encoder2.inverse_transform([cat2.predict(test)])
print("该样本%s的class #%s"%(raw_test,a.flatten().tolist()[0]))

该样本['中年', '否', '是', '一般']的class #是


参考文献:

  1. 周志华《机器学习》
  2. 李航《统计学习方法》
  3. 《机器学习实战》
posted @ 2020-05-11 20:52  LgRun  阅读(948)  评论(0编辑  收藏  举报