【手写算法实现】 之 朴素贝叶斯 Naive Bayes 篇

朴素贝叶斯模型(naive bayes)属于分类模型,也是最为简单的概率图模型,对于之后理解HMM、CRF等模型,大有裨益。这里手写算法介绍一下朴素贝叶斯模型。

朴素贝叶斯模型

朴素贝叶斯模型(Naive Bayes)中的“朴素”就在于它的条件独立性假设:
p(X1,X2,...,Xn∣Y)=p(X1∣Y)⋅p(X2∣Y)⋯p(Xn∣Y)p(X_1,X_2,...,X_n\mid Y)=p(X_1\mid Y)\cdot p(X_2\mid Y)\cdots p(X_n\mid Y) p(X1​,X2​,...,Xn​∣Y)=p(X1​∣Y)⋅p(X2​∣Y)⋯p(Xn​∣Y)
用概率图表示如下:

所以它的联合概率分布:
p(X1,X2,...,Xn,Y)=p(Y)∏i=1np(Xi∣Y)p(X_1,X_2,...,X_n,Y)=p(Y)\prod_{i=1}^np(X_i\mid Y) p(X1​,X2​,...,Xn​,Y)=p(Y)i=1∏n​p(Xi​∣Y)
这里 ck(k=1,2,...,K)c_k(k=1,2,...,K)ck​(k=1,2,...,K) 表示分类器所属的类别

这里还要补充一个前提条件,就是贝叶斯定理
P(Y∣X)=P(X∣Y)P(Y)P(X).P(Y|X)=\frac {P(X|Y)P(Y)}{P(X)}. P(Y∣X)=P(X)P(X∣Y)P(Y)​.
其中,P(Y)(Y)(Y)叫做先验概率,叫做条件概率,P(Y∣X)P(Y|X)P(Y∣X)叫做后验概率。

根据上面两大前提条件,我们可以得到朴素贝叶斯模型。实际上我们就是要最大化后验概率,从而得到类别标签。
P(Y=ck∣X=x)=P(X=x∣Y=ck)P(Y=ck)P(X=x)=P(Y=ck)∏j=1nP(X=x(i)∣Y=ck)P(X=x)P(Y=c_k|X=x)=\frac {P(X=x|Y=c_k)P(Y=c_k)}{P(X=x)}=\frac {P(Y=c_k)\prod_{j=1}^{n}P(X=x^{(i)}|Y=c_k)}{P(X=x)} P(Y=ck​∣X=x)=P(X=x)P(X=x∣Y=ck​)P(Y=ck​)​=P(X=x)P(Y=ck​)∏j=1n​P(X=x(i)∣Y=ck​)​
由于P(X=x)P(X=x)P(X=x)对于所有的类别标签来说的话,都是一样的,所以可以去掉,最终得到的公式如下:
y=argmaxckP(Y=ck)∏j=1nP(X=x(i)∣Y=ck)y=argmax_{c_k}P(Y=c_k){\prod_{j=1}^{n}P(X=x^{(i)}|Y=c_k)} y=argmaxck​​P(Y=ck​)j=1∏n​P(X=x(i)∣Y=ck​)
这就是朴素贝叶斯的公式

在朴素贝叶斯估计中,条件独立性假设是非常强的假设,实际上是为了简化运算,实际上很多时候,特征之间是存在联系的,但是这样朴素贝叶斯也更加简单,与此同时,损失了一些精度。

参数估计 先验概率与条件概率的计算

当得到了朴素贝叶斯的公式后,那么其中的p(Y=ck)p(Y=c_k)p(Y=ck​)与p(Xi=xi∣Y=ck)p(X_i=x_i\mid Y=c_k)p(Xi​=xi​∣Y=ck​)怎么求呢?在这里,我们需要分情况讨论:得看特征本身是离散的还是连续的。

  • 当特征是离散的时候,我们使用极大似然估计,叫做多项式模型,MultinomialNB就是先验为多项式分布的朴素贝叶斯
  • 当特征是连续的时候,我们让其满足高斯分布,叫做高斯模型,所以GaussianNB就是先验为高斯分布的朴素贝叶斯
  • 当特征是二元离散值或者很稀疏的多元离散值的时候,叫做伯努利模型,BernoulliNB就是先验为伯努利分布的朴素贝叶斯。

朴素贝叶斯将实例分到后验概率最大的类中,这其实等价于经验风险最小化

多项式模型

当特征是离散的时候,我们使用极大似然估计去得到先验概率与条件概率。

1.求解 p(Y=ck)p(Y=c_k)p(Y=ck​)
p(Y=ck)=∑i=1NI(yi=ck)N,k=1,2,...,K,N表示样本量p(Y=c_k)=\frac{\sum_{i=1}^NI(y_i=c_k)}{N},k=1,2,...,K,N表示样本量 p(Y=ck​)=N∑i=1N​I(yi​=ck​)​,k=1,2,...,K,N表示样本量
2.求解 p(Xi=xi∣Y=ck)p(X_i=x_i\mid Y=c_k)p(Xi​=xi​∣Y=ck​)

假设第iii个特征可能的取值为Ai={ai1,ai2,...,aiSi}A_i=\{a_{i1},a_{i2},...,a_{iS_i}\}Ai​={ai1​,ai2​,...,aiSi​​},所以有xi=ail∈Aix_i=a_{il}\in A_ixi​=ail​∈Ai​,所以
p(Xi=xi∣Y=ck)=p(Xi=ail∣Y=ck)=∑j=1NI(xij=ail,yi=ck)∑i=1nI(yj=ck)p(X_i=x_i\mid Y=c_k)=p(X_i=a_{il}\mid Y=c_k)=\frac{\sum_{j=1}^NI(x_i^j=a_{il},y_i=c_k)}{\sum_{i=1}^nI(y_j=c_k)} p(Xi​=xi​∣Y=ck​)=p(Xi​=ail​∣Y=ck​)=∑i=1n​I(yj​=ck​)∑j=1N​I(xij​=ail​,yi​=ck​)​
但是,在计算先验概率与条件概率的时候,我们会做一些平滑处理,以防出现为0的情况,从而影响到后验概率的计算。这种操作叫做laplace平滑 拉普拉斯平滑,这一部分就是采用贝叶斯估计
Pλ(X(j)=ajl∣Y=ck)=∑i=1NI(xi(j)=ajl,yi=ck)+λ∑i=1NI(yi=ck)+SjλP_\lambda\left(X^{(j)}=a_{j l} \mid Y=c_k\right)=\frac{\sum_{i=1}^N I\left(x_i^{(j)}=a_{j l}, y_i=c_k\right)+\lambda}{\sum_{i=1}^N I\left(y_i=c_k\right)+S_j \lambda} Pλ​(X(j)=ajl​∣Y=ck​)=∑i=1N​I(yi​=ck​)+Sj​λ∑i=1N​I(xi(j)​=ajl​,yi​=ck​)+λ​
式中 λ⩾0\lambda \geqslant 0λ⩾0. 等价于在随机变量各个取值的频数上赋予一个正数 λ>0\lambda>0λ>0. 当 λ=0\lambda=0λ=0 时 就是极大似然估计. 常取 λ=1\lambda=1λ=1, 这时称为拉普拉斯平滑 (Laplace smoothing). 显 然, 对任何 l=1,2,⋯,Sj,k=1,2,⋯,Kl=1,2, \cdots, S_j, k=1,2, \cdots, Kl=1,2,⋯,Sj​,k=1,2,⋯,K, 有
Pλ(X(j)=ajl∣Y=ck)>0∑l=1sjP(X(j)=ajl∣Y=ck)=1\begin{array}{l}P_\lambda\left(X^{(j)}=a_{j l} \mid Y=c_k\right)>0 \\\sum_{l=1}^{s_j} P\left(X^{(j)}=a_{j l} \mid Y=c_k\right)=1\end{array} Pλ​(X(j)=ajl​∣Y=ck​)>0∑l=1sj​​P(X(j)=ajl​∣Y=ck​)=1​
同样,先验概率的贝叶斯估计是
Pλ(Y=ck)=∑i=1NI(yi=ck)+λN+KλP_\lambda\left(Y=c_k\right)=\frac{\sum_{i=1}^N I\left(y_i=c_k\right)+\lambda}{N+K \lambda} Pλ​(Y=ck​)=N+Kλ∑i=1N​I(yi​=ck​)+λ​

高斯模型

特征为连续值的时候,我们就不能采取多项式模型来估计先验概率与条件概率了,因为会导致很多 P(X=xi∣Y=ck)P(X=x_i|Y=c_k)P(X=xi​∣Y=ck​) 等于0。所以需要采用高斯模型。高斯模型的思想是:让特征的每一维都满足高斯分布(正态分布),从而来处理连续特征。注意,先验概率的计算与多项式模型相同。公式如下:
P(X(j)=a∣Y=ck)=12πσck,j2exp(−(a−μck,j)22σck,j2)P(X^{(j)}=a|Y=c_k)=\frac {1}{\sqrt {2\pi \sigma^{2}_{c_k,j}}}exp({-\frac {({a-\mu_{c_k,j}})^2}{2\sigma^{2}_{c_k,j}}}) P(X(j)=a∣Y=ck​)=2πσck​,j2​​1​exp(−2σck​,j2​(a−μck​,j​)2​)
其中, σck,j2\sigma_{c_k, j}^2σck​,j2​ 表示类别是 ckc_kck​ 的实例中,第 jjj 维特征的方差, μck,j\mu_{c_k, j}μck​,j​ 表示类别是 ckc_kck​ 的实例中,第 jjj 维特征的均值

当求出先验概率与条件概率之后,再带入到朴素贝叶斯公式中,就可以得到实例的类别标签了。

朴素贝叶斯模型的python实现

只有把模型实现一遍才说明是真的掌握了,这里用了两种方法实现,一个是纯python实现,以及调用scikit-learn库来实现。

########-----NaiveBayes------#########
class NaiveBayes():def __init__(self) -> None:self.y = Noneself.classes = Noneself.classes_num = None # 类别数量self.parameters = []def fit(self, X, y) -> None:self.y = yself.classes = np.unique(y) # 类别 self.classes_num = len(self.classes) # 计算每个特征针对每个类的均值和方差for i, c in enumerate(self.classes):# 选择类别为c的XX_where_c = X[np.where(self.y == c)]self.parameters.append([])# 添加均值与方差for col in X_where_c.T:parameters = {"mean": col.mean(), "var": col.var()}self.parameters[i].append(parameters)def _calculate_prior(self, c):'''先验函数,也就是求先验概率利用极大似然估计的结果得到'''frequency = np.mean(self.y == c)return frequency# 贝叶斯估计的先验概率frequency = (np.sum(self.y == c) + 1) / (len(X) + self.classes_num)def _calculate_likelihood(self, mean, var, X): """似然函数"""# 高斯概率eps = 1e-4 # 防止除数为0coeff = 1.0 / math.sqrt(2.0 * math.pi * var + eps)exponent = math.exp(-(math.pow(X - mean, 2) / (2 * var + eps)))return coeff * exponentdef _calculate_probabilities(self, X):posteriors = [] # 后验概率for i,c in enumerate(self.classes):# p(y)posterior = self._calculate_prior(c)# p(x | y)for feature_value, params in zip(X, self.parameters[i]):# 独立性假设# P(x1,x2|Y) = P(x1|Y)*P(x2|Y)likelihood = self._calculate_likelihood(params["mean"], params["var"], feature_value)posterior *= likelihoodposteriors.append(posterior)# 返回具有最大后验概率的类别return self.classes[np.argmax(posteriors)]def predict(self, X):y_pred = [self._calculate_probabilities(sample) for sample in X]return y_preddef score(self, X, y):y_pred = self.predict(X)accuracy = np.sum(y == y_pred, axis=0) / len(y)return accuracy

iris数据集测试

def create_data():iris = load_iris()df = pd.DataFrame(iris.data, columns=iris.feature_names)df['label'] = iris.targetdf.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']data = np.array(df.iloc[:100, :])return data[:,:-1], data[:,-1]

用自定义 NaiveBayes 模型

# 用自定义 NaiveBayes 模型
model = NaiveBayes()
model.fit(X_train, y_train)
# 测试数据
print(model.score(X_test, y_test))
1.0

从sklearn 包中调用GaussianNB 测试

# 从sklearn 包中调用GaussianNB 测试
from sklearn.naive_bayes import GaussianNB
skl_model = GaussianNB()
# 训练数据集
skl_model.fit(X_train, y_train)
# 测试数据
print(skl_model.score(X_test, y_test))
1.0

完整代码

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_iris
import math########-----NaiveBayes------#########
class NaiveBayes():def __init__(self) -> None:self.y = Noneself.classes = Noneself.classes_num = None # 类别数量self.parameters = []def fit(self, X, y) -> None:self.y = yself.classes = np.unique(y) # 类别 self.classes_num = len(self.classes) # 计算每个特征针对每个类的均值和方差for i, c in enumerate(self.classes):# 选择类别为c的XX_where_c = X[np.where(self.y == c)]self.parameters.append([])# 添加均值与方差for col in X_where_c.T:parameters = {"mean": col.mean(), "var": col.var()}self.parameters[i].append(parameters)def _calculate_prior(self, c):'''先验函数,也就是求先验概率利用极大似然估计的结果得到'''frequency = np.mean(self.y == c)return frequency# 贝叶斯估计的先验概率frequency = (np.sum(self.y == c) + 1) / (len(X) + self.classes_num)def _calculate_likelihood(self, mean, var, X): """似然函数"""# 高斯概率eps = 1e-4 # 防止除数为0coeff = 1.0 / math.sqrt(2.0 * math.pi * var + eps)exponent = math.exp(-(math.pow(X - mean, 2) / (2 * var + eps)))return coeff * exponentdef _calculate_probabilities(self, X):posteriors = [] # 后验概率for i,c in enumerate(self.classes):# p(y)posterior = self._calculate_prior(c)# p(x | y)for feature_value, params in zip(X, self.parameters[i]):# 独立性假设# P(x1,x2|Y) = P(x1|Y)*P(x2|Y)likelihood = self._calculate_likelihood(params["mean"], params["var"], feature_value)posterior *= likelihoodposteriors.append(posterior)# 返回具有最大后验概率的类别return self.classes[np.argmax(posteriors)]def predict(self, X):y_pred = [self._calculate_probabilities(sample) for sample in X]return y_preddef score(self, X, y):y_pred = self.predict(X)accuracy = np.sum(y == y_pred, axis=0) / len(y)return accuracydef create_data():iris = load_iris()df = pd.DataFrame(iris.data, columns=iris.feature_names)df['label'] = iris.targetdf.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']data = np.array(df.iloc[:100, :])return data[:,:-1], data[:,-1]if __name__ == '__main__':# iris 数据集测试X, y = create_data()X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)print(X_train[0], y_train[0])# 用自定义 NaiveBayes 模型model = NaiveBayes()model.fit(X_train, y_train)# 测试数据print(model.score(X_test, y_test))# 从sklearn 包中调用GaussianNB 测试from sklearn.naive_bayes import GaussianNBskl_model = GaussianNB()# 训练数据集skl_model.fit(X_train, y_train)# 测试数据print(skl_model.score(X_test, y_test))

【手写算法实现】 之 朴素贝叶斯 Naive Bayes 篇相关推荐

  1. 机器学习:基于朴素贝叶斯(Naive Bayes)的分类预测

    目录 一.简介和环境准备 简介: 环境: 二.实战演练 2.1使用葡萄(Wine)数据集,进行贝叶斯分类 1.数据导入 2.模型训练 3.模型预测 2.2模拟离散数据集–贝叶斯分类 1.数据导入.分析 ...

  2. 朴素贝叶斯(naive bayes)

    朴素贝叶斯(naive bayes) 标签: Python 机器学习 主要參考资料:<机器学习实战><统计学习方法> 1.朴素贝叶斯分类原理 朴素贝叶斯法是基于贝叶斯定理和特征 ...

  3. 机器学习一:朴素贝叶斯(Naive Bayes)

    朴素贝叶斯 Naive Bayes 1. Introduction 1.1 离散属性 1.2 连续属性 1.2.1 Gaussian Naive Bayes 1.2.2 Multinomial Nai ...

  4. 【机器学习】朴素贝叶斯(Naive Bayes)

    在所有的机器学习分类算法中,朴素贝叶斯和其他绝大多数的分类算法都不同.对于大多数的分类算法,比如决策树,KNN,逻辑回归,支持向量机等,他们都是判别方法,也就是直接学习出特征输出Y和特征X之间的关系, ...

  5. 西瓜书+实战+吴恩达机器学习(八)监督学习之朴素贝叶斯 Naive Bayes

    文章目录 0. 前言 1. 朴素贝叶斯算法 2. 半朴素贝叶斯算法 2.1. ODE 2.2. SPODE 2.3. TAN 2.4. AODE 如果这篇文章对你有一点小小的帮助,请给个关注,点个赞喔 ...

  6. 机器学习笔记——朴素贝叶斯(Naive Bayes)

    1贝叶斯算法简介 贝叶斯分类算法是统计学的一种分类方法,它是一类利用概率统计知识进行分类的算法.在许多场合,朴素贝叶斯(Naïve Bayes,NB)分类算法可以与决策树和神经网络分类算法相媲美,该算 ...

  7. 机器学习(十)分类算法之朴素贝叶斯(Naive Bayes)算法

    贝叶斯定理 首先我们来了解一下贝叶斯定理: 贝叶斯定理是用来做什么的?简单说,概率预测:某个条件下,一件事发生的概率是多大? 了解一下公式 事件B发生的条件下,事件A发生的概率为: 这里写图片描述 同 ...

  8. 机器学习算法: 朴素贝叶斯(Naive Bayes)

    朴素贝叶斯的介绍 朴素贝叶斯算法(Naive Bayes, NB) 是应用最为广泛的分类算法之一.它是基于贝叶斯定义和特征条件独立假设的分类器方法.由于朴素贝叶斯法基于贝叶斯公式计算得到,有着坚实的数 ...

  9. 朴素贝叶斯 Naive Bayes Classifier

    目录 前言 一.朴素贝叶斯是什么? 二.朴素贝叶斯的优点和缺点 三.朴素贝叶斯的应用场景 四.构建朴素贝叶斯模型的注意事项 五.朴素贝叶斯模型的实现类库 六.朴素贝叶斯模型的评价指标 七.类库scik ...

最新文章

  1. NR 5G 非3GPP 接入到核心网络的安全性
  2. 面试:如何快速过滤出一次请求的所有日志?
  3. error: No resource identifier found for attribute ‘backIcon’ in package
  4. UART串口通信浅谈之(二)--寄存器设置
  5. 物生政可以报计算机专业吗,新高考物生政组合好吗?高考物生政可以报什么专业?...
  6. rpm oracle 离线,在CentOS中离线安装Oracle之安装准备
  7. Linux 下, npm i 老是被killed 已杀死
  8. 7.3.8.1 - 并发多线程 死锁和递归锁 - 信号量
  9. 【jQuery学习】—实现弹幕效果
  10. HPC:鱼与熊掌可以兼得
  11. 扫描仪上显示服务器连接错误代码,扫描仪 常见问题解决方法
  12. java 无法加载dll_java中调用本地动态链接库(*.DLL)的两种方式详解和not found library、打包成jar,war包dll无法加载等等问题解决办法...
  13. du -sh * 与 ls -lh 显示文件大小不一样
  14. html js中英切换,使用js实现URL中的中英文转化 - 小俊学习网
  15. 每日一算法7--35选7彩票程序
  16. C++中的system(pause);
  17. cglib BeanCopier的使用
  18. BrupSuite Repeater模块
  19. 最近两周我们接触到的两种线上抓娃娃机的技术实现方案(一种RTSP/一种RTMP)
  20. 安装VS2010sp1显示兼容性问题

热门文章

  1. 项目二 管理和操作linux系统
  2. 《电网技术》最新投稿经验
  3. Android游戏源码——忍者快跑
  4. 操作系统-信号量机制
  5. MATLAB中的eig函数用法
  6. matlab里有没有大气模型,[转载]VB+ACCESS+MATLAB大气污染模型系统(毕业论文+文
  7. 《伤寒论》——辨太阳病脉证并治(下)51条
  8. Hibernate的HQL、QBC、QBE查询总结
  9. ImageX继续评测系列(1):Discuz 用上AVIF图片格式
  10. 你能给芯片产业贡献什么价值?李力游博士发问