机器学习:多种朴素贝叶斯及混合贝叶斯的实现

目录

贝叶斯介绍:

贝叶斯数学原理:

先验概率:

联合概率密度函数:

伯努利贝叶斯:

伯努利概率质量函数:

训练阶段:

预测阶段:

代码展示

拓展:

高斯贝叶斯:

高斯概率分布:

训练阶段:

预测阶段:

代码展示:

混合贝叶斯模型:


贝叶斯介绍:


        贝叶斯定理由英国数学家贝叶斯( Thomas Bayes 1702-1761年) 发展,用来描述两个条件概率之间的关系,比如 P(A|B) 和 P(B|A)。贝叶斯数学模型,在假设特征之间互不相关联,互相独立的情况下,利用特征的联合分布与先验概率得到最终的预测类别。

贝叶斯数学原理:

        P(A|B)=\frac{P(AB)}{P(B)}

        这里翻译成文字的语言就是,事件A在事件B发生的前提下发生的条件概率=事件A事件B共同发生的概率/事件B发生的概率。根据同时根据以上公式,可以将P(AB)转换一下。

  P(AB)=P(B|A)*P(A)

那么将上式公式带入贝叶斯的原理公式中,这便是贝叶斯的关键公式。

P(A|B)=\frac{P(B|A)*P(A)}{P(B)}

如何让它从数学公式变成一个数学模型呢,所以我们需要把A和B写的更贴近数据一些。

P(y=k|x1,x2,x3..)=\frac{P(x1,x2,x3..|y=k)*P(y=k)}{P(x1,x2,x3...)}

那么目标就变得明显了,要得到x1,x2,x3...属于k类的概率,我们需要知道,属于k类时x1,x2,x3..的概率。

先验概率:

        在上式中P(y=k)被称为先验概率,根据领域知识或以往的经验,确定未观测数据的先验概率分布。是从样本总体估计样本为某一类的概率,可以简单理解为,某类占总样本的个数。所以它的计算公式其中一种可以表示为:

P(y=k)=\frac{count(y_{y=k})}{\sum y_{y=i}}

这也是我们下面模型,使用到的公式。

联合概率密度函数:

        p(x1,x2,x3,...|y=k)表示在给定类别y=k的条件下,特征向量x=(x1,x2,x3,...)的联合概率密度函数。如果直接计算以上联合概率密度,将会使算法异常冗杂。所以,贝叶斯假设特征之间互相独立,根据概率论的基础知识,当两个特征互相独立的时候,可以产生以下运算:

P(AB)=P(A)*P(B)

P(x1,x2|y=K)=P(x1|y=k)*P(x2|y=k)

所以可以将联合概率密度分割开来。

P(x1,x2...|y=K)=P(x1|y=k)*P(x2|y=k)*P(x3|y=k)...

        p(x1,x2,x3,..)表示特征向量x的边缘概率密度函数,也就是所有类别下的特征向量x的联合概率密度函数。在实际应用中,由于p(x)对于所有类别都是相同的,因此可以省略掉,只需要计算分子部分即可。

伯努利贝叶斯:

伯努利贝叶斯是一种基于贝叶斯定理的分类算法,它适用于处理二元特征的分类问题。该算法假设每个特征都是独立的,并且每个特征的取值只能是0或1,表示特征的存在与否。

伯努利概率质量函数:

        伯努利分布,简单来看就是二项分布,因此,多适用于二项分布中。伯努利分布是一种离散概率分布,用于描述只有两个可能结果的随机试验。在伯努利分布中,每次试验的结果只能是成功或失败,成功的概率记为p,失败的概率记为1-p。因其为离散的分布,所以使用概率质量函数来描述它的取值。其公式为:


P(X = k)=p^{k}*(1-p)^{1-k}

训练阶段:

        我们在构建伯努利贝叶斯的时候需要知道每个特征的条件概率,在这里我将每一列特征当做一个整体,并且假设每组特征都服从伯努利分布。那么如果数据集中有3个特征x1,x2,x3,先以类别作为划分,划出为某一类的所有数据,假设为0,划分之后得到所有最后取值都为0的数据集,然后分别对其取和,得到三个数,将每一组sum(xi)取得的和除以sum(x1+x2+x3),也就是单个样本除总样本,得到每个样本的条件概率,完成对0之后再对1的类重复进行以上操作。对于二分类,两次之后,将会得到两组p,分别对应为0的p和为1的p。训练阶段也就此完成。

预测阶段:

        利用我们从训练阶段求到的p带入概率质量函数中,分别计算为0的概率或为1的概率,取较大的概率值当做预测值。由于我们提前知道了,为0为1的p值,那么将其写入公式中,也就是描述在y=0或1的前提下,x的取值,求得贝叶斯的联合概率密度。于是可以将上述公式转换为下面的公式:

P(x|y=k)=p_{y=k}^{x}*(1-p_{y=k})^{1-x}

那么将所有特征的P(x|y=k)相乘便求得了,联合概率密度函数,在乘以先验概率即可得到最终属于0和属于1的概率。再进行对比,取大的值来当做预测结果即可。

代码展示

import numpy as np
class BernoulliNB:
    def fit(self, X, y):
        n_samples, n_features = X.shape#获取样本数,特征数
        self.classes = np.unique(y)
        n_classes = len(self.classes)
        self.priors = np.bincount(y)/n_samples#先验概率
        self.p=np.zeros((n_classes,n_features))#建立伯努利概率p的容器
        for i in self.classes:
            sum_all_row=np.sum(X[y==i],axis=0)#按照类别,每个特征求和
            sum_all=np.sum(X[y==i])#按照类别,总样本求和
            #得到伯努利概率p,为确保概率不为0使用了拉普拉斯平滑
            self.p[i]=(sum_all_row+1)/(sum_all+2)
            
    def predict(self,x):
        x=np.array(x)
        predict=[]
        for i in range(len(self.priors)):
            x1=x.copy()
            for j in range(len(x)):
                x1[j,:]=(self.p[i,:]**x1[j,:])*((1-self.p[i,:])**(1-x1[j,:]))
                #伯努利概率质量计算公式
            predict.append(np.prod(x1,axis=1)*self.priors[i])#贝叶斯公式
        predict=np.array(predict)
        return np.argmax(predict,axis=0)

导入数据集进行测试。数据集样式,可以看到这组数据并不符合伯努利分布,看一下模型在不同的数据分布上的效果:

 相同种子数,没有进行数据处理,与sklearn进行比较。

差距并不明显,手写模型成功。 

拓展:

在预测阶段,若是特征很多,直接相乘可能会超过numpy的数字上限,让数据产生无穷小,-inf。这个时候可以使用最大似然估计方法,预测概率取对数,将相乘改为相加。并且对于公式来说,我们只需要对比为0和为1的概率大小,所以可以公式变为:

logP(y=k|x)=logP(x|y=k)+logP(y=k)

logP(x|y=k)=logP(x1|y=k)+logP(x2|y=k)+...

改成这样需要注意,log里面的数不能小于等于0,若是概率为0可能是前面步骤的某一点没有进行平滑造成。

高斯贝叶斯:

        高斯贝叶斯是基于高斯分布产生的分类器,它假设所有特征服从高斯分布,根据概率论的大数定理,我们知道当数据大到一定程度的时候,数据的均值将会趋近于数据的期望值。那么当均值等于期望值会发生什么呢,原数据将会成为一个高斯分布。所以高斯贝叶斯也适用于数据量较大的情况下。

高斯概率分布:

        高斯概率分布,也称为正态分布或钟形曲线,是一种连续概率分布,常用于描述自然界中许多现象的分布情况。高斯分布的概率密度函数(PDF)可以表示为:

f(x) =\frac{1}{\sqrt{2\pi }*\sigma }*e^{\frac{(x-x.mean)^2}{2\sigma ^2}}

其中,μ是分布的均值(期望值),σ是分布的标准差。

训练阶段:

        与伯努利训练相同,高斯的训练,也需要取得不同类别下的标准差和均值。原理为,将每一列当做高斯分布,那么训练集与测试集独立同分布,可以假设测试集的均值和训练集均值相同,方差同理。那么对于训练集只需要求得每一个类下,每一列均值和标准差。

预测阶段:

        高斯的预测需要用到训练集中得到的均值和标准差,即将每个测试集带入高斯概率密度函数中,求得一个全为高斯概率的矩阵,对其每行进行累乘即得到P(x|y=K)。预测和训练较为简单。

代码展示:

class GaussianNB1:
    def fit(self,x,y):
        x=np.array(x)
        y=np.array(y)
        num_samples,num_featers=x.shape
        self.num_class=np.unique(y)
        self.posteriors_mean=np.zeros((len(self.num_class),num_featers))
        self.posteriors_std=np.zeros((len(self.num_class),num_featers))
        self.py=np.bincount(y)/num_samples
        for i in self.num_class:
            px1=x[np.where(y==i)[0]].T
            self.posteriors_mean[i,:]=np.mean(px1,axis=1)#获取属于每个类下的均值
            self.posteriors_std[i,:]=np.var(px1,axis=1)#获取属于每个类下的标准差
    def predict(self,x1):    
        results=[]
        for i in self.num_class:
            x2=x1.copy()
            x2=(x2-self.posteriors_mean[i,:])**2
            e=np.exp((-1)*(x2)/(2*self.posteriors_std[i,:]*self.posteriors_std[i,:]))
            
            p=(1/(np.sqrt(2*np.pi*self.posteriors_std[i,:]*self.posteriors_std[i,:])))*e
            #公式带入
            result=(np.prod(p,axis=1)*self.py[i])#概率
            results.append(result)
        results=np.array(results)
        return1=[]
        return1.append(self.num_class[np.argmax(results,axis=0)])
        return1=np.array(return1)   
        return return1[0]
         

导入数据集,与伯努利贝叶斯数据集相同,对比与官方的区别:


from sklearn.naive_bayes import GaussianNB
x_train,x_test,y_train,y_test=train_test_split(data.iloc[:,:-1],data.iloc[:,-1],random_state=421)#相同种子数
# 创建朴素贝叶斯分类器
gnb1=GaussianNB1()#手写
gnb = GaussianNB()#sklearn官方
stand=StandardScaler()
x_train=stand.fit_transform(x_train)
x_test=stand.fit_transform(x_test)
gnb.fit(x_train,y_train)
print('sklearn贝叶斯得分',gnb.score(x_test,y_test))
gnb1.fit(x_train,y_train)
y_pre=gnb1.predict(x_test)
print('手搓高斯贝叶斯得分为',accuracy_score(y_test,y_pre))
print('手搓高斯贝叶斯精确率为',precision_score(y_test,y_pre))  
print('手搓高斯贝叶斯召回率为',recall_score(y_test,y_pre)) 

 可以看出手写高斯贝叶斯得分略高于sklearn的得分,并且召回率达到了恐怖的1,手写模型成功。

混合贝叶斯模型:

在熟悉以上两种算法之后,会发现,不论如何接近分布,都是假设其为某一种分布。那假如提前知道了数据分布,那么我们可以使用更精确地概率分布来假设,也就是说,每一列的x特征可能都有其单独的分布,那么下面的每一个P都为一种概率分布求到的概率。

                P(x1,x2...|y=K)=P(x1|y=k)*P(x2|y=k)*P(x3|y=k)...

于是我简单的尝试了将伯努利和高斯混合起来,跑天猫复购的数据。简单瞄一眼密密麻麻的数据:

总共有50w条数据,40多个特征。经过观察,里面有几列只有0和1的,我将其假设为伯努利分布,剩下其他的假设为高斯分布。使用的jyputer断断续续的写的,整理了其中关键的代码进行展示。

import pandas as pd #导入数据,删除无用的列
data=pd.read_csv('data/features.csv')
y=data['label'].copy()
data=data.drop(['label','origin','user_id', 'merchant_id'],axis=1)


import numpy as np
from sklearn.decomposition import PCA
import matplotlib.pyplot as plot
# 创建PCA对象,指定降维后的维度为2
pca = PCA(n_components=15)
#高斯组数据
gocs_data=data.drop(['age_0.0', 'age_1.0', 'age_2.0', 'age_3.0', 'age_4.0',
       'age_5.0', 'age_6.0', 'age_7.0', 'age_8.0', 'gender_0.0', 'gender_1.0',
       'gender_2.0'],axis=1)
#伯努利组数据
bonuli_data=np.array(data[['age_0.0', 'age_1.0', 'age_2.0', 'age_3.0', 'age_4.0',
       'age_5.0', 'age_6.0', 'age_7.0', 'age_8.0', 'gender_0.0', 'gender_1.0',
       'gender_2.0']].copy())


#使用mpmath自定义精度
import mpmath as mp
from sklearn.model_selection import train_test_split
mp.dps=256
#伯努利概率分布
sample=len(bonuli_data)
py=np.bincount(y)/len(y)
def high_precision_func(x):
    return mp.mpf(x)

# 使用vectorize函数将函数向量化
vectorized_func = np.vectorize(high_precision_func)

#伯努利组训练
for i in range(len(np.unique(y))):
    x_c=x_train_b[y_train_b==i]
    sum_row=np.sum(x_c,axis=0)
    sum_all=np.sum(x_c)
    p[i]=(sum_row+1)/(sum_all+2)
p=vectorized_func(p)


#高斯组训练
gocs_mean=[]
gocs_std=[]
for i in np.unique(y):
    px=x_train[np.where(y_train_g==i)[0]]
    print('1 ',px.shape)
    gocs_mean.append(np.mean(px,axis=0))
    gocs_std.append(np.var(px,axis=0))
gocs_mean=np.array(gocs_mean)
gocs_std=np.array(gocs_std)


#伯努利预测
y_b_p=[]
for i in p:
    y_b_p.append(i**x_test_b*(1-i)**(1-x_test_b))
y_b_p


#高斯预测
y_g_p=[]
for i in range(len(np.unique(y))):
    mi=(((-1)*(x_test-gocs_mean[i])**2+1)/((2*gocs_std[i]**2)+2))
    y_g_p.append(np.power(np.e,mi)*(1/(np.sqrt(2*np.pi)*gocs_std[i]+2)))
y_g_p

#联合预测
kinds=np.bincount(y)
final_p=[]
for i in range(len(kinds)):
    final_p.append(py[i]*np.prod(y_b_p[i],axis=1)*np.prod(y_g_p[i],axis=1))


#设置为1的阈值
xm=0
xm=final_p[1]
xn=final_p[0]
print(np.mean(final_p[1]))
print(np.mean(final_p[0]))
xm_1=np.mean(final_p[1])
xm_1+=0.65*xm_1
xm[xm>=xm_1]=1
xm[(xm<xm_1)]=0
y_pre=[]
for i in xm:
    y_pre.append(int(i))




结果,特别说明:这组数据集对分类器非常不友好,它的正类出现概率仅为0.03其他全为负类,所以对于该模型,应该更注重它的召回率和精确率:

官方sklearn的高斯贝叶斯:

由于我手动修改了阈值,但阈值是我估摸着进行设置的,所以准确率较低,召回率相较于sklearn要稍微高,这里使用的sklearn的高斯贝叶斯没有进行降维操作,对这个结果也应该有影响,但结果和sklearn的模型相差不大。模型手写成功。

 如有不足,还请各位大佬指出。博主会继续虚心学习

  • 29
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值