机器学习:详解半朴素贝叶斯分类AODE原理(附Python实现)
Posted Mr.Winter`
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了机器学习:详解半朴素贝叶斯分类AODE原理(附Python实现)相关的知识,希望对你有一定的参考价值。
目录
0 写在前面
机器学习强基计划聚焦深度和广度,加深对机器学习模型的理解与应用。“深”在详细推导算法模型背后的数学原理;“广”在分析多个机器学习模型:决策树、支持向量机、贝叶斯与马尔科夫决策、强化学习等。
1 独依赖假设
在机器学习强基计划4-3:详解朴素贝叶斯分类原理 | 例题分析 | Python实现中我们介绍了朴素贝叶斯之所以“朴素”,是因为其给定了很强的属性独立性假设。然而,属性独立性假设在实际上很难成立,因此引入半朴素贝叶斯分类器(Semi-Naïve Bayes Classifier),其核心思想是:适当考虑部分属性的相互依赖,从而既简化了联合概率计算,又不至于彻底忽略属性间的强依赖关系。
半朴素贝叶斯分类器最常见的建模策略是独依赖估计(One-Dependent Estimator, ODE),即假设每个属性在类别外最多依赖于一个属性
f ∗ ( x ) = a r g max C ∈ Y P ( C ) ∏ i = 1 d P ( x i ∣ C , p a i ) f^*\\left( \\boldsymbolx \\right) =\\undersetC\\in \\mathcalY\\mathrmarg\\maxP\\left( C \\right) \\prod_i=1^dP\\left( x_i|C, pa_i \\right) f∗(x)=C∈YargmaxP(C)i=1∏dP(xi∣C,pai)
其中 p a i pa_i pai为属性 x i x_i xi所依赖的父属性。若对 ∀ x i \\forall x_i ∀xi确定了其 p a i pa_i pai,则可按朴素贝叶斯的方式进行贝叶斯分类,因此问题的核心转换为如何确定 p a i pa_i pai。
另一个问题是,可以假设属性依赖多个父属性吗?答案是:高阶依赖估计的准确性要求训练样本随指数级增加,在有限样本条件下,一般不适合采用。
2 AODE原理
先介绍一个比较直接的想法——假设所有属性都依赖于同一个父属性,称该属性为超父(super-parent),这种半朴素贝叶斯分类器称为SPODE(Super-Parent ODE)算法。
f ∗ ( x ) = a r g max C ∈ Y P ( C ) ∏ i = 1 d P ( x i ∣ C , p a ) f^*\\left( \\boldsymbolx \\right) =\\undersetC\\in \\mathcalY\\mathrmarg\\maxP\\left( C \\right) \\prod_i=1^dP\\left( x_i|C, pa \\right) f∗(x)=C∈YargmaxP(C)i=1∏dP(xi∣C,pa)
建立在SPODE的基础上,AODE(Averaged ODE)算法是一种基于集成学习机制、更为强大的ODE分类器,其将每个属性作为超父构造SPODE,再加权计算各属性间的平均依赖,即
f ∗ ( x ) = a r g max C ∈ Y ∑ i = 1 , ∣ D x i ∣ ⩾ m d P ( C , x i ) ∏ j = 1 d P ( x j ∣ C , x i ) f^*\\left( \\boldsymbolx \\right) =\\undersetC\\in \\mathcalY\\mathrmarg\\max\\sum_i=1,|\\boldsymbolD_x_i|\\geqslant m^dP\\left( C,x_i \\right) \\prod_j=1^dP\\left( x_j|C, x_i \\right) f∗(x)=C∈Yargmaxi=1,∣Dxi∣⩾m∑dP(C,xi)j=1∏dP(xj∣C,xi)
其中 D x i \\boldsymbolD_x_i Dxi为第 i i i属性上取值为 x i x_i xi的样本子集, m m m默认设为30。类似地,AODE的拉普拉斯平滑修正为
P ( C , x i ) = ∣ D C , x i ∣ + 1 ∣ D ∣ + N × N i P ( x j ∣ C , x i ) = ∣ D C , x i , x j ∣ + 1 ∣ D C , x i ∣ + N j \\begincases P\\left( C,x_i \\right) =\\frac|\\boldsymbolD_C,x_i|+1|\\boldsymbolD|+N\\times N_i\\\\ P\\left( x_j|C,x_i \\right) =\\frac|\\boldsymbolD_C,x_i,x_j|+1|\\boldsymbolD_C,x_i|+N_j\\\\\\endcases ⎩ ⎨ ⎧P(C,xi)=∣D∣+N×Ni∣DC,xi∣+1P(xj∣C,xi)=∣DC,xi∣+Nj∣DC,xi,xj∣+1
简单说,AODE就是SPODE的加权平均版本
接下来基于上述原理开始编程,并和朴素贝叶斯分类做个比较,看性能有没提升
3 Python实现
3.1 计算类先验概率
'''
* @breif: 计算类先验概率P(C, xi)
* @param[in]: None
* @retval: None
C1:
超父属性(只能是离散属性)
pa1:
超父属性值
px1:
p: p(C1, x1)
N: n(C1, x1)
...
pxn: ...
num(pa1): int 属性a1的可取值数
...
pan: ...
...
Cn: ...
num: 类别数
'''
def calPrior(self):
# 可选的类别数
label = np.unique(self.y)
self.prior['num'] = len(label)
# 计算先验概率
for _label in label:
self.prior[_label] =
# 获取标签取值_label的样本集
labelIndex = np.squeeze(np.argwhere(np.squeeze(self.y)==_label))
labelX = self.X[:, labelIndex]
# 超父特征层
for i in range(self.d):
# 属性i的可选属性值列表
attr = np.unique(self.X[i, :])
# 可选属性数
attrNum = len(attr)
# 离散属性(只有离散属性能作为超父属性)
if attrNum <= 0.85 * self.m:
self.prior[_label][str(i)] =
self.prior[_label][str(i)]['num'] = attrNum
# 计算每个取值的联合先验概率
for a in attr:
self.prior[_label][str(i)][a] =
n = int(sum(labelX[i, :] == a))
self.prior[_label][str(i)][a]['p'] = (n + self.laplace) / (self.m + self.prior['num'] * attrNum)
self.prior[_label][str(i)][a]['N'] = n
3.2 计算属性后验概率
'''
* @breif: 计算属性后验概率P(xj|C, xi)
* @param[in]: None
* @retval: None
C1:
超父属性(只能是离散属性)
pa1:
超父属性值
px1:
常规属性
a1:
type: discrete 离散属性
x1: p(x1)
...
xn: p(xn)
num(a1): int 属性b1的可取值数
a2:
type: continous 连续属性
mean: 样本均值
std: 标准差
...
pxn: ...
...
pan: ...
...
Cn: ...
num: 类别数
'''
def calPosterior(self):
if not self.prior:
raise ValueError("please calculate prior first!")
# 以上是关于机器学习:详解半朴素贝叶斯分类AODE原理(附Python实现)的主要内容,如果未能解决你的问题,请参考以下文章
太赞了!机器学习基础核心算法:贝叶斯分类!(附西瓜书案例及代码实现)