朴素贝叶斯算法的Python实现(多项式、高斯)

@[TOC]朴素贝叶斯算法的Python实现(多项式、高斯)

朴素贝叶斯算法

贝叶斯定理、特征独立假设是朴素贝叶斯算法的重要理论基础
朴素贝叶斯有三种模型:多项式模型、高斯模型。这三种模型最大的不同之处就在于求条件概率的方式,该条件概率指的是:P(X =x|Y = ck),x是样本的其中一个特征,ck是labels的其中一个类别。
朴素贝叶斯是一个基本的、简单的机器学习算法,也是很重要很经典的算法。听起来感觉都懂,但是真真用代码实现起来,还是会发现各种各样的问题,最重要的是在用代码实现算法的过程中,一会加强对算法的理解,二是会提高编程能力。

本渣渣最开始的实现结果

class NaiveBayes(object):
    def getTrainSet(self):
        dataSet = pd.read_csv('./naivebayes_data.csv')
        #print(dataSet)
        dataSetNP = np.array(dataSet)  #将数据由dataframe类型转换为数组类型
        #print(dataSetNP)
        trainData = dataSetNP[:,0:dataSetNP.shape[1]-1]   #训练数据x1,x2
        labels = dataSetNP[:,dataSetNP.shape[1]-1]        #训练数据所对应的所属类型Y
        return trainData, labels

    def classify(self, trainData, labels, features):
        #求labels中每个label的先验概率
        labels = list(labels)    #转换为list类型
        P_y = {}#存入label的概率
        for label in labels:#计算每一个类别的概率
            P_y[label] = labels.count(label)/float(len(labels)) 
        print(P_y)
        '''
        ******************极大似然估计----朴素贝叶斯*******************
        D = {x1,x2,X3........}
        argmax P(a|D):给定D的情况下,起一个a参数使得概率最大
        根据贝叶斯定理:p(a|D) = (p(D|a)p(a))/p(D)
        
        概率派认为p(a)不变,且p(D)也已知,因此可以转换为 argmax P(D|a),“朴素”假设D的特征x1,x2....相互独立\
        一次上式可以转换为:argmax P(D|a)=argmax {P(x1|a)P(x2|a)P(x3|a)......}

        ****************************************************************
       
        
       由于假设各特征之间相符独立,所以P(X|y) = P(x1|y)P(x2|y) 
       那么如何求P(x1|y)P(x2|y)????
       统计得到各类别下各特征属性的条件概率
        '''
        #后验概率
        P_x_y = {}
        for y in P_y.keys():
            y_index = [i for i, label in enumerate(labels) if label == y]  # labels中出现y值的所有数值的下标索引
            #找出标签为  -1 的所有索引分别为0, 1, 4, 5, 6, 14
            #找出标签为  1 的所有索引分别为2, 3, 7, 8, 9, 10, 11, 12, 13
            print('标签',y,'的长度',len(y_index))
            for j in range(len(features)):  # features[0] 在trainData[:,0]中出现的值的所有下标索引
                x_index = [i for i, feature in enumerate(trainData[:,j]) if feature == features[j]]
                #找出训练数据中第一列 与 测试数据第一列相等 的索引分别为:5, 6, 7, 8, 9
                #找出训练数据中第一列 与 测试数据第二列相等 的索引分别为:0, 3, 4, 5
                '''
                j =0:trainData[:,j]取出训练数据的第一列,如果第一列的值与测试数据第一列的值相等就得到该测试集数据的索引
                j =1:.................................................................................................
                '''
                
                x_y_count = len(set(x_index) & set(y_index))   # set(x_index)&set(y_index)列出两个表相同的元素
                '''
                外部第一次循环
                第一次:2|-1----->x_index与y_index重合的索引有5,6,因此,内第一次循环 得到P(2|-1) = 2/6
                第二次:S|-1----->x_index与y_index重合的索引有0,4,5,因此,内第二次新婚换 得到P(S|-1) = 3/6
                外部第二次循环
                第一次:2|1----->x_index与y_index重合的索引有7,8,9,因此,内第一次循环 得到P(2|1) = 3/9
                第二次:S|1----->x_index与y_index重合的索引有3,因此,内第二次新婚换 得到P(S|1) = 1/9
                '''
                pkey = str(features[j]) + '|' + str(y)
                #print(pkey)
                P_x_y[pkey] = x_y_count / float(len(y_index))#公式(1)
                print(P_x_y)
        
        #求[2,'S']所属类别
        F = {}   #[2,'S']属于各个类别的概率
        for y in P_y:
            F[y] = P_y[y]
            #print(F)
            for x in features:
                F[y] = F[y]*P_x_y[str(x)+'|'+str(y)]     #P[y/X] = P[X/y]*P[y]/P[X],分母相等,比较分子即可,所以有F=P[X/y]*P[y]=P[x1/Y]*P[x2/Y]*P[y]
        print(F)
        features_label = max(F, key=F.get)  #概率最大值对应的类别
        return features_label


if __name__ == '__main__':
    nb = NaiveBayes()
    # 训练数据
    trainData, labels = nb.getTrainSet()
    #print(trainData[:,0])
    print(type(labels))
    features = [3,'L']
    # 该特征应属于哪一类
    result = nb.classify(trainData, labels, features)
    print(features,'属于',result)

多项式模型

多项式模型其实就是前面简单实现的原理
######贝叶斯估计----朴素贝叶斯------多项式模型(特征离散)###########
P_x_y[pkey] = x_y_count / float(len(y_index)) 公式(1)
条件概率的贝叶斯估计:
P_x_y[pkey] = (x_y_count + A) / (float(len(y_index))+Si*A)公式(2),Si表示每一维特征可能取值的个数
其中A>=0,A=0时就是极大似然估计,A=1时,叫拉普拉斯平滑

作用:例如某一维特征在训练样本中没有出现过,则会导致y(xi|y) = 0,导致后验概率为零,加上平滑就能克服这一问题

#################################################

class MultinomialNB(object):
    def __init__(self,alpha = 1.0,fit_prior=True):
        self.alpha = alpha
        self.fit_prior = fit_prior
        self.class_prior = None
        self.classes = None
        self.conditional_prob = None
        self.predict_prob = None
        '''
        fit_class:是否学习类的先验概率,False则使用统一的先验
        class_prior:类的先验概率,如果指定,则先验不能根据数据调整
    
    '''
  	def fit(self,x,y):
	        #计算类别y的先验概率
	        self.classes = np.unique(y)
	
	        if self.class_prior == None:#先验概率没有指定
	            class_num = len(self.classes)
	            if not self.fit_prior:
	                self.class_prior = [1.0/class_num for i in range(class_num)]
	            else:
	                self.class_prior = {}
	                for d in self.classes:
	                    c_num = np.sum(np.equal(y,d))
	                    self.class_prior[d]=(c_num+self.alpha) / (float(len(y) + class_num * self.alpha))
	        #print(self.class_prior)           
	        #计算条件概率------多项式
	        self.conditional_prob = {}#{x1|y1:p1,x2|y1:p2,.....,x1|y2:p3,x2|y2:p4,.....}
	        y = list(y)
	        for yy in self.class_prior.keys():
	            y_index = [i for i,label in enumerate(y) if label == yy]
	        #print(y_index)#标签的先验概率
	            for i in range(len(x)):
	                x_class = np.unique(x[i])
	                for c in list(x_class):
	                    x_index = [x_i for x_i,value1 in enumerate(list(x[i])) if value1 == c]
	                    xy_count = len(set(x_index) & set(y_index))
	                    pkey = str(c) + '|' + str(yy)
	                    self.conditional_prob[pkey] = (xy_count+self.alpha) / (float(len(y_index))+len(list(np.unique(x[i]))))
	        return self


    def predict(self,X_test):#此处只能对一个样本输入测试,加循环可以多个样本一个测试,高斯模型有实现
        self.predict_prob = {}
        for i in self.classes:
            self.predict_prob[i] = self.class_prior[i]
            
            for d in X_test:
                tkey = str(d) + '|'+ str(i)
                self.predict_prob[i] = self.predict_prob[i]*self.conditional_prob[tkey]
        label = max(self.predict_prob, key=self.predict_prob.get)
        return label

测试结果

X = np.array([
                      [1,1,1,1,1,2,2,2,2,2,3,3,3,3,3],
                      ['S','M','M','S','S','S','M','M','L','L','L','M','M','L','L']
             ])
#X = X.T
y = np.array([-1,-1,1,1,-1,-1,-1,1,1,1,1,1,1,1,-1])

mnb = MultinomialNB(alpha=1.0,fit_prior=True)
mnb.fit(X,y)

高斯模型

###############朴素贝叶斯----高斯模型(特征连续)##############
处理方法:
当特征是连续变量的时候,不采取平滑使用多项式模型就会导致很多P(xi|yk)=0,即使使用了平滑,得到的后验概率也不能描述真实情况,因此采用高斯模型。
步骤 1.计算每个特征的平均值和方差,得到正态分布的密度函数,通过密度函数就能计算的到测试数据的密度函数值
2.将密度函数值作为条件概率的值
3.通过朴素贝叶斯方法计算测试样本所属类别

class GaussionNB(object):#计算条件概率的方法不一样
def __init__(self,fit_prior=True):
    self.fit_prior = fit_prior
    self.class_prior = None
    self.classes = None
    self.mean = None
    self.var = None
    self.predict_prob = None
    '''
    fit_class:是否学习类的先验概率,False则使用统一的先验
    class_prior:类的先验概率,如果指定,则先验不能根据数据调整
    
    '''

def fit(self,x,y):
    #计算类别y的先验概率
    self.classes = np.unique(y)

    if self.class_prior == None:#先验概率没有指定
        class_num = len(self.classes)
        if not self.fit_prior:
            self.class_prior = [1.0/class_num for i in range(class_num)]
        else:
            self.class_prior = {}
            for d in self.classes:
                c_num = np.sum(np.equal(y,d))
                self.class_prior[d]=(c_num) / (float(len(y)))
    #print(self.class_prior)           
    #计算条件概率------多项式
    self.mean = {}
    self.var = {}
    y = list(y)
    for yy in self.class_prior.keys():
        y_index = [i for i,label in enumerate(y) if label == yy]
    #print(y_index)#标签的先验概率
        for i in range(len(x)):
            x_class =[]
            for ii in y_index:
                x_class.append(x[i][ii])
                #print(x_class)
                pkey = '特征'+str(i)+'|'+'类别'+str(yy)
                mean = np.mean(x_class)
                var = np.var(x_class)
                self.mean[pkey] = mean
                self.var[pkey] = var
    return self
def _calculat_prob_gaussion(self,mu,sigma,x):
    
    prob = ( 1.0/(sigma * np.sqrt(2 * np.pi)) *
                    np.exp( - (x - mu)**2 / (2 * sigma**2)) )
    return prob

def predict(self,X_test):
    labels = []
    self.predict_prob = []
    predict_prob_ = {}
    for d in range(len(X_test)):
        x_test = X_test[d]
        #print(x_test)
        for yy in self.class_prior.keys():
            predict_prob_[yy] = self.class_prior[yy]
            for i,x in  enumerate(list(x_test)):
                #print(x)
                tkey = '特征'+str(i)+'|'+'类别'+str(yy)
                #print(tkey)
                mu = self.mean[tkey] 
                #print('mu值',mu)
                sigma = self.var[tkey] 
                #print('sigma值',sigma)
                prob = self._calculat_prob_gaussion(mu,sigma,x)
                #print(prob)
                predict_prob_[yy] = predict_prob_[yy]*prob
                #print(predict_prob_[yy])
        
        print(predict_prob_)
        new_predict_prob_ = predict_prob_.copy()
        self.predict_prob.append(new_predict_prob_)
        label = max(predict_prob_, key=predict_prob_.get)
        labels.append(label)   
    return labels

伯努利模型

伯努利模型以为是针对于离散特征的情况,伯努利模型中每个特征的取值只能是1和0,本文中没有做实现,和前面情况类似,做一些小改动就好。

伯努利模型中,条件概率P(xi|yk)计算方式是:

当特征值xixi为1时,P(xi|yk)=P(xi=1|yk)P(xi|yk)=P(xi=1|yk);

当特征值xixi为0时,P(xi|yk)=1−P(xi=1|yk)P(xi|yk)=1−P(xi=1|yk)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值