【手写算法实现】 之 朴素贝叶斯 Naive Bayes 篇
【手写算法实现】 之 朴素贝叶斯 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∏np(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=1nP(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=argmaxckP(Y=ck)j=1∏nP(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=1NI(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=1nI(yj=ck)∑j=1NI(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=1NI(yi=ck)+Sjλ∑i=1NI(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=1sjP(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=1NI(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,j21exp(−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 篇相关推荐
- 机器学习:基于朴素贝叶斯(Naive Bayes)的分类预测
目录 一.简介和环境准备 简介: 环境: 二.实战演练 2.1使用葡萄(Wine)数据集,进行贝叶斯分类 1.数据导入 2.模型训练 3.模型预测 2.2模拟离散数据集–贝叶斯分类 1.数据导入.分析 ...
- 朴素贝叶斯(naive bayes)
朴素贝叶斯(naive bayes) 标签: Python 机器学习 主要參考资料:<机器学习实战><统计学习方法> 1.朴素贝叶斯分类原理 朴素贝叶斯法是基于贝叶斯定理和特征 ...
- 机器学习一:朴素贝叶斯(Naive Bayes)
朴素贝叶斯 Naive Bayes 1. Introduction 1.1 离散属性 1.2 连续属性 1.2.1 Gaussian Naive Bayes 1.2.2 Multinomial Nai ...
- 【机器学习】朴素贝叶斯(Naive Bayes)
在所有的机器学习分类算法中,朴素贝叶斯和其他绝大多数的分类算法都不同.对于大多数的分类算法,比如决策树,KNN,逻辑回归,支持向量机等,他们都是判别方法,也就是直接学习出特征输出Y和特征X之间的关系, ...
- 西瓜书+实战+吴恩达机器学习(八)监督学习之朴素贝叶斯 Naive Bayes
文章目录 0. 前言 1. 朴素贝叶斯算法 2. 半朴素贝叶斯算法 2.1. ODE 2.2. SPODE 2.3. TAN 2.4. AODE 如果这篇文章对你有一点小小的帮助,请给个关注,点个赞喔 ...
- 机器学习笔记——朴素贝叶斯(Naive Bayes)
1贝叶斯算法简介 贝叶斯分类算法是统计学的一种分类方法,它是一类利用概率统计知识进行分类的算法.在许多场合,朴素贝叶斯(Naïve Bayes,NB)分类算法可以与决策树和神经网络分类算法相媲美,该算 ...
- 机器学习(十)分类算法之朴素贝叶斯(Naive Bayes)算法
贝叶斯定理 首先我们来了解一下贝叶斯定理: 贝叶斯定理是用来做什么的?简单说,概率预测:某个条件下,一件事发生的概率是多大? 了解一下公式 事件B发生的条件下,事件A发生的概率为: 这里写图片描述 同 ...
- 机器学习算法: 朴素贝叶斯(Naive Bayes)
朴素贝叶斯的介绍 朴素贝叶斯算法(Naive Bayes, NB) 是应用最为广泛的分类算法之一.它是基于贝叶斯定义和特征条件独立假设的分类器方法.由于朴素贝叶斯法基于贝叶斯公式计算得到,有着坚实的数 ...
- 朴素贝叶斯 Naive Bayes Classifier
目录 前言 一.朴素贝叶斯是什么? 二.朴素贝叶斯的优点和缺点 三.朴素贝叶斯的应用场景 四.构建朴素贝叶斯模型的注意事项 五.朴素贝叶斯模型的实现类库 六.朴素贝叶斯模型的评价指标 七.类库scik ...
最新文章
- NR 5G 非3GPP 接入到核心网络的安全性
- 面试:如何快速过滤出一次请求的所有日志?
- error: No resource identifier found for attribute ‘backIcon’ in package
- UART串口通信浅谈之(二)--寄存器设置
- 物生政可以报计算机专业吗,新高考物生政组合好吗?高考物生政可以报什么专业?...
- rpm oracle 离线,在CentOS中离线安装Oracle之安装准备
- Linux 下, npm i 老是被killed 已杀死
- 7.3.8.1 - 并发多线程 死锁和递归锁 - 信号量
- 【jQuery学习】—实现弹幕效果
- HPC:鱼与熊掌可以兼得
- 扫描仪上显示服务器连接错误代码,扫描仪 常见问题解决方法
- java 无法加载dll_java中调用本地动态链接库(*.DLL)的两种方式详解和not found library、打包成jar,war包dll无法加载等等问题解决办法...
- du -sh * 与 ls -lh 显示文件大小不一样
- html js中英切换,使用js实现URL中的中英文转化 - 小俊学习网
- 每日一算法7--35选7彩票程序
- C++中的system(pause);
- cglib BeanCopier的使用
- BrupSuite Repeater模块
- 最近两周我们接触到的两种线上抓娃娃机的技术实现方案(一种RTSP/一种RTMP)
- 安装VS2010sp1显示兼容性问题