Bootstrap

机器学习之朴素贝叶斯法及其python实现

写在前面

本文主要是学习记录贴,参考《统计学习方法》和部分博客完成。如有错误,欢迎积极评论指出。

背景介绍

首先,简单讲一下机器学习中的两大学派——频率学派和贝叶斯派。

频率派

频率派认为世界是确定的,即θ(表示需要求的事件)存在唯一的真值,并且这个真值是不变的,我们的目标就是找到这个真值或者说这个真值存在的范围。用个例子来说明,我们在学概统时,常说投硬币,如果我们现在投硬币100次,出现正面朝上的概率是40,那么我们用θ表示硬币正面朝上,则p(θ) = 40 / 100 = 0.4。当数据量趋于无穷大的时候,这种方法能给出准确的估计。但是在缺乏数据的时候,就可能存在很大的误差。比如现在我们投10次硬币,全部都是正面朝上的,这是我们认为正面朝上的概率就是1,这在我们的认知中,是肯定错误的。

贝叶斯派

贝叶斯派认为世界是不确定的,人们应该提前有个先验,然后通过观测数据,对这个先验进行调整,然后找到最优的用于描述这个世界的概率分布。在贝叶斯派中,有两大输入和一大输出。两大输入是先验似然,输出的是后验先验,p(θ)指的是在没有观察到数据时对θ的预先判断,可以通过给定的训练样本进行统计得到的。似然,p(x|θ)是假设θ已知后我们观察到的数据应该是什么样,也是可以根据训练样本进行统计得到的。后验,p(θ|x)也就是我们最后需要求的值。

朴素贝叶斯法

朴素贝叶斯法的核心是贝叶斯公式,其工作原理是根据通过某种方法求该事件所有可能性的发生概率,然后哪个高就是哪个。那么,这种方法是什么?

基本概念

首先是贝叶斯公式,如下:
P ( θ ∣ X ) = P ( X ∣ θ ) × P ( θ ) P ( X ) P(\theta|X)=\frac{P(X|\theta) \times P(\theta)}{P(X)} P(θX)=P(X)P(Xθ)×P(θ)
公式中,   P ( θ ) \ P(\theta)  P(θ)表示先验概率,   P ( X ∣ θ ) \ P(X|\theta)  P(Xθ)表示似然。
接下来,我们针对实际应用中,定义几个变量。首先Ck 表示不同的类,X表示给定的样本,Y表示Ck的概率。公式变换如下:
在这里插入图片描述
对于上面公式变换,可能会存在一个疑问,那就是为什么可以将里面的拆开成连乘?我们在概率论中,这个公式的使用是有前提条件的,也就是相互独立。那么这里面,是条件独立吗?事实上,特征向量之间大概率不是独立的,但是我们默认特征之间是独立的,最主要的原因就是简便计算。如果我们考虑其特征向量之间的联系,会使模型变得非常复杂,容易造成过拟合。所以我们就假设所有的特征向量都是相互独立的。

最后我们得到了如下公式:
在这里插入图片描述
针对上面上面的公式,我们会发现,对同一组训练数据,其分母部分是相同的。我们最后的目的,是要找出最大的那个类,因此,这一部分不影响大小比较,接着简化公式。
在这里插入图片描述

参数估计

在朴素贝叶斯法中,学习意味着对于参数P(Y=Ck) 和 P(X=x|Y=Ck)进行估计。常用的方法是利用极大似然估计,估计如下:
在这里插入图片描述
在这里插入图片描述

学习与分类算法

构建学习与分类算法如下:
在这里插入图片描述
在这里插入图片描述

贝叶斯估计

对于上面的算法,因为其中出现了连乘,那么就需要考虑,零的问题了。

在利用极大似然进行估计的时候,可能会出现某个值为0,那么在进行后续连乘计算的时候,就会出现问题,导致分类产生偏差。解决这个问题的一个方法就是采用贝叶斯估计。条件概率的贝叶斯估计如下:
在这里插入图片描述
其中,λ=0时,就是极大似然估计。常取λ=1,称为拉普拉斯平滑,其中S表示该项特征的可能取值。
在这里插入图片描述

python实现

import numpy as np

# 读取数据,将图片读取为785维的向量,其中前784维是28*28,最后是标签
def LoadData(filename):
    data = []
    i = 0
    file = open(filename)
    for line in file.readlines():
        curline = line.strip().split(',')
        data.append([int(int(dt) > 128) for dt in curline[1:]])   #二值化
        data[i].append(int(curline[0]))
        i += 1
    return data

# 计算似然和先验
def calProbability(trainData):
    featurenum = len(trainData[0]) - 1
    P_y = [0] * 10      # 计算先验P(θ),并存储
    for i in trainData:
        P_y[i[-1]] += 1
    for j in range(len(P_y)):
        P_y[j] = (P_y[j] + 1) / (len(trainData) + len(P_y))
    P_x_y = np.zeros((10, featurenum, 2))       # 计算似然P(x|θ)
    for i in trainData:
        for j in range(featurenum):
            P_x_y[i[-1]][j][i[j]] += 1
    for m in range(10):
        for n in range(featurenum):
            value0 = P_x_y[m][n][0]
            value1 = P_x_y[m][n][1]
            P_x_y[m][n][0] = (value0 + 1) / (value0 + value1 + 2)
            P_x_y[m][n][1] = (value1 + 1) / (value0 + value1 + 2)
    return P_y, P_x_y

# 通过朴素贝叶斯进行概率估计
# Data:测试数据
# P_y:先验
# P_x_y:似然
def Bayes(Data, P_y, P_x_y):
    predict = [0] * 10
    for i in range(10):
        p = P_y[i]
        for j in range(len(Data)):
            p = p * P_x_y[i][j][Data[j]]
        predict[i] = p
    return predict.index(max(predict))

# 测试函数
def test(testData, P_y, P_x_y):
    err = 0
    m = 0
    for i in testData:
        print('测试编号:', m)
        m += 1
        predict = Bayes(i[:-1], P_y, P_x_y)
        if predict != i[-1]:
            err += 1
    return 1 - (err / (len(testData)))


if __name__ == "__main__":   
    print('read train data')
    trainData = LoadData('Mnist/mnist_train/mnist_train.csv')

    print('read test data')
    testData = LoadData('Mnist/mnist_test/mnist_test.csv')

    print('start train model')
    P_y, P_x_y = calProbability(trainData)

    print('start test model')
    acc = test(testData, P_y, P_x_y)

    print('the acc is: ', acc)
;