蓝皮书系列之 朴素贝叶斯

李航的蓝皮书《统计学习方法》,可谓是机器学习的中文经典。其中所设计的一些算法,是机器学习的基础。这篇博文将要讲述蓝皮书中的第四章朴素贝叶斯法。

Part i 算法原理

朴素贝叶斯方法的整体思路是相对容易理解的:通过样本知识,可以求得先验概率,然后根据全概率公式,计算出所求事件的概率。朴素贝叶斯的算法框架如下:
=============================朴素贝叶斯=============================
输入:样本集X,标签y
输出:某个样本的分类
==================================================================
Step.1 计算先验概率和条件概率


图片来源于《统计学习方法》

Step.2 对于给定样本,计算该样本属于各类的概率


图片来源于《统计学习方法》

Step.3 确定样本属于哪一类
图片来源于《统计学习方法》

==================================================================
虽然整个算法非常简单,但是在实际操作中会存在一些细小的问题,如
1.在计算某个条件概率时如果出现了0的情况,则很有可能会影响到厚颜概率的计算。
2.对于离散变量可以很好的计算概率,可是对于连续变量就存在问题。
面对上述两个问题,问题一通常的解决方法是加入一个系数lambda,使概率不为0。故而对Step.1 中的公式进行如下的更改:

图片来源于《统计学习方法》

问题二的通常解决方法是假设其满足某个分布,通常为正太分布,则通过样本集来估计该分布的参数。在测试集中,根据实例的数值,来计算其在该分布下的概率。

Part ii Python 代码实现

定义一个NaiveBayes(object)类,初始化参数λ=1

def __init__(self,lamda=1):
        self.lamda = lamda

训练模型,训练模型的过程,其实就是计算各个概率的过程。对于离散变量直接其概率,对于连续变量,则计算他们的均值和方差。当然为了简化,额外设置了两个输入参数class_numind,其中class_num表示类别的数目,而ind表示离散变量的标号。
在这里以一个字典的形式记录下各个概率。

def fit(self,x,y,class_num,ind):
        total_num,Len = x.shape
        Feat_Len = np.max(x,axis=0)+1
        prob = dict()
        for i in range(class_num):
            y_category_num = np.sum(y==i)   
            prob.update( {str(i+1):(y_category_num+self.lamda)/(total_num+class_num*self.lamda) } )
            for j in range(Len):
                temp_x = x[y==i,j]
                if j in ind:
                    for k in range(Feat_Len[j]):                     
                        feat_category_num = np.sum(temp_x==k)
                        prob.update( {str(100*(i+1)+10*(j+1)+k+1): (feat_category_num + self.lamda) / (y_category_num + Feat_Len[j]*self.lamda)  }  )
                if j not in ind:
                    mu = np.mean( temp_x)
                    prob.update( {str(100*(i+1)+10*(j+1)+1):mu} )
                    sigma = np.std( temp_x,ddof=1)
                    prob.update( {str(100*(i+1)+10*(j+1)+2):sigma} )
        self.prob_matrix = prob
        self.class_num = class_num
        self.ind = ind

为了使模型能够预测,定义了一个predict(self,x)的函数,直接输出预测数据的种类

def predict(self,x):
        try:
            Num,Len = x.shape
        except:
            Len = len(x)  
            Num = 1
            x = x.reshape([1,-1])
        p_pred =  np.zeros([Num,self.class_num])
        prob_matrix = self.prob_matrix
        for n in range(Num):
            for i in range(self.class_num):
                pb = prob_matrix[str(i+1)]
                print( prob_matrix[str(i+1)] )
                for j in range(Len):
                    if j in self.ind:
                        pb *=  prob_matrix[ str(100*(i+1)+10*(j+1)+x[n,j]+1) ]
                        print( prob_matrix[ str(100*(i+1)+10*(j+1)+x[n,j]+1) ] )
                    if j not in self.ind:
                        pb *= stats.norm.pdf(x[n,j], prob_matrix[str(100*(i+1)+10*(j+1)+1)], prob_matrix[str(100*(i+1)+10*(j+1)+2)])
                        print(stats.norm.pdf(x[n,j], prob_matrix[str(100*(i+1)+10*(j+1)+1)], prob_matrix[str(100*(i+1)+10*(j+1)+2)]) )
                print('==================================')
                p_pred[n,i] = pb
            
        self.predict_prob_ = p_pred
        return np.argmax(p_pred,axis=1)

Part iii 实验结果:

为了验证代码的准确性,采用了两个例子,一个是蓝皮书上的数据,另一个此网址的数据

蓝皮书训练集.png

网络数据集

令蓝皮书中的S=0,M=1,L=2,令网络数据集中的有房=1,无房=0,单身=0,已婚=1,离婚=2,拖欠贷款=1,不拖欠贷款=0,则定义的两个数据集量化后如下表示:

def data_set(self,data):
        if data == 1:
            X = np.array([[1,1,1,1,1,2,2,2,2,2,3,3,3,3,3],[1,2,2,1,1,1,2,2,3,3,3,2,2,3,3]]).T -1
            y = np.array([-1,-1,1,1,-1,-1,-1,1,1,1,1,1,1,1,-1])  
            y[y==-1]=0  
            ind= [0,1]
            x_te = np.array([1,0])
        else:
            X = np.array([[1,0,0,1,0,0,1,0,0,0],[0,1,0,1,2,1,2,0,1,0],[125,100,70,120,95,60,220,85,75,90]]).T
            y = np.array( [0,0,0,0,1,0,0,1,0,1] )
            ind = [0,1]
            x_te = np.array([0,1,120])
        return X,y,ind,x_te

对这两组数据分别进行训练和预测:

print('蓝皮书数据集')
nb = NaiveBayes()
X,y,ind,x_te = nb.data_set(1)
nb.fit(X,y,2,ind)
x_te = np.array( [[1,0]] )
p1 = nb.predict(x_te)
print( '各类概率:',p1,'识别结果:', nb.predict_prob_)

print('网络数据集')
nb = NaiveBayes(lamda=0)
X,y,ind,x_te = nb.data_set(0)
nb.fit(X,y,2,ind)
p2 = nb.predict_prob(x_te)
print( '各类概率:',p2,'识别结果:',nb.predict_prob_)

最终的结果和书本保持一致。


程序运行结果

你可能感兴趣的:(蓝皮书系列之 朴素贝叶斯)