0


机器学习:详解半朴素贝叶斯分类AODE原理(附Python实现)

目录

0 写在前面

机器学习强基计划聚焦深度和广度,加深对机器学习模型的理解与应用。“深”在详细推导算法模型背后的数学原理;“广”在分析多个机器学习模型:决策树、支持向量机、贝叶斯与马尔科夫决策、强化学习等。

🚀详情:机器学习强基计划(附几十种经典模型源码合集)


1 独依赖假设

在机器学习强基计划4-3:详解朴素贝叶斯分类原理 | 例题分析 | Python实现中我们介绍了朴素贝叶斯之所以“朴素”,是因为其给定了很强的属性独立性假设。然而,属性独立性假设在实际上很难成立,因此引入半朴素贝叶斯分类器(Semi-Naïve Bayes Classifier),其核心思想是:适当考虑部分属性的相互依赖,从而既简化了联合概率计算,又不至于彻底忽略属性间的强依赖关系

在这里插入图片描述

半朴素贝叶斯分类器最常见的建模策略是**独依赖估计(One-Dependent Estimator, ODE)**,即假设每个属性在类别外最多依赖于一个属性

  1. f
  2. (
  3. x
  4. )
  5. =
  6. a
  7. r
  8. g
  9. max
  10. C
  11. Y
  12. P
  13. (
  14. C
  15. )
  16. i
  17. =
  18. 1
  19. d
  20. P
  21. (
  22. x
  23. i
  24. C
  25. ,
  26. p
  27. a
  28. i
  29. )
  30. {f^*\left( \boldsymbol{x} \right) =\underset{C\in \mathcal{Y}}{\mathrm{arg}\max}P\left( C \right) \prod_{i=1}^d{P\left( x_i|C, pa_i \right)}}
  31. f∗(x)=CYargmaxP(C)i=1dP(xi​∣C,pai​)

其中

  1. p
  2. a
  3. i
  4. pa_i
  5. pai​为属性
  6. x
  7. i
  8. x_i
  9. xi​所依赖的父属性。若对
  10. x
  11. i
  12. \forall x_i
  13. xi​确定了其
  14. p
  15. a
  16. i
  17. pa_i
  18. pai​,则可按朴素贝叶斯的方式进行贝叶斯分类,因此问题的核心转换为如何确定
  19. p
  20. a
  21. i
  22. pa_i
  23. pai​。

另一个问题是,可以假设属性依赖多个父属性吗?答案是:高阶依赖估计的准确性要求训练样本随指数级增加,在有限样本条件下,一般不适合采用。

2 AODE原理

先介绍一个比较直接的想法——假设所有属性都依赖于同一个父属性,称该属性为**超父(super-parent),这种半朴素贝叶斯分类器称为SPODE(Super-Parent ODE)**算法。

  1. f
  2. (
  3. x
  4. )
  5. =
  6. a
  7. r
  8. g
  9. max
  10. C
  11. Y
  12. P
  13. (
  14. C
  15. )
  16. i
  17. =
  18. 1
  19. d
  20. P
  21. (
  22. x
  23. i
  24. C
  25. ,
  26. p
  27. a
  28. )
  29. f^*\left( \boldsymbol{x} \right) =\underset{C\in \mathcal{Y}}{\mathrm{arg}\max}P\left( C \right) \prod_{i=1}^d{P\left( x_i|C, pa \right)}
  30. f∗(x)=CYargmaxP(C)i=1dP(xi​∣C,pa)

建立在SPODE的基础上,**AODE(Averaged ODE)**算法是一种基于集成学习机制、更为强大的ODE分类器,其将每个属性作为超父构造SPODE,再加权计算各属性间的平均依赖,即

  1. f
  2. (
  3. x
  4. )
  5. =
  6. a
  7. r
  8. g
  9. max
  10. C
  11. Y
  12. i
  13. =
  14. 1
  15. ,
  16. D
  17. x
  18. i
  19. m
  20. d
  21. P
  22. (
  23. C
  24. ,
  25. x
  26. i
  27. )
  28. j
  29. =
  30. 1
  31. d
  32. P
  33. (
  34. x
  35. j
  36. C
  37. ,
  38. x
  39. i
  40. )
  41. f^*\left( \boldsymbol{x} \right) =\underset{C\in \mathcal{Y}}{\mathrm{arg}\max}\sum_{i=1,|\boldsymbol{D}_{x_i}|\geqslant m}^d{P\left( C,x_i \right) \prod_{j=1}^d{P\left( x_j|C, x_i \right)}}
  42. f∗(x)=CYargmaxi=1,∣Dxi​​∣⩾mdP(C,xi​)j=1dP(xj​∣C,xi​)

其中

  1. D
  2. x
  3. i
  4. \boldsymbol{D}_{x_i}
  5. Dxi​​为第
  6. i
  7. i
  8. i属性上取值为
  9. x
  10. i
  11. x_i
  12. xi​的样本子集,
  13. m
  14. m
  15. m默认设为30。类似地,AODE的拉普拉斯平滑修正为
  16. {
  17. P
  18. (
  19. C
  20. ,
  21. x
  22. i
  23. )
  24. =
  25. D
  26. C
  27. ,
  28. x
  29. i
  30. +
  31. 1
  32. D
  33. +
  34. N
  35. ×
  36. N
  37. i
  38. P
  39. (
  40. x
  41. j
  42. C
  43. ,
  44. x
  45. i
  46. )
  47. =
  48. D
  49. C
  50. ,
  51. x
  52. i
  53. ,
  54. x
  55. j
  56. +
  57. 1
  58. D
  59. C
  60. ,
  61. x
  62. i
  63. +
  64. N
  65. j
  66. \begin{cases} P\left( C,x_i \right) =\frac{|\boldsymbol{D}_{C,x_i}|+1}{|\boldsymbol{D}|+N\times N_i}\\ P\left( x_j|C,x_i \right) =\frac{|\boldsymbol{D}_{C,x_i,x_j}|+1}{|\boldsymbol{D}_{C,x_i}|+N_j}\\\end{cases}
  67. ⎩⎨⎧​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 计算类先验概率

  1. '''
  2. * @breif: 计算类先验概率P(C, xi)
  3. * @param[in]: None
  4. * @retval: None
  5. {
  6. C1: {
  7. 超父属性(只能是离散属性)
  8. pa1: {
  9. 超父属性值
  10. px1: {
  11. p: p(C1, x1)
  12. N: n(C1, x1)
  13. }
  14. ...
  15. pxn: ...
  16. num(pa1): int 属性a1的可取值数
  17. }
  18. ...
  19. pan: ...
  20. }
  21. ...
  22. Cn: ...
  23. num: 类别数
  24. }
  25. '''defcalPrior(self):# 可选的类别数
  26. label = np.unique(self.y)
  27. self.prior['num']=len(label)# 计算先验概率for _label in label:
  28. self.prior[_label]={}# 获取标签取值_label的样本集
  29. labelIndex = np.squeeze(np.argwhere(np.squeeze(self.y)==_label))
  30. labelX = self.X[:, labelIndex]# 超父特征层for i inrange(self.d):# 属性i的可选属性值列表
  31. attr = np.unique(self.X[i,:])# 可选属性数
  32. attrNum =len(attr)# 离散属性(只有离散属性能作为超父属性)if attrNum <=0.85* self.m:
  33. self.prior[_label][str(i)]={}
  34. self.prior[_label][str(i)]['num']= attrNum
  35. # 计算每个取值的联合先验概率for a in attr:
  36. self.prior[_label][str(i)][a]={}
  37. n =int(sum(labelX[i,:]== a))
  38. self.prior[_label][str(i)][a]['p']=(n + self.laplace)/(self.m + self.prior['num']* attrNum)
  39. self.prior[_label][str(i)][a]['N']= n

3.2 计算属性后验概率

  1. '''
  2. * @breif: 计算属性后验概率P(xj|C, xi)
  3. * @param[in]: None
  4. * @retval: None
  5. {
  6. C1: {
  7. 超父属性(只能是离散属性)
  8. pa1: {
  9. 超父属性值
  10. px1: {
  11. 常规属性
  12. a1: {
  13. type: discrete 离散属性
  14. x1: p(x1)
  15. ...
  16. xn: p(xn)
  17. num(a1): int 属性b1的可取值数
  18. }
  19. a2: {
  20. type: continous 连续属性
  21. mean: 样本均值
  22. std: 标准差
  23. }
  24. }
  25. ...
  26. pxn: ...
  27. }
  28. ...
  29. pan: ...
  30. }
  31. ...
  32. Cn: ...
  33. num: 类别数
  34. }
  35. '''defcalPosterior(self):ifnot self.prior:raise ValueError("please calculate prior first!")# 可选的类别数
  36. label = np.unique(self.y)
  37. self.posterior['num']=len(label)# 标签层for _label in label:
  38. self.posterior[_label]={}# 获取标签取值_label的样本集
  39. labelIndex = np.squeeze(np.argwhere(np.squeeze(self.y)==_label))
  40. labelX = self.X[:, labelIndex]# 超父特征层for pa, paDict in self.prior[_label].items():
  41. self.posterior[_label][pa]={}# 超父属性值层for paVal, paValDict in paDict.items():ifisinstance(paValDict,dict):
  42. self.posterior[_label][pa][paVal]={}# 常规属性层for i inrange(self.d):# 常规属性为超父属性则跳过if i == pa:continue# 获取超父属性值为paVal的样本子集
  43. paIndex = np.squeeze(np.argwhere(labelX[int(pa),:]==paVal))
  44. paLabelX = labelX[:, paIndex].reshape(self.d,-1)
  45. _, mpa = paLabelX.shape
  46. self.posterior[_label][pa][paVal][str(i)]={}# 属性i的可选属性值列表
  47. attr = np.unique(self.X[i,:])# 可选属性数
  48. attrNum =len(attr)# 离散属性if attrNum <=0.85* self.m:
  49. self.posterior[_label][pa][paVal][str(i)]['num']= attrNum
  50. self.posterior[_label][pa][paVal][str(i)]['type']='discrete'# 计算每个取值的后验概率for a in attr:
  51. n =int(sum(paLabelX[i,:]== a))
  52. self.posterior[_label][pa][paVal][str(i)][a]= \
  53. (n + self.laplace)/(self.prior[_label][pa][paVal]['N']+ attrNum)# 连续属性else:
  54. self.posterior[_label][pa][paVal][str(i)]['type']='continous'# 发生的概率足够大,才会产生足够的样本if mpa >1:
  55. self.posterior[_label][pa][paVal][str(i)]['std']= paLabelX[i,:].std(ddof=1)
  56. self.posterior[_label][pa][paVal][str(i)]['mean']= paLabelX[i,:].mean()else:
  57. self.posterior[_label][pa][paVal][str(i)]['std']= self.sigmaEpi
  58. self.posterior[_label][pa][paVal][str(i)]['mean']=0

3.3 预测

采用机器学习强基计划2-3:图文详解决策树预剪枝、后剪枝原理+Python实现的数据集训练,获得准确率如下

  1. model = AODE(X, y)# 训练模型
  2. model.train()# 模型预测
  3. predictY = model.predict(X)print("错误:", np.sum(predictY!=y.T),"个\n准确率为:", np.sum(predictY==y.T)/y.size)>>> 错误:0
  4. >>> 准确率为:1.0

而在机器学习强基计划4-3:详解朴素贝叶斯分类原理 | 例题分析 | Python实现中准确率只有83.2%,可见适当的依赖性假设有助于提高分类准确率

完整代码联系下方博主名片获取


🔥 更多精彩专栏

  • 《ROS从入门到精通》
  • 《机器人原理与技术》
  • 《机器学习强基计划》
  • 《计算机视觉教程》

👇源码获取 · 技术交流 · 抱团学习 · 咨询分享 请联系👇


本文转载自: https://blog.csdn.net/FRIGIDWINTER/article/details/127087623
版权归原作者 Mr.Winter` 所有, 如有侵权,请联系我们删除。

“机器学习:详解半朴素贝叶斯分类AODE原理(附Python实现)”的评论:

还没有评论