机器学习教程 之 朴素贝叶斯分类器:判别皮马印第安糖尿病人数据集

本科的时候就看过贝叶斯判别和贝叶斯分类,趁这次正好找到了一个比较合适的数据集—皮马印第安糖尿病人数据集,就顺手写了一个,写出来发现正确率还可以,思路也比较清晰,为了便于大家理解,在讲解时我尽量给代码多加一些注释

一、贝叶斯判别
二、朴素贝叶斯分类器
三、拉普拉斯修正
四、皮马印第安糖尿病人数据集
五、朴素贝叶斯分类器的python实现

一、贝叶斯判别
从统计学的角度来看,机器学习所要实现的是基于有限的训练样本集尽可能准确地估计出后验概率P(C | X),大体上来说,主要有两种策略
1)给定 X 可直接通过建模P(C | X)来预测C,这样得到的是“判别式模型”(discriminative models),比如决策树、BP神经网络、支持向量机等
2)先对联合概率分布P(C , X)建模,然后再由此获得P(C | X),这样得到的是“生成式模型”(generative models)
对于生成式模型,根据贝叶斯公式*P(C | X)*可以写为

这里写图片描述

P(C)是先验概率;P(X|C)是样本X相对于类标记C的类条件概率,或称为“似然”;P(X)是由于归一化的“证据因子”。对给定的样本X,证据因子P(X)与类标记无关,因此估计P(C|X)的问题就转化为如何基于训练集D来估计先验概率P(C)和似然P(X|C)

对于先验概率P(C)和似然P(X|C)的计算,有:
1)类先验概率
P(C)表达了样本空间中各类样本所占的比例,根据大数定律,当训练集包含充足的独立同分布样本时,P(C)可通过各类样本出现的频率来进行估计
2)对于类条件概率
P(X|C)来说,由于它涉及关于X所有属性的联合概率,直接根据样本频率来估计会遇到严重的困难。例如,假设样本中有d
个属性都是二值的,则样本空间将会有2的
d
次方中可能取值,在现实应用中这个值往往远大于训练样本数。

二、朴素贝叶斯分类器
根据贝叶斯公式来估计后验概率*P(C|X)的主要困难在于:类条件概率P(X|C)*是所有属性上的联合概率,难以直接从样本直接获得。为了避开这个障碍,朴素贝叶斯分类器(naive bayes classifier)采用了“属性条件独立性假设”,即:对已知类别,假设所有属性相互独立
根据该假设,即可将贝叶斯公式重写为

这里写图片描述

其中d为属性数目,XiX在第i个属性上的取值,由于对所有类别来说*P(X)*相同,因此基于贝叶斯判别可得朴素贝叶斯分类器的表达式如下

这里写图片描述

1)先验概率计算公式如下

这里写图片描述

上式中Dc表示训练集D中第c类样本组成的集合

2)条件概率*P(Xi|C)*针对离散属性和连续属性有不同的计算公式
对于离散属性有

这里写图片描述

上式中Dc,xi表示Dc中在第i 个属性上取值为Xi的样本组成的集合
对于连续属性,考虑概率密度函数有

这里写图片描述

其中的两个参数分别是第c类样本在第i个属性上取值的均值和方差

三、拉普拉斯修正
由第二节根据合适的数据集即可得到贝叶斯分类器的分类结果,但是,若某个属性值在训练集中没有与某个类同时出现过,则直接基于条件概率与先验概率的计算公式进行计算,将出现不论其它属性如何,该类的判别概率都为0的问题
为了避免避免其它属性携带的信息被训练集中未出现的属性值归0,在估计概率值时要使用“平滑”的手段,通常使用的方法为拉普拉斯修正
现在用N表示训练集D中的类别数,Ni表示第i个属性的取值数,则可以将条件概率和先验概率的计算公式更新为

这里写图片描述
这里写图片描述

四、皮马印第安糖尿病人数据集
数据的下载地址
https://github.com/LiangjunFeng/Machine-Learning/blob/master/pima-indians-diabetes%20dataset
————————————————————————————
每个例子的8个属性依次为:
怀孕次数
口服葡萄糖耐量试验中血浆葡萄糖浓度
舒张压(mm Hg)
三头肌组织褶厚度(mm)
2小时血清胰岛素(μU/ ml)
体重指数(kg/(身高(m))^ 2)
糖尿病系统功能
年龄(岁)

标签为:是否患病

注: 属性中0表示缺失值

算法预测标准精度大约为65%,最好分类精度约为77%
————————————————————————————
对于数据的预处理如下:

#!/usr/bin/env python3
# -*-coding: utf-8-*-
# Author : LiangjunFeng
# Blog   : http://my.csdn.net/Liangjun_Feng
# GitHub : https://www.github.com/LiangjunFeng
# File   : naive_bayes_classifier.py
# Date   : 2017/09/21 1:29
# Version: 0.1
# Description: naive_bayes_classifier
 
from pandas import read_csv
import numpy

def loadData():      #读取CSV格式数据文件
    dataSet = read_csv('/Users/zhuxiaoxiansheng/Desktop/data_set/pima-indians-diabetes/pima-indians-diabetes.csv',header = None)
    return dataSet

def dataPrepreocess(dataSet):   #数据预处理,将属性与标签分开,同时以中值填充缺少数据,将数据转化为矩阵格式
    dataSet[[0,1,2,3,4,5,6,7]] = dataSet[[0,1,2,3,4,5,6,7]].replace(0,numpy.NaN)
    data = dataSet[[0,1,2,3,4,5,6,7]]
    data.fillna(data.median(),inplace = True)              #以中值填充空白
    label = dataSet[8]                  
    data = numpy.matrix(data.as_matrix(columns=None))      #转化为矩阵
    label = numpy.matrix(label.as_matrix(columns=None))    #转化为矩阵   
    return data,label.T

def dataSplit(data,label):      #将数据分为测试集和训练集
    mid = int(len(data)*0.6)    
    trainData = data[0:mid]     #前60%为训练集   
    testData = data[mid:]       #剩下的为测试集
    
    trainLabel = label[0:mid,0]
    testLabel = label[mid:,0]
    return trainData,trainLabel,testData,testLabel        #返回训练集、训练标签、测试集、测试标签

输出的数据形式如下

这里写图片描述

五、朴素贝叶斯分类器的python实现
完整代码地址
https://github.com/LiangjunFeng/Machine-Learning/blob/master/5.naive_bayes_classifier.py

贝叶斯分类器代码

# -*-coding: utf-8-*-
# Author : LiangjunFeng
# Blog   : http://my.csdn.net/Liangjun_Feng
# GitHub : https://www.github.com/LiangjunFeng
# File   : naive_bayes_classifier.py
# Date   : 2017/09/21 1:29
# Version: 0.1
# Description: naive_bayes_classifier

def featureIterator(Data):                          #输入数据矩阵属性的迭代器,即每次返回矩阵的一列
    sampleNumbers,featureNumbers = Data.shape
    for i in range(featureNumbers):
        yield trainData[:,i]
        
def itemIterator(Data):         #输入数据矩阵例子的迭代器,即每次返回矩阵的一行
    for i in range(len(Data)):
        yield numpy.ravel(Data[i,:])

def average(feature,i,trainLabel):                 #求一种类下某种属性的平均值
    sum_,count = 0,0
    for j in range(len(feature)):
        if trainLabel[j] == i:
            sum_ += feature[j]
            count += 1
    return float(sum_/count)

def variance(feature,i,trainLabel):               #求一种类下某种属性的方差
    ave = average(feature,i,trainLabel)
    sum_,count = 0,0
    for j in range(len(feature)):
        if trainLabel[j] == i:
            sum_ += (feature[j]-ave)**2
            count += 1
    return float(sum_/count)

def gaussDistribute(x,average,variance):          #计算连续属性的高斯分布函数
    part1 = 1/((2*numpy.pi)**0.5*(variance**0.5))
    part2 = numpy.exp(-(x-average)**2/(2*variance**2))
    return part1/part2 

def assess(result,label):                         #评估预测结果与实际结果是否相同的函数
    count = 0
    for i in range(len(result)):
        if result[i] == label[i][0]:
            count += 1
    return count/len(label)

class naive_bayes_classifier():                   #贝叶斯分类器
    def __init__(self):
        self._classnumber = 0                     #记录分类标签数
        self._featurenumber = 0                   #记录属性数
        self._samplenumber = 0                    #记录测试集样本数
        self._priorProbility = []                 #先验分布查询表
        self._unionProbility = {}                 #使用字典建立的联合分布表
    
    def get_priorProbility(self,Label):           #计算先验分布,使用了拉普拉斯修正
        priorProbility = [0]*self._classnumber
        for i in range(len(Label)):               #统计每个种类的样本数
            priorProbility[int(Label[i])] += 1
        for i in range(self._classnumber):        #计算先验分布
            priorProbility[i] = (priorProbility[i]+1)/(len(Label)+self._classnumber) 
        self._priorProbility = priorProbility

    def get_unionProbility(self,trainData,trainLabel):     #计算联合分布,使用了拉普拉斯修正
        unionProbility = {} 
        classgroup = [0]*self._classnumber        #用于存储每组样本数

        for i in range(len(trainLabel)):
            classgroup[int(trainLabel[i])] += 1   #统计每个种类的样本数

        for i in range(self._classnumber):        #先建立一个联合分布二重循环嵌套存储字典,第三重在下一个循环建立
            unionProbility[i] = {}
            for j in range(self._featurenumber):
                unionProbility[i][j] = {}
  
        for i in range(self._classnumber):        #核心循环,建立联合分布概率查询字典
            for feature,j in zip(featureIterator(trainData),range(self._featurenumber)):
                if j == 0 :                       #第0个属性为离散变量,单独处理
                    low = int(min(feature.tolist())[0])
                    high = int(max(feature.tolist())[0])
                    k = low
                    while k <= high:
                        if k not in unionProbility[i][j]:     
                            unionProbility[i][j][k] = 1     
                        for p in range(self._samplenumber):
                            if int(feature[p]) == k:
                                unionProbility[i][j][k] += 1                   
                        unionProbility[i][j][k] /= float(classgroup[i]+high-low+1)
                        k += 1
                else:
                    unionProbility[i][j][average] = average(feature,i,trainLabel)      #连续属性只需要存储均值和方差
                    unionProbility[i][j][variance] = variance(feature,i,trainLabel)

        self._unionProbility = unionProbility
       
    def fit(self,trainData,trainLabel):           #拟合函数
        self._classnumber = max(numpy.ravel(trainLabel.tolist()))+1
        self._samplenumber,self._featurenumber = trainData.shape
        self.get_priorProbility(trainLabel)
        self.get_unionProbility(trainData,trainLabel)
    
    def predict(self,testData):                   #预测函数
        predictResult = []
        res = [1]*self._classnumber
        for item in itemIterator(testData):       #计算每个样本对应不同分类的可能性概率
            for i in range(self._classnumber):
                res[i] = self._priorProbility[i]
                for j in range(self._featurenumber):
                    if j == 0 :
                        res[i] *=  self._unionProbility[i][j][item[j]]
                    else:
                        res[i] *= gaussDistribute(item[j],self._unionProbility[i][j][average],self._unionProbility[i][j][variance])
            maxp = max(res)
            for i in range(self._classnumber):    #判定为概率最大的种类
                if maxp == res[i]:
                    kind = i
                    predictResult.append(kind)
        return  predictResult


if __name__ == '__main__':
    dataSet = loadData()
    data,label = dataPrepreocess(dataSet)
    trainData,trainLabel,testData,testLabel = dataSplit(data,label)
    
    nbc = naive_bayes_classifier()
    nbc.fit(trainData,trainLabel)
    res = nbc.predict(testData)
    
    testLabel = testLabel.tolist()
    success = assess(res,testLabel)
    
    print(success)
    

正确率为69.8%

发布了74 篇原创文章 · 获赞 269 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/Liangjun_Feng/article/details/78057753