【自己动手写机器学习算法】第4章 朴素贝叶斯

11 篇文章 0 订阅
11 篇文章 0 订阅

第4章 朴素贝叶斯

朴素贝叶斯的基本思想是: 基于特征条件独立假设学习输入和输出的联合概率分布,然后,基于此模型,对给定的输入 x x x,利用贝叶斯订立求出后验概率最大的输出 y y y,根据《统计学习方法》书中的规划,这里将简述朴素贝叶斯的学习和分类、朴素贝叶斯的参数估计方法。


4.1 朴素贝叶斯的学习和分类

4.1.1简述

输入:训练数据集
T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ( x i , y i ) , . . . , ( x N , y N ) } T=\{ \left( x_1,y_1 \right),\left( x_2,y_2 \right),\left( x_i,y_i \right),...,\left( x_N,y_N \right) \} T={(x1,y1),(x2,y2),(xi,yi),...,(xN,yN)}

朴素贝叶斯的目的是: 通过训练数据集 T T T学习到联合概率分布 P ( X , Y ) P(X,Y) P(X,Y),具体来讲,这个联合概率分布的求解根据联合概率密度求解的公式 P ( x ∣ y ) = P ( x , y ) P Y ( y ) P(x|y)=\frac{P(x,y)}{P_Y(y)} P(xy)=PY(y)P(x,y)又分为两个部分:

先验概率分布:
P ( Y = c k ) , k = 1 , 2 , . . . , K P(Y=c_k),k=1,2,...,K P(Y=ck),k=1,2,...,K
条件概率分布:
KaTeX parse error: Got function '\left' with no arguments as superscript at position 17: …(X=x|Y=c_k)=P(X^̲\left(1\right)=…

其中,条件独立性假设

KaTeX parse error: Got function '\left' with no arguments as superscript at position 32: …_k \right)=P( X^̲\left(1\right)=…

关于生成模型判别模型
根据上面的式子,这里由于要求解的是 P ( x ∣ y ) = P ( x , y ) P Y ( y ) P(x|y)=\frac{P(x,y)}{P_Y(y)} P(xy)=PY(y)P(x,y),实质上是一种生成数据的机制,所以属于生成模型,可能有的同学会听说过生成对抗网络(GAN)的概念,没错,里面的生成也是生成模型,在GAN中,生成模型也是用来生成数据的,但是可能它生成的数据更加复杂,不再单单是一个高斯分布了,很有可能是一张图什么的,这个是后话了,这里只要知道朴素贝叶斯是生成模型、它学习的是一种生成数据的机制,就可以了。但是话又说回来了,你都知道如何生成数据了,当然就可以知道如何对数据进行区分了,不是么哈哈~

4.1.2算法描述

好了,这里简言之,直接描述朴素贝叶斯的算法过程
输入:

  • 训练数据 T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ( x i , y i ) , . . . , ( x N , y N ) } T=\{ \left( x_1,y_1 \right),\left( x_2,y_2 \right),\left( x_i,y_i \right),...,\left( x_N,y_N \right) \} T={(x1,y1),(x2,y2),(xi,yi),...,(xN,yN)},其中,KaTeX parse error: Got function '\left' with no arguments as superscript at position 16: x_i = \left(x_i^̲\left(1\right),…;
  • KaTeX parse error: Got function '\left' with no arguments as superscript at position 4: x_i^̲\left(j\right)是第 i i i个样本的第 j j j个特征,KaTeX parse error: Got function '\left' with no arguments as superscript at position 4: x_i^̲\left(j\right) … a j l a_{jl} ajl是第 j j j个特征可能取的第 l l l个值, j = 1 , 2 , . . . , n j=1,2,...,n j=1,2,...,n l = 1 , 2 , . . . , S j l=1,2,...,S_j l=1,2,...,Sj y i ∈ { c 1 , c 2 , . . . , c K } y_i \in \left\{c_1,c_2,...,c_K \right\} yi{c1,c2,...,cK};
  • 实例 x x x;

输出:

  • 实例 x x x的分类结果

算法过程:

  1. 计算先验概率以及条件概率
    P ( Y = c k ) = ∑ i = 1 N I ( y i = c i ) N , k = 1 , 2 , . . . , K P\left( Y = c_k \right) = \frac{\sum_{i=1}^N I(y_i=c_i)}{N},k=1,2,...,K P(Y=ck)=Ni=1NI(yi=ci),k=1,2,...,K
    KaTeX parse error: Got function '\left' with no arguments as superscript at position 10: P\left( X^̲\left(j\right)=…
    j = 1 , 2 , . . . , n ; l = 1 , 2 , . . . , S j , k = 1 , 2 , . . . , K j=1,2,...,n; l=1,2,...,S_j,k=1,2,...,K j=1,2,...,n;l=1,2,...,Sj,k=1,2,...,K
  1. 对于给定的实例 x x x,计算
    KaTeX parse error: Got function '\left' with no arguments as superscript at position 26: …rod_{j=1}^n P(X^̲\left(j\right) …
  2. 确定实例 x x x的类
    KaTeX parse error: Got function '\left' with no arguments as superscript at position 40: …x} P(Y=c_k) P(X^̲\left(j\right) …

4.1.3编写代码测试朴素贝叶斯算法

# 给定数据集,其中SML分别替换为1,2,3
import numpy as np
x = np.asarray([[1,1],[1,2],[1,2],[1,1],[1,1],
      [2,1],[2,2],[2,2],[2,3],[2,3],
      [3,3],[3,2],[3,2],[3,3],[3,3]])
y = np.asarray([-1,-1,1,1,-1,
     -1,-1,1,1,1,
     1,1,1,1,-1])

计算 P ( Y = 1 ) P(Y=1) P(Y=1) P ( Y = − 1 ) P(Y=-1) PY=1

AllNum = np.size(y)
# P(Y=1)
PosY = np.sum(y==1)/AllNum
#P(Y=-1)
NegY = np.sum(y==-1)/AllNum
print('P(Y=1)=',PosY,', P(Y=-1)=',NegY)

P(Y=1)= 0.6 , P(Y=-1)= 0.4

计算KaTeX parse error: Got function '\left' with no arguments as superscript at position 10: P\left( X^̲\left(j\right)=…

# y 取值范围
y_ = np.unique(y)
# x 的维度
feanum = np.size(x,1)
min_result_dictionary = {}
for k in range(len(y_)):# 对于每个Y类别
    P_Y = np.sum(y==y_[k])/np.size(y) # 计算P(Y=k)的值
    print('P(Y =',y_[k],') =',P_Y)
    k_x = x[y==y_[k],:]# 保留在P(Y=k)条件下所有的x样本
    for j in range(feanum): # 对于每个特征
        fea_val_range = np.unique(x[:,j])# 该维度下属性值的取值范围
        for l in range(len(fea_val_range)): # 对于特征下的第l个取值
            P_X_Y = np.sum(k_x[:,j] == fea_val_range[l])/np.sum(y==y_[k])
            print('P(X特征维度=',j,'特征值=',fea_val_range[l],'| Y=',y_[k],')=',P_X_Y)
            split = ','
            key = split.join([str(y_[k]),str(j),str(fea_val_range[l])])
            min_result_dictionary[key] = P_X_Y
min_result_dictionary

输出:
P(Y = -1 ) = 0.4
P(X特征维度= 0 特征值= 1 | Y= -1 )= 0.5
P(X特征维度= 0 特征值= 2 | Y= -1 )= 0.333333333333
P(X特征维度= 0 特征值= 3 | Y= -1 )= 0.166666666667
P(X特征维度= 1 特征值= 1 | Y= -1 )= 0.5
P(X特征维度= 1 特征值= 2 | Y= -1 )= 0.333333333333
P(X特征维度= 1 特征值= 3 | Y= -1 )= 0.166666666667
P(Y = 1 ) = 0.6
P(X特征维度= 0 特征值= 1 | Y= 1 )= 0.222222222222
P(X特征维度= 0 特征值= 2 | Y= 1 )= 0.333333333333
P(X特征维度= 0 特征值= 3 | Y= 1 )= 0.444444444444
P(X特征维度= 1 特征值= 1 | Y= 1 )= 0.111111111111
P(X特征维度= 1 特征值= 2 | Y= 1 )= 0.444444444444
P(X特征维度= 1 特征值= 3 | Y= 1 )= 0.444444444444
{’-1,0,1’: 0.5,
‘-1,0,2’: 0.33333333333333331,
‘-1,0,3’: 0.16666666666666666,
‘-1,1,1’: 0.5,
‘-1,1,2’: 0.33333333333333331,
‘-1,1,3’: 0.16666666666666666,
‘1,0,1’: 0.22222222222222221,
‘1,0,2’: 0.33333333333333331,
‘1,0,3’: 0.44444444444444442,
‘1,1,1’: 0.1111111111111111,
‘1,1,2’: 0.44444444444444442,
‘1,1,3’: 0.44444444444444442}

当给定 x = ( 2 , S ) T x=(2,S)^T x=(2,S)T,也即 x = ( 2 , 1 ) T x=(2,1)^T x=(2,1)T,计算KaTeX parse error: Got function '\left' with no arguments as superscript at position 26: …rod_{j=1}^n P(X^̲\left(j\right) …

testx = np.array([2,1])
labelvalue =[]
for k in range(len(y_)):
    P_Y = np.sum(y==y_[k])/np.size(y) # 计算P(Y=k)的值
    j_p = 1# 记录每个类别下的概率值
    for j in range(feanum):
        split = ','
        key = split.join([str(y_[k]),str(j),str(testx[j])])
        j_p = j_p * min_result_dictionary[key]
    labelvalue.append(P_Y*j_p)

返回值最大时候得到的类别标签KaTeX parse error: Got function '\left' with no arguments as superscript at position 40: …x} P(Y=c_k) P(X^̲\left(j\right) …

y_[labelvalue.index(max(labelvalue))]

输出:-1
综上可以得知,实际上朴素贝叶斯是在给定每个类别标签的情况下,查看这个数据“合理性”有多高,“合理性”越高,说明越有可能是这个类别,而这个“合理性”的度量,就是使用了概率来衡量,最后动过一个 arg ⁡ max ⁡ \arg \max argmax操作,得到了对应的类别标签。

4.1.4贝叶斯估计

用心的读者也许会发现,如果用以上的方法(实际是“极大似然估计”)可能会得到概率值为0的情况,然后会影响后续一系列操作(如求最大值等)。根据《统计学习方法》一书,我们在这里加一个步骤,将原来的式子做小的修改:
KaTeX parse error: Got function '\left' with no arguments as superscript at position 10: P\left( X^̲\left(j\right)=…
其中, λ ≥ 0 \lambda \geq 0 λ0,当 λ = 0 \lambda = 0 λ=0的时候等同于原始的极大似然估计,而通常,这里取 λ = 1 \lambda = 1 λ=1,称为拉普拉斯平滑,这时,对于 l = 1 , 2 , . . . , S j l=1,2,...,S_j l=1,2,...,Sj k = 1 , 2 , . . . , K k=1,2,...,K k=1,2,...,K,有
KaTeX parse error: Got function '\left' with no arguments as superscript at position 12: P_\lambda(X^̲\left(j\right)=…
KaTeX parse error: Got function '\left' with no arguments as superscript at position 28: …S_j}P_\lambda(X^̲\left(j\right)=…

这两个式子说明拉普拉斯平滑之后的式子确实是一个概率分布(即有每个概率大于0,总概率和等于1),此时,得到鲜艳概率贝叶斯估计为:
P λ ( Y = c k ) = ∑ i = 1 N I ( y i = c k ) + λ N + K λ P_\lambda(Y=c_k)=\frac{\sum_{i=1}^N I(y_i=c_k)+\lambda}{N+K\lambda} Pλ(Y=ck)=N+Kλi=1NI(yi=ck)+λ

此时我们修改上面的代码,运行可以得到如下结果

# y 取值范围
y_ = np.unique(y)
# x 的维度
feanum = np.size(x,1)
lmda = 1
min_result_dictionary = {}
for k in range(len(y_)):# 对于每个Y类别
    P_Y = (np.sum(y==y_[k])+lmda)/(np.size(y)+lmda*len(y_)) # 计算P(Y=k)的值
    print('P(Y =',y_[k],') =',P_Y)
    k_x = x[y==y_[k],:]# 保留在P(Y=k)条件下所有的x样本
    for j in range(feanum): # 对于每个特征
        fea_val_range = np.unique(x[:,j])# 该维度下属性值的取值范围
        for l in range(len(fea_val_range)): # 对于特征下的第l个取值
            P_X_Y = (np.sum(k_x[:,j] == fea_val_range[l])+lmda)/(np.sum(y==y_[k])+lmda*len(fea_val_range))
            print('P(X特征维度=',j,'特征值=',fea_val_range[l],'| Y=',y_[k],')=',P_X_Y)
            split = ','
            key = split.join([str(y_[k]),str(j),str(fea_val_range[l])])
            min_result_dictionary[key] = P_X_Y
min_result_dictionary

输出:
P(Y = -1 ) = 0.411764705882
P(X特征维度= 0 特征值= 1 | Y= -1 )= 0.444444444444
P(X特征维度= 0 特征值= 2 | Y= -1 )= 0.333333333333
P(X特征维度= 0 特征值= 3 | Y= -1 )= 0.222222222222
P(X特征维度= 1 特征值= 1 | Y= -1 )= 0.444444444444
P(X特征维度= 1 特征值= 2 | Y= -1 )= 0.333333333333
P(X特征维度= 1 特征值= 3 | Y= -1 )= 0.222222222222
P(Y = 1 ) = 0.588235294118
P(X特征维度= 0 特征值= 1 | Y= 1 )= 0.25
P(X特征维度= 0 特征值= 2 | Y= 1 )= 0.333333333333
P(X特征维度= 0 特征值= 3 | Y= 1 )= 0.416666666667
P(X特征维度= 1 特征值= 1 | Y= 1 )= 0.166666666667
P(X特征维度= 1 特征值= 2 | Y= 1 )= 0.416666666667
P(X特征维度= 1 特征值= 3 | Y= 1 )= 0.416666666667
{’-1,0,1’: 0.44444444444444442,
‘-1,0,2’: 0.33333333333333331,
‘-1,0,3’: 0.22222222222222221,
‘-1,1,1’: 0.44444444444444442,
‘-1,1,2’: 0.33333333333333331,
‘-1,1,3’: 0.22222222222222221,
‘1,0,1’: 0.25,
‘1,0,2’: 0.33333333333333331,
‘1,0,3’: 0.41666666666666669,
‘1,1,1’: 0.16666666666666666,
‘1,1,2’: 0.41666666666666669,
‘1,1,3’: 0.41666666666666669}

testx = np.array([2,1])
labelvalue =[]
for k in range(len(y_)):
    P_Y = np.sum(y==y_[k])/np.size(y) # 计算P(Y=k)的值
    j_p = 1# 记录每个类别下的概率值
    for j in range(feanum):
        split = ','
        key = split.join([str(y_[k]),str(j),str(testx[j])])
        j_p = j_p * min_result_dictionary[key]
    labelvalue.append(P_Y*j_p)
print(labelvalue)

输出:[0.059259259259259262, 0.033333333333333333]

返回值最大时候得到的类别标签

-1
注:上面由于浮点精度问题,这里的结果与《统计学习方法》书中的结果存在一点差异,但是原理就是这样了!


4.2 定义自己的朴素贝叶斯算法

# 4.2定义自己的朴素贝叶斯算法
import numpy as np
class myNaiveBayes():
    def train(self,x,y,lmda=None):
        """
        x: 训练集  
        y: 类别标签向量
        lmda: 拉普拉斯平滑参数值,默认为1
        """
        self.x = x
        self.y = y
        self.lmda = lmda
        model = []
        # y 取值范围
        y_ = np.unique(y)
        # x 的维度
        feanum = np.size(x,1)
        min_result_dictionary = {}
        P_Y = np.empty(len(y_))
        for k in range(len(y_)):# 对于每个Y类别
            P_Y[k] = np.sum(y==y_[k])/np.size(y) # 计算P(Y=k)的值
            k_x = x[y==y_[k],:]# 保留在P(Y=k)条件下所有的x样本
            for j in range(feanum): # 对于每个特征
                fea_val_range = np.unique(x[:,j])# 该维度下属性值的取值范围
                for l in range(len(fea_val_range)): # 对于特征下的第l个取值
                    P_X_Y = (np.sum(k_x[:,j] == fea_val_range[l])+lmda)/(np.sum(y==y_[k])+lmda*len(fea_val_range))
                    split = ','
                    key = split.join([str(y_[k]),str(j),str(fea_val_range[l])])
                    min_result_dictionary[key] = P_X_Y
        
        model.append(feanum) # 特征维度数目  0
        model.append(y_) # 类别取值范围 1
        model.append(min_result_dictionary) # dictionary保存的概率值 2
        model.append(np.size(y)) # 样本个数 3
        model.append(P_Y)
        return model
    
    def predict(self,x,model):
        """
        x 应该与train中的x具有相同的维度数
        y 为预测类别标签
        """
        y = []
        self.model = model
        self.x = x
        if self.x.ndim==1:
            self.x = self.x.reshape(1,len(x))
        for i in range(len(self.x)):
            testx = np.asarray(self.x[i,:])
            labelvalue =[]
            for k in range(len(self.model[1])):
                P_Y = model[4][k]
                j_p = 1# 记录每个类别下的概率值
                for j in range(model[0]):
                    split = ','
                    key = split.join([str(model[1][k]),str(j),str(testx[j])])
                    j_p = j_p * model[2][key]
                labelvalue.append(P_Y*j_p)
            y.append(y_[labelvalue.index(max(labelvalue))])
        return y
    
    def accuracy(self,y,realy):
        self.y = y
        self.realy = realy
        return 1-sum(np.sign(np.abs(y-realy)))/len(y)

定义课本的15个实例

x = np.asarray([[1,1],[1,2],[1,2],[1,1],[1,1],
      [2,1],[2,2],[2,2],[2,3],[2,3],
      [3,3],[3,2],[3,2],[3,3],[3,3]])
y = np.asarray([-1,-1,1,1,-1,
     -1,-1,1,1,1,
     1,1,1,1,-1])

验证课本的15个实例

mynb = myNaiveBayes()
model = mynb.train(x,y,lmda=1)
newy = mynb.predict(x,model)
print('正确率:',mynb.accuracy(y,newy))

正确率: 0.733333333333

课本测试用例

temp = np.asarray([2,1])
newy = mynb.predict(temp,model)
print(newy)

[-1]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值