当前位置: 首页 > news >正文

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

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

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

朴素贝叶斯模型

朴素贝叶斯模型(Naive Bayes)中的“朴素”就在于它的条件独立性假设:
p ( X 1 , X 2 , . . . , X n ∣ Y ) = p ( X 1 ∣ Y ) ⋅ p ( X 2 ∣ Y ) ⋯ p ( X n ∣ 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,...,XnY)=p(X1Y)p(X2Y)p(XnY)
用概率图表示如下:

在这里插入图片描述

所以它的联合概率分布:
p ( X 1 , X 2 , . . . , X n , Y ) = p ( Y ) ∏ i = 1 n p ( X i ∣ 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=1np(XiY)
这里 c k ( 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(YX)=P(X)P(XY)P(Y).
其中,P ( Y ) (Y) (Y)叫做先验概率,叫做条件概率, P ( Y ∣ X ) P(Y|X) P(YX)叫做后验概率。

根据上面两大前提条件,我们可以得到朴素贝叶斯模型。实际上我们就是要最大化后验概率,从而得到类别标签。
P ( Y = c k ∣ X = x ) = P ( X = x ∣ Y = c k ) P ( Y = c k ) P ( X = x ) = P ( Y = c k ) ∏ j = 1 n P ( X = x ( i ) ∣ Y = c k ) 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=ckX=x)=P(X=x)P(X=xY=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 = a r g m a x c k P ( Y = c k ) ∏ j = 1 n P ( X = x ( i ) ∣ Y = c k ) y=argmax_{c_k}P(Y=c_k){\prod_{j=1}^{n}P(X=x^{(i)}|Y=c_k)} y=argmaxckP(Y=ck)j=1nP(X=x(i)Y=ck)
这就是朴素贝叶斯的公式

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

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

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

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

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

多项式模型

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

1.求解 p ( Y = c k ) p(Y=c_k) p(Y=ck)
p ( Y = c k ) = ∑ i = 1 N I ( y i = c k ) 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)=Ni=1NI(yi=ck),k=1,2,...,K,N表示样本量
2.求解 p ( X i = x i ∣ Y = c k ) p(X_i=x_i\mid Y=c_k) p(Xi=xiY=ck)

假设第 i i i个特征可能的取值为 A i = { a i 1 , a i 2 , . . . , a i S i } A_i=\{a_{i1},a_{i2},...,a_{iS_i}\} Ai={ai1,ai2,...,aiSi},所以有 x i = a i l ∈ A i x_i=a_{il}\in A_i xi=ailAi,所以
p ( X i = x i ∣ Y = c k ) = p ( X i = a i l ∣ Y = c k ) = ∑ j = 1 N I ( x i j = a i l , y i = c k ) ∑ i = 1 n I ( y j = c k ) 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=xiY=ck)=p(Xi=ailY=ck)=i=1nI(yj=ck)j=1NI(xij=ail,yi=ck)
但是,在计算先验概率与条件概率的时候,我们会做一些平滑处理,以防出现为0的情况,从而影响到后验概率的计算。这种操作叫做laplace平滑 拉普拉斯平滑,这一部分就是采用贝叶斯估计
P λ ( X ( j ) = a j l ∣ Y = c k ) = ∑ i = 1 N I ( x i ( j ) = a j l , y i = c k ) + λ ∑ i = 1 N I ( y i = c k ) + S j λ 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)=ajlY=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 , ⋯   , S j , k = 1 , 2 , ⋯   , K l=1,2, \cdots, S_j, k=1,2, \cdots, K l=1,2,,Sj,k=1,2,,K, 有
P λ ( X ( j ) = a j l ∣ Y = c k ) > 0 ∑ l = 1 s j P ( X ( j ) = a j l ∣ Y = c k ) = 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)=ajlY=ck)>0l=1sjP(X(j)=ajlY=ck)=1
同样,先验概率的贝叶斯估计是
P λ ( Y = c k ) = ∑ i = 1 N I ( y i = c k ) + λ 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 = x i ∣ Y = c k ) P(X=x_i|Y=c_k) P(X=xiY=ck) 等于0。所以需要采用高斯模型。高斯模型的思想是:让特征的每一维都满足高斯分布(正态分布),从而来处理连续特征。注意,先验概率的计算与多项式模型相同。公式如下:
P ( X ( j ) = a ∣ Y = c k ) = 1 2 π σ c k , j 2 e x p ( − ( a − μ c k , j ) 2 2 σ c k , j 2 ) 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)=aY=ck)=2πσck,j2 1exp(2σck,j2(aμck,j)2)
其中, σ c k , j 2 \sigma_{c_k, j}^2 σck,j2 表示类别是 c k c_k ck 的实例中,第 j j j 维特征的方差, μ c k , j \mu_{c_k, j} μck,j 表示类别是 c k c_k ck 的实例中,第 j j j 维特征的均值

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

朴素贝叶斯模型的python实现

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

########-----NaiveBayes------#########
class NaiveBayes():
    def __init__(self) -> None:
        self.y = None
        self.classes = None
        self.classes_num = None # 类别数量
        self.parameters = []
        
    def fit(self, X, y) -> None:
        self.y = y
        self.classes = np.unique(y) # 类别 
        self.classes_num = len(self.classes) 
        # 计算每个特征针对每个类的均值和方差
        for i, c in enumerate(self.classes):
            # 选择类别为c的X
            X_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 # 防止除数为0
        coeff = 1.0 / math.sqrt(2.0 * math.pi * var + eps)
        exponent = math.exp(-(math.pow(X - mean, 2) / (2 * var + eps)))
        return coeff * exponent
    
    
    def _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 *= likelihood
            posteriors.append(posterior)
        # 返回具有最大后验概率的类别
        return self.classes[np.argmax(posteriors)]
            
    def predict(self, X):
        y_pred = [self._calculate_probabilities(sample) for sample in X]
        return y_pred
    
    def 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.target
    df.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 = None
        self.classes = None
        self.classes_num = None # 类别数量
        self.parameters = []
        
    def fit(self, X, y) -> None:
        self.y = y
        self.classes = np.unique(y) # 类别 
        self.classes_num = len(self.classes) 
        # 计算每个特征针对每个类的均值和方差
        for i, c in enumerate(self.classes):
            # 选择类别为c的X
            X_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 # 防止除数为0
        coeff = 1.0 / math.sqrt(2.0 * math.pi * var + eps)
        exponent = math.exp(-(math.pow(X - mean, 2) / (2 * var + eps)))
        return coeff * exponent
    
    
    def _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 *= likelihood
            posteriors.append(posterior)
        # 返回具有最大后验概率的类别
        return self.classes[np.argmax(posteriors)]
            
    def predict(self, X):
        y_pred = [self._calculate_probabilities(sample) for sample in X]
        return y_pred
    
    def score(self, X, y):
        y_pred = self.predict(X)
        accuracy = np.sum(y == y_pred, axis=0) / len(y)
        return accuracy
    
def create_data():
    iris = load_iris()
    df = pd.DataFrame(iris.data, columns=iris.feature_names)
    df['label'] = iris.target
    df.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 GaussianNB
    skl_model = GaussianNB()
    # 训练数据集
    skl_model.fit(X_train, y_train)
    # 测试数据
    print(skl_model.score(X_test, y_test))

相关文章:

  • Vue.js核心技术解析与uni-app跨平台实战开发学习笔记 第12章 Vue3.X新特性解析 12.12 响应式系统工具集的使用
  • java服务器端开发-servlet:202、Servlet执行过程介绍:get请求与post请求、编码相关等
  • yara 分析器
  • 数据结构(三) -- 栈
  • 神策数据发布融媒行业版,驱动媒体深度融合转型
  • 解决安装GDAL库报错问题(Windos)
  • 数据逻辑校验机制
  • Linux关于jar包的基本操作
  • 用什么软件可以提高视频批量剪辑的效率
  • 搜索替换 csv 文件中的文本
  • DBCO-PEG-Casein/Ovalbumin/Lectins点击化学DBCO偶联络蛋白/卵清蛋白/凝集素
  • 【JetPack Compose】声明式UI 、组合、重组
  • jquary样式操作、动画
  • Apollo配置语法——Apollo配置List和Map以及组合应用——非常详细
  • ESP8266-Arduino编程实例-MAX30102脉搏和心率监测传感器驱动
  • [微信小程序] 使用ES6特性Class后出现编译异常
  • 【347天】每日项目总结系列085(2018.01.18)
  • 07.Android之多媒体问题
  • Android单元测试 - 几个重要问题
  • go append函数以及写入
  • jQuery(一)
  • JS实现简单的MVC模式开发小游戏
  • python docx文档转html页面
  • windows-nginx-https-本地配置
  • 订阅Forge Viewer所有的事件
  • 关于List、List?、ListObject的区别
  • 使用iElevator.js模拟segmentfault的文章标题导航
  • 源码安装memcached和php memcache扩展
  • C# - 为值类型重定义相等性
  • 阿里云服务器购买完整流程
  • 如何正确理解,内页权重高于首页?
  • #宝哥教你#查看jquery绑定的事件函数
  • #我与Java虚拟机的故事#连载17:我的Java技术水平有了一个本质的提升
  • #我与Java虚拟机的故事#连载18:JAVA成长之路
  • #在 README.md 中生成项目目录结构
  • (30)数组元素和与数字和的绝对差
  • (52)只出现一次的数字III
  • (C语言)输入自定义个数的整数,打印出最大值和最小值
  • (MonoGame从入门到放弃-1) MonoGame环境搭建
  • (ros//EnvironmentVariables)ros环境变量
  • (附源码)基于SpringBoot和Vue的厨到家服务平台的设计与实现 毕业设计 063133
  • (一)Neo4j下载安装以及初次使用
  • (一)pytest自动化测试框架之生成测试报告(mac系统)
  • .net core 调用c dll_用C++生成一个简单的DLL文件VS2008
  • .secret勒索病毒数据恢复|金蝶、用友、管家婆、OA、速达、ERP等软件数据库恢复
  • [] 与 [[]], -gt 与 > 的比较
  • [2018-01-08] Python强化周的第一天
  • [AI]ChatGPT4 与 ChatGPT3.5 区别有多大
  • [Android] 修改设备访问权限
  • [Android]如何调试Native memory crash issue
  • [C++]四种方式求解最大子序列求和问题
  • [docker]docker网络-直接路由模式
  • [hdu 4552] 怪盗基德的挑战书
  • [IE编程] WebBrowser控件的多页面浏览(Tabbed Browsing)开发接口
  • [Latex] \bibitem{} | .bbl 格式参考文献转换与获得