模型

生成模型介绍

我们定义样本空间为\(\mathcal{X} \subseteq \mathbb{R}^n\),输出空间为\(\mathcal{Y} = \{c_1, c_2, ..., c_K\}\)。\(\textbf{X}\)为输入空间上的随机向量,其取值为\(\textbf{x}\),满足\(\textbf{x} \in \mathcal{X}\);\(Y\)为输出空间上的随机变量,设其取值为\(y\),满足\(y \in \mathcal{Y}\)。我们将容量为\(m\)的训练样本表示为:

\[\begin{aligned}
D = \{\{\textbf{x}^{(1)}, y^{(1)}\}, \{\textbf{x}^{(2)}, y^{(2)}\},..., \{\textbf{x}^{(m)}, y^{(m)}\}\}
\end{aligned}\tag{1}
\]
\[

\]

我们遵循机器学习的一个基本假设,即训练样本是从一个未知的总体分布\(P(\textbf{X} = \textbf{x}, Y=y)\)中采样产生,且训练样本独立同分布。

我们采取概率模型的视角,即将分类模型表示为条件概率分布\(P(Y=y|\textbf{X}=\textbf{x})\)。而依据分布\(P(Y=y|\textbf{X}=\textbf{x})\)的求解可将模型分为判别模型和生成模型。判别模型直接对条件概率分布\(P(Y=y|\textbf{X}=\textbf{x})\)进行参数估计(估计方法可采用极大似然估计或贝叶斯估计);而生成模型则利用条件概率公式\(P(Y=y|\textbf{X}=\textbf{x}) = \frac{P(\textbf{X}=\textbf{x}, Y=y)}{P(\textbf{X}=\textbf{x})}\)来计算分布。分子\(P(\textbf{X}=\textbf{x}, Y=y)\)是一个联合概率分布,能够还原出联合概率分布\(P(\textbf{X}=\textbf{x}, Y=y)\)是生成模型的一大特性。

朴素贝叶斯模型推导

我们对分子继续运用条件概率公式,进一步得到

\[\begin{aligned}
P(Y=y|\textbf{X}=\textbf{x}) = \frac{P(\textbf{X}=\textbf{x}|Y=y)P(Y=y)}{P(\textbf{X}=\textbf{x})}
\end{aligned} \tag{2}
\]

这个公式即大名鼎鼎的贝叶斯公式。 这里我们采用贝叶斯学派的视角,将\(P(Y=y)\)称为先验概率分布,表示在数据观测之前对\(Y\)的信念;\(P(Y = y|\textbf{X}=\textbf{x})\)称为后验概率分布,表示经过观测数据\(\textbf{X}\)(也称“证据”)校正后对\(Y\)的信念。注意不要和和贝叶斯估计中参数\(\theta\)的先验和后验分布搞混了,贝叶斯估计也应用了贝叶斯公式,但先验概率分布和后验概率分布的实际含义与这里完全不同。

我们再将分母运用全概率公式展开,我们得到

\[\begin{aligned}
P(Y=y|\textbf{X}=\textbf{x})= \frac{P(\textbf{X}=\textbf{x}|Y=y)P(Y=y)}{\sum_{y\in \mathcal{Y}}P(\textbf{X}=\textbf{x}|Y=y)P(Y=y)}
\end{aligned}\tag{3}
\]

这意味着我们只需要学习概率分布\(P(Y=y)\)和\(P(\textbf{X}=\textbf{x}|Y=y)\),而无需关心\(P(\textbf{X}=\textbf{x})\)。

将随机向量\(\textbf{x}\)沿着其特征维度展开,我们继续得到

\[\begin{aligned}
P(\textbf{X} = \textbf{x} | Y = y) = P(X_1 = x_1, ..., X_n = x_n | Y=y), \quad n \text{为特征维度}
\end{aligned}\tag{4}
\]

这里我们为了简单起见,假设样本属性是离散的,第\(j\)个属性\(x_j\)的属性集为\(A_j=\{a_{j1}, a_{j2},...,a_{jl},..., a_{j{N_j}}\}\),满足\(x_j \in A_j\)。可以看出,条件概率分布\(P(\textbf{X}=\textbf{x}|Y=y)\)的参数总量是指数级的(\(x_j\)的属性集\(A_j\)大小为\(N_j\),\(j=1, 2, ..., n\),\(Y\)可取值有\(K\)个,那么参数个数为\(K \prod_{j=1}^{n}N_j\)),不能对其直接进行参数估计。

因此,我们决定对原本拥有指数级参数数量的分布进行拆分。这里,朴素贝叶斯法做出了条件独立性假设:样本特征在类确定的条件下条件独立(这也是“朴素”(Naive)一词的得名)。这样我们就能将原本拥有庞大参数的概率分布进行拆分:

\[\begin{aligned}
P(\textbf{X} = \textbf{x} | Y=y) = P(X_1 = x_1, ..., X_n = x_n | Y=y) = \prod_{j=1}^nP(X_j = x_j | Y = y)
\end{aligned}\tag{5}
\]

这样,我们就可以对\(P(\textbf{X} | Y=y)\)分布进行高效的参数估计。之后,我们对于输入样本\(\textbf{x}\),计算概率分布\(P(Y=y|\textbf{X}=\textbf{x})\):

\[\begin{aligned}
P(Y=y|\textbf{X}=\textbf{x})= \frac{P(\textbf{X}=\textbf{x}|Y=y)P(Y=y)}{\sum_{ y \in \mathcal{Y}}P(\textbf{X}=\textbf{x}|Y=y)P(Y=y)}
= \frac{\prod_{j=1}^nP(X_j = x_j | Y=y)P(Y=y)}{\sum_{y \in \mathcal{Y}}[ \prod_{j=1}^nP(X_j = x_j | Y=y)P(Y=y) ]}
\end{aligned}\tag{6}
\]

我们采取后验概率最大化原则(即最终的输出分类取使条件概率最大的那个),设\(f(\textbf{x})\)为分类决策函数,即

\[\begin{aligned}
y = f(\textbf{x}) = \underset{y}{\arg\max} P(Y = y|\textbf{X}=\textbf{x})=\underset{y}{\arg\max}\frac{\prod_{j=1}^nP(X_j = x_j | Y=y)P(Y=y)}{\sum_{y \in \mathcal{Y}}[ \prod_{j=1}^nP(X_j = x_j | Y=y)P(Y=y) ]}
\end{aligned}\tag{7}
\]

我们发现,不管\(y\)取何值,式\((7)\)中分母总是恒定的,因此我们可以将式\((7)\)化简为

\[\begin{aligned}
y = f(\textbf{x}) = \underset{y}{\arg\max} P(Y=y|\textbf{X}=\textbf{x}) = \underset{y}{\arg\max}\prod_{j=1}^nP(X_j = x_j | Y=y)P(Y=y)
\end{aligned}\tag{8}
\]

这就是朴素贝叶斯模型分类决策函数的最终表达式。

参数估计

极大似然估计

如式\((8)\)中所述,我们需要对先验概率分布\(P(Y=y)\)和条件概率分布\(P(X_j = x_j|Y=y)\)进行参数估计。根据极大似然估计(具体的推导过程可以参见李航《统计学习方法》中的习题解答),我们可以运用训练集\(D\)将先验概率分布\(P(Y=y)\)估计为

\[\begin{aligned}
P(Y=y) = \frac{\sum_{i=1}^m(y^{(i)} = y)}{m}, \quad m \text{为}D \text{中样本个数}
\end{aligned} \tag{9}
\]

同样,条件概率分布\(P(X_j = x_j|Y=y)\)的估计为

\[\begin{aligned}
P(X_j = x_j | Y=y) = \frac{P(X_j = x_j, Y = y)}{P(Y = y)} = \frac{\sum_{i=1}^{m}I(x_j^{(i)} =x_j, y^{(i)}=y)}{\sum_{i=1}^{m}I(y^{(i)}=y)}
, \quad m \text{为}D \text{中样本个数}
\end{aligned} \tag{10}
\]

贝叶斯估计(平滑修正)

观察式\((10)\)可知,如果训练集中属性值\(x_j\)和类\(y\)没有同时出现过,即\(P(X_j=x_j, Y=y)=0\),那么\(P(X_j = x_j | Y=y)=0\)会直接导致连乘式。这就意味着不管其他属性如何,哪怕其他属性明显符合要求,样本\(\prod_{j=1}^nP(X_j = x_j | Y=y)=0\) ,\(\textbf{x}\)属于类\(y\)的概率都会被判为0,这明显不太合理。

因此,为了避免其他属性携带的信息被训练集中未出现的属性值“抹去”,我们采用贝叶斯估计,等价于在估计概率值时通常进行“平滑”(smoothing)(具体的推导过程可以参见李航《统计学习方法》中的习题解答)。即令式\((10)\)修正为

\[\begin{aligned}
P_{\lambda}(X_j = x_j|Y=y) = \frac{\sum_{i=1}^{m}I(x_j^{(i)} =x_j, y^{(i)}=y) + \lambda}{\sum_{i=1}^{m}I(y^{(i)}=y)+N_j \lambda}, \quad \lambda > 0, \quad N_j\text{为属性}x_j\text{可能的取值数}
\end{aligned} \tag{11}
\]

我们常取\(\lambda=1\),这时称为拉普拉斯平滑(Laplacian smoothing)。

类似地,式\((9)\)中先验概率被修正为:

\[\begin{aligned}
P_\lambda(Y=y) = \frac{\sum_{i=1}^m(y^{(i)} = y)+\lambda}{m+K\lambda}, \quad \lambda > 0, \quad K\text{为标签}y\text{可能的取值数}
\end{aligned} \tag{12}
\]

可以看出,拉普拉斯平滑解决了训练集样本数不足导致的概率值为 0 的问题。拉普拉斯修正实际上假设了属性值与类别均匀分布,这是在参数估计的过程中额外引入的关于数据的先验 (prior)。当样本容量趋近于无穷时,我们发现修正过程所引入的先验的影响也趋近于 0,使得计算的概率值趋近于实际的概率值。

算法

在实际的应用中,朴素贝叶斯模型有两种训练方式。

若使用的场景对模型的预测速度要求较高,在给定训练集\(D\)的情况下,我们将概率分布\(P_\lambda(Y=y)\)和概率分布\(P_{\lambda}(X_j = x_j|Y=y)\)所有可能的取值(\(y\in \mathcal{Y}\),\(x_j \in A_j\),\(A_j\)为第\(j\)个样本属性的取值集合)都计算出来存好,然后在测试样本\(\textbf{x}^{*}\)来了之后,通过“查表”的方式将对应的概率值检索出来,然后再对其类别进行判别。这样,我们计算概率分布\(P_\lambda(Y=y)\)和概率分布\(P_{\lambda}(X_j = x_j|Y=y)\)所有可能取值的过程即对朴素贝叶斯模型进行显式训练的过程。

朴素贝叶斯的训练和测试算法如下:

如果我们不断有新的训练数据产生,可以采用“懒惰学习”(lazy learning)的方法,先不进行任何训练,测试样本来了之后再依照测试样本的属性\(x_j^{*}\)和当前数据集的状况来计算单点概率,这样可以避免对所有可能的属性都计算单点概率。若训练数据不断增加,则可在现有计算结果的基础上,仅仅对新增样本的属性值所涉及的单点概率进行计数修正,这样可以实现“增量学习”。

代码实现

使用Python语言实现朴素贝叶斯算法如下(这里我们采用Iris数据集进行测试)。

from numpy.lib.index_tricks import c_
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import numpy as np
from copy import deepcopy
class NaiveBayes():
def __init__(self, A, C, lambda_v=1):
#常常取lambda_v=1,此时称为拉普拉斯平滑
self.K = len(C)
self.A = deepcopy(A)
self.C = deepcopy(C)
self.y_cnt = {} #记录sum I(y_i = y)
self.y_prob = {} # 记录平滑后的P_lambda(Y = y)
self.lambda_v = lambda_v # 平滑参数
self.x_condition_y_prob = {}
#记录P_lambda(X_j = x_j | Y = y),这是个字典-列表-字典嵌套
# 初始化y_cnt和y_prob
for c_k in self.C:
self.y_cnt[c_k], self.y_prob[c_k] = 0, 0
# 初始化attr_cnt和x_condition_y_prob
for c_k in self.C:
self.x_condition_y_prob[c_k] = [dict() for j in range(len(self.A))]
for j in range(len(self.A)):
for a_j_l in self.A[j]:
self.x_condition_y_prob[c_k][j][a_j_l] = 0 def fit(self, X_train, y_train):
try:
assert(X_train.shape[0] == y_train.shape[0])
except:
AssertionError("input dimension 0 should be the same!") # 记录 sum I(y_i = y)
for y in y_train:
try:
self.y_cnt[y] += 1
#如果训练集中没出现过,此处y_cnt[y]将一直为0
except:
raise ValueError("invalid label!")
# 计算P_lambda(Y = c_k)
# 并对所有概率进行平滑处理
for c_k in self.C:
self.y_prob[c_k] = self.y_cnt[c_k]
self.y_prob[c_k] += self.lambda_v
self.y_prob[c_k] /= (X_train.shape[0] + self.K * self.lambda_v) # 记录sum I(x_j(i) = x_j, y(i) = y)
# 遍历每一个训练样本
for x, y in zip(X_train, y_train):
# 遍历训练样本中的每一个属性
for j, attr in enumerate(x):
try:
self.x_condition_y_prob[y][j][attr] += 1
#如果训练集中没出现过,此处self.x_condition_y_prob[y][attr] 将一直为0
except:
raise ValueError("invalid attribution %.1f!" % attr)
# 计算P_lambda(X_j = x_j | Y = c_k)
# 并对所有样本进行平滑处理
for c_k in self.C:
c_cnt = self.y_cnt[c_k]
# 遍历所有属性的属性集合
for j in range(len(self.A)):
# 遍历每一个属性的取值集合
for a_j_l in self.A[j]:
self.x_condition_y_prob[c_k][j][a_j_l] += self.lambda_v
self.x_condition_y_prob[c_k][j][a_j_l]/=(c_cnt + len(self.A[j])*self.lambda_v) def pred(self, X_test):
# 遍历测试集中的每一个样本
y_pred = []
for x in X_test:
max_ck = self.C[0]
max_prob = -1 # 考察每一种可能的标签
for c_k in self.C:
prob = 1.0
# 考察该测试样本的每一个属性
for j, attr in enumerate(x):
prob *= self.x_condition_y_prob[c_k][j][attr] prob *= self.y_prob[c_k]
if prob == 0:
raise ValueError("prob underflow!")
if prob > max_prob:
max_prob = prob
max_ck = c_k
y_pred.append(max_ck)
return np.array(y_pred) # 获得特征和类标记的结合
def get_A_and_C(X, y):
# A为第j各属性的可能取值集合
A, C = [set() for j in range(X.shape[1])], set()
# 遍历每个样本的特征向量
for x in X:
for j, attr in enumerate(x):
A[j].add(attr)
# 遍历每个样本的标签
for c_k in y:
C.add(c_k)
A = [ list(A_j) for A_j in A]
return list(A), list(C) if __name__ == "__main__":
X, y = load_iris(return_X_y=True)
A, C = get_A_and_C(X, y)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=0)
clf = NaiveBayes(A, C)
clf.fit(X_train, y_train)
y_pred = clf.pred(X_test)
acc_score = accuracy_score(y_test, y_pred)
print("The accuracy is: %.1f" % acc_score)

最终的测试结果如下:

The accuracy is: 0.9

可以看出我们实现的算法在Iris数据集上取得了90%的精度,说明我们算法的效果不错。

参考文献

  • [1] 李航. 统计学习方法(第2版)[M]. 清华大学出版社, 2019.
  • [2] 周志华. 机器学习[M]. 清华大学出版社, 2016.
  • [3] Calder K. Statistical inference[J]. New York: Holt, 1953.

统计学习1:朴素贝叶斯模型(Numpy实现)的更多相关文章

  1. 第十三次作业——回归模型与房价预测&第十一次作业——sklearn中朴素贝叶斯模型及其应用&第七次作业——numpy统计分布显示

    第十三次作业——回归模型与房价预测 1. 导入boston房价数据集 2. 一元线性回归模型,建立一个变量与房价之间的预测模型,并图形化显示. 3. 多元线性回归模型,建立13个变量与房价之间的预测模 ...

  2. 机器学习Matlab打击垃圾邮件的分类————朴素贝叶斯模型

    该系列来自于我<人工智能>课程回顾总结,以及实验的一部分进行了总结学习机 垃圾分类是有监督的学习分类最经典的案例,本文首先回顾了概率论的基本知识.则以及朴素贝叶斯模型的思想.最后给出了垃圾 ...

  3. PGM:贝叶斯网表示之朴素贝叶斯模型naive Bayes

    http://blog.csdn.net/pipisorry/article/details/52469064 独立性质的利用 条件参数化和条件独立性假设被结合在一起,目的是对高维概率分布产生非常紧凑 ...

  4. 一步步教你轻松学朴素贝叶斯模型算法Sklearn深度篇3

    一步步教你轻松学朴素贝叶斯深度篇3(白宁超   2018年9月4日14:18:14) 导读:朴素贝叶斯模型是机器学习常用的模型算法之一,其在文本分类方面简单易行,且取得不错的分类效果.所以很受欢迎,对 ...

  5. [ML学习笔记] 朴素贝叶斯算法(Naive Bayesian)

    [ML学习笔记] 朴素贝叶斯算法(Naive Bayesian) 贝叶斯公式 \[P(A\mid B) = \frac{P(B\mid A)P(A)}{P(B)}\] 我们把P(A)称为"先 ...

  6. 11.sklearn中的朴素贝叶斯模型及其应用

    #1.使用朴素贝叶斯模型对iris数据集进行花分类 #尝试使用3种不同类型的朴素贝叶斯: #高斯分布型,多项式型,伯努利型 from sklearn import datasets iris=data ...

  7. Python实现 利用朴素贝叶斯模型(NBC)进行问句意图分类

    目录 朴素贝叶斯分类(NBC) 程序简介 分类流程 字典(dict)构造:用于jieba分词和槽值替换 数据集构建 代码分析 另外:点击右下角魔法阵上的[显示目录],可以导航~~ 朴素贝叶斯分类(NB ...

  8. 后端程序员之路 18、朴素贝叶斯模型(Naive Bayesian Model,NBM)

    贝叶斯推断及其互联网应用(一):定理简介 - 阮一峰的网络日志http://www.ruanyifeng.com/blog/2011/08/bayesian_inference_part_one.ht ...

  9. sklearn中的朴素贝叶斯模型及其应用

    1.(1)多项式 from sklearn.datasets import load_iris iris = load_iris() from sklearn.naive_bayes import G ...

随机推荐

  1. DPARAM

    中M_电子科技大学_计算机组成原理 双端口RAM Dual Port Access RAM 存储器不断接受CPU访问,还要频繁地和I/O设备通信.如果只有一套MAR,ID,MDR和读写电路.任一时刻只 ...

  2. MyBatis 中实现SQL语句中in的操作 (11)

    MyBatis 中实现SQL语句中in的操作 概括:应用myBatis实现SQL查询中IN的操作 1.数据库结构及其数据 2.mapper.xml文件 <?xml version="1 ...

  3. 用NXOpen.CAM.CAMSetup.CopyObjects复制刻字操作

    复制刻字操作 手动时,报粘贴对象失败: 用代码执行,报内部错误: Dim destinationObject As NXOpen.CAM.CAMObject = CType(NXOpen.Utilit ...

  4. [no code][scrum meeting] Alpha 11

    项目 内容 会议时间 2020-04-17 会议主题 OCR紧急技术风险分析 会议时长 30min 参会人员 PM+OCR组成员 $( "#cnblogs_post_body" ) ...

  5. 【二食堂】Alpha - Scrum Meeting 5

    Scrum Meeting 5 例会时间:4.15 12:30 - 13:00 进度情况 组员 昨日进度 今日任务 李健 1. 主页搭建结束issue2. 与后端协商确定接口的设计3. 查找文本区域功 ...

  6. 使用Mybatis的TypeHandler加解密数据

    使用Mybatis的TypeHandler加解密数据 一.背景 二.解决方案 三.需求 四.实现思路 1.编写一个实体类,凡是此实体类的数据都表示需要加解密的 2.编写一个加解密的`TypeHandl ...

  7. elasticsearch的bulk(批量)操作

    在es中我们可能会有这么一种需求,即有时需要批量向es中插入或更新或删除数据,如果一条一条数据的操作,那么速度必然很慢,那么es的bulk api就可以派上用场. delete 删除操作,只需要写一个 ...

  8. threading python2 和python3

    from __future__ import division from __future__ import print_function import threading balance = 0 d ...

  9. Codeforces Round #741 (Div. 2)部分题题解

    我果然还是太菜了,就写了两道题....真是水死了.... A The Miracle and the Sleeper 简化题意:给定\(l,r\),求\(a\)%\(b\)的最大值,其中\(r> ...

  10. log4j日志集成

    一.介绍  Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台.文件.GUI组件.甚至是套接口服务 器.NT的事件记录器.UNIX Syslog ...