蓝皮书系列之 朴素贝叶斯

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

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_)

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


程序运行结果
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容