贝叶斯分类器
核心思想
贝叶斯决策理论的核心思想,即选择具有最高概率的决策。
背景:假定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.
理论基础
“属性条件独立性假设”:对于已知类别,假定所有属性相互独立,换言之,假设
每个属性独立地对分类结果发生影响。这也是‘朴素’的来源。
另一层意思是:每个属性同等重要。
基于属性条件独立性假设,贝叶斯公式可以改写为:
其中d为属性数目,\(x_i\)为x在第i个属性上的取值。
由于对所有类别来说P(x)相同,因此基于贝叶斯准则有
朴素贝叶斯分类器的训练过程就是基于训练集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)=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:是
- class:否
所以预测该person的贷款类型为 “是”.
(该样本是基于person 9修改loan后得到,根据决策树节的分支可知,loan
在众多属性特征重要性中属于最后,所以上述预测结果可以接受!!)
关于连续变量的案例,见周志华《机器学习》。
2. 调用Sklearn库
高斯朴素贝叶斯
假设\(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\)出现的次数。
多项式朴素贝叶斯分布的目标优化函数为:
\(b_c\)是阈值项(中括号里面第一项是个常数),\(w_{ci}\)是单词\(i\)在类\(c\)中的权重。
这些值是决策边界的自然参数。 对于二进制分类,这尤其容易看到,其中通过将正类参数和负类参数之间的差设置为零来定义边界,
基于多项式分布于狄利克雷分布共轭,可以估计出参数\(\theta_{ci}\),
从而可以得到多项式NB的优化条件:
\(\alpha = \sum\limits_i {{\alpha _i}} ,\alpha_i\)表示一种先验
为了简单,使用均匀分布作为先验,多项式NB的决策边界转为为对下列式子的估计:
Complement NB
针对在有偏数据集上,权重的决策边界偏向样本数较多的类的问题。论文
提出了Complement Naive Bayes(CNB),与MNB不同的是,CNB的估计为:
CNB的优化目标为:
\({N_{^ \~ c}}\)表示除过\(c\)以外的类出现的次数。
注:
负号表示我们要分配给与complement参数估计值不匹配的c类文档的事实。
这个主要应用在文本分类上,论文提出了具体的处理方案:
伯努利朴素贝叶斯
BernoulliNB实现了根据多元Bernoulli分布分布的数据的朴素贝叶斯训练和分类算法,即可能有多个特征,但每一个特征都被假定为一个二元值(Bernoulli,boole)变量。因此,这个类需要将样本表示为二进制值的特征向量;如果传递任何其他类型的数据,则BernoulliNB实例可以对其输入进行二进制化(取决于binarize参数)。
Bernoulli朴素贝叶斯的决策规则是基于
关于\(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 #是
参考文献:
- 周志华《机器学习》
- 李航《统计学习方法》
- 《机器学习实战》