机器学习---用python实现朴素贝叶斯算法(Machine Learning Naive Bayes Algorithm Application)

《机器学习---朴素贝叶斯分类器(Machine Learning Naive Bayes Classifier)》一文中,咱们介绍了朴素贝叶斯分类器的原理。如今,让咱们来实践一下。html

 

在这里,咱们使用一份皮马印第安女性的医学数据,用来预测其是否会得糖尿病。文件一共有768个样本,咱们先剔除缺失值,而后选出20%的样本做为测试样本。git

 

文件下载地址:https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.data.csvgithub

 

特征分别是:算法

怀孕次数 
口服葡萄糖耐量试验中血浆葡萄糖浓度
舒张压(mm Hg) 
三头肌组织褶厚度(mm) 
2小时血清胰岛素(μU/ ml) 
体重指数(kg/(身高(m))^ 2) 
糖尿病系统功能 
年龄(岁)数组

(注:特征值为0表示缺失值)app

 

标签是:dom

是否患有糖尿病,0表明没有糖尿病,1表明患有糖尿病机器学习

 


 

在开始以前,先回顾一下朴素贝叶斯算法:post

 

假设训练集特征数为i,记做xi,目标为k个类别,记为Ck,样本数为n,新样本特征记做xnew_sample学习

1,计算出先验几率P(Ck),即每一个类别在训练集中的几率;

2,分别计算出训练集中每一个特征在每一个类别下的条件几率P(xi|Ck),具体分为如下三种状况;

      a)若是特征数据是离散值,那么咱们假设其符合多项式分布,代入公式{\displaystyle p(\mathbf {x} \mid C_{k})={\frac {(\sum _{i}x_{i})!}{\prod _{i}x_{i}!}}\prod _{i}{p_{ki}}^{x_{i}}}

      b)若是特征数据是布尔类型的离散值,那么咱们假设其符合伯努利分布,代入公式{\displaystyle p(\mathbf {x} \mid C_{k})=\prod _{i=1}^{n}p_{ki}^{x_{i}}(1-p_{ki})^{(1-x_{i})}}

      c)若是特征数据是连续值,那么咱们假设其符合高斯分布。只须要求出这个特征在每一个类别下的平均值μk和方差σk2,而后代入公式{\displaystyle p(x=v\mid C_{k})={\frac {1}{\sqrt {2\pi \sigma _{k}^{2}}}}\,e^{-{\frac {(v-\mu _{k})^{2}}{2\sigma _{k}^{2}}}}}

3,对于须要预测类别的新样本,分别计算每一个类别下的极大后验几率:(咱们在实际计算中为防止数据下溢,将连乘运算取对数变成相加运算);

4,对上述极大后验几率进行比较,最大的那个对应的类别即为结果;

 


 

首先导入pandas和numpy库,读取csv文件,而后去除缺失值:

import pandas as pd import numpy as np data=pd.read_csv(r"C:\Users\ccav\pima-indians-diabetes.data.csv",header=None,\ names=["怀孕次数","口服葡萄糖耐量试验中血浆葡萄糖浓度",\ "舒张压(mm Hg)","三头肌组织褶厚度(mm)",\ "2小时血清胰岛素(μU/ ml)","体重指数(kg/(身高(m))^ 2)",\ "糖尿病系统功能","年龄(岁)","是否患有糖尿病"]) data.iloc[:,0:8]=data.iloc[:,0:8].applymap(lambda x:np.NaN if x==0 else x) #把属性值为0的地方转换成NaN
 data=data.dropna(how="any",axis=0) #去除有缺失值的行

 

通过处理还只剩336个有效样本:

print(len(data)) 336

 

经过查看,咱们发如今这336个有效样本中,225个是没有糖尿病人的样本,111个是患有糖尿病人的样本,显然原数据里两个类别的样本是不均衡的。这个问题能够经过不一样的采样方法解决,可是鉴于简便起见,咱们随机选取大约20%的样本用于测试:

#随机选取80%的样本做为训练样本
data_train=data.sample(frac=0.8,random_state=4,axis=0) #剩下的做为测试样本
test_idx=[i for i in data.index.values if i not in data_train.index.values] data_test=data.loc[test_idx,:]

 

因为咱们一共有8个特征,2个类别,所以,咱们一共须要计算16个条件几率以及2个先验几率,而后根据这些数据计算出2个极大后验几率。该如何计算这些数据,又该如何合并,若是不事先想清楚怎么作,很容易被搞晕。此外,考虑到数据是连续值,咱们假设其符合正态分布,所以在训练时须要计算出每一个特征的平均值和方差。

 

让咱们先来理清一下思路:

1,按类别分隔数据

2,获取类别总数和类别名称

3,训练数据:计算每一个类别的先验几率,计算每一个类别每一个特征的平均值和方差

4,预测:计算每一个类别每一个特征的条件几率,计算每一个类别的极大后验几率并合并,得出最大可能的类别名称

 

按照这个步骤,咱们发现如下几项都是须要复用的:按类别分隔数据,计算每一个类别的先验几率,计算每一个类别每一个特征的平均值和方差,计算每一个类别每一个特征的条件几率。所以,咱们把这几项分别作成function。

 

按类别分隔数据:

def SepByClass(X, y): ###按类别分隔数据###
    ###输入未分类的特征和目标,输出分类完成的数据(字典形式)###
    num_of_samples=len(y) #总样本数
 y=y.reshape(X.shape[0],1) data=np.hstack((X,y)) #把特征和目标合并成完整数据
 data_byclass={} #初始化分类数据,为一个空字典
    #提取各种别数据,字典的键为类别名,值为对应的分类数据
    for i in range(len(data[:,-1])): if i in data[:,-1]: data_byclass[i]=data[data[:,-1]==i] class_name=list(data_byclass.keys()) #类别名
        num_of_class=len(data_byclass.keys()) #类别总数
        
    return data_byclass

 

计算每一个类别的先验几率:

def CalPriorProb(y_byclass): ###计算y的先验几率(使用拉普拉斯平滑)###
    ###输入当前类别下的目标,输出该目标的先验几率###
    #计算公式:(当前类别下的样本数+1)/(总样本数+类别总数)
    return (len(y_byclass)+1)/(num_of_samples+num_of_class)

 

计算每一个类别每一个特征的平均值和方差:

def CalXMean(X_byclass): ###计算各种别特征各维度的平均值###
    ###输入当前类别下的特征,输出该特征各个维度的平均值###
    X_mean=[] for i in range(X_byclass.shape[1]): X_mean.append(np.mean(X_byclass[:,i])) return X_mean def CalXVar(X_byclass): ###计算各种别特征各维度的方差###
    ###输入当前类别下的特征,输出该特征各个维度的方差###
    X_var=[] for i in range(X_byclass.shape[1]): X_var.append(np.var(X_byclass[:,i])) return X_var

 

计算每一个类别每一个特征的条件几率:

def CalGaussianProb(X_new, mean, var): ###计算训练集特征(符合正态分布)在各种别下的条件几率###
    ###输入新样本的特征,训练集特征的平均值和方差,输出新样本的特征在相应训练集中的分布几率###
    #计算公式:(np.exp(-(X_new-mean)**2/(2*var)))*(1/np.sqrt(2*np.pi*var))
    gaussian_prob=[] for a,b,c in zip(X_new, mean, var): formula1=np.exp(-(a-b)**2/(2*c)) formula2=1/np.sqrt(2*np.pi*c) gaussian_prob.append(formula2*formula1) return gaussian_prob

 

接下来,咱们开始训练数据。首先按类别分隔数据,而后遍历每个类别,分别计算每一个类别的先验几率和每一个类别每一个特征的平均值和方差,并储存在列表中。

def fit(X, y): ###训练数据###
    ###输入训练集特征和目标,输出目标的先验几率,特征的平均值和方差###
    #将输入的X,y转换为numpy数组
    X, y = np.asarray(X, np.float32), np.asarray(y, np.float32) data_byclass=Gaussian_NB.SepByClass(X,y) #将数据分类
        #计算各种别数据的目标先验几率,特征平均值和方差
    for data in data_byclass.values(): X_byclass=data[:,:-1] y_byclass=data[:,-1] prior_prob.append(Gaussian_NB.CalPriorProb(y_byclass)) X_mean.append(Gaussian_NB.CalXMean(X_byclass)) X_var.append(Gaussian_NB.CalXVar(X_byclass)) return prior_prob, X_mean, X_var

 

最后,输入一个新样本的特征,预测其所属的类别。首先,遍历以前在训练数据时计算出的每一个类别的先验几率和每一个类别每一个特征的平均值和方差,再以此计算条件几率和极大后验几率。选出最大的极大后验几率所对应的索引,最后提取其对应的类别名称。

def predict(X_new): ###预测数据###
    ###输入新样本的特征,输出新样本最有可能的目标###
    #将输入的x_new转换为numpy数组
    X_new=np.asarray(X_new, np.float32) posteriori_prob=[] #初始化极大后验几率
        
    for i,j,o in zip(prior_prob, X_mean, X_var): gaussian=Gaussian_NB.CalGaussianProb(X_new,j,o) posteriori_prob.append(np.log(i)+sum(np.log(gaussian))) idx=np.argmax(posteriori_prob) return class_name[idx]

 


 

整理一下以上代码,咱们把这个高斯朴素贝叶斯分类器作成一个类,完整代码以下:

import pandas as pd import numpy as np data=pd.read_csv(r"C:\Users\ccav\pima-indians-diabetes.data.csv",header=None,\ names=["怀孕次数","口服葡萄糖耐量试验中血浆葡萄糖浓度",\ "舒张压(mm Hg)","三头肌组织褶厚度(mm)",\ "2小时血清胰岛素(μU/ ml)","体重指数(kg/(身高(m))^ 2)",\ "糖尿病系统功能","年龄(岁)","是否患有糖尿病"]) data.iloc[:,0:8]=data.iloc[:,0:8].applymap(lambda x:np.NaN if x==0 else x) #把属性值为0的地方转换成NaN
 data=data.dropna(how="any",axis=0) #去除有缺失值的行

#随机选取80%的样本做为训练样本
data_train=data.sample(frac=0.8,random_state=4,axis=0) #剩下的做为测试样本
test_idx=[i for i in data.index.values if i not in data_train.index.values] data_test=data.loc[test_idx,:] #提取训练集和测试集的特征和目标
X_train=data_train.iloc[:,:-1] y_train=data_train.iloc[:,-1] X_test=data_test.iloc[:,:-1] y_test=data_test.iloc[:,-1] class Gaussian_NB: def __init__(self): self.num_of_samples = None self.num_of_class = None self.class_name = [] self.prior_prob = [] self.X_mean = [] self.X_var = [] def SepByClass(self, X, y): ###按类别分隔数据###
        ###输入未分类的特征和目标,输出分类完成的数据(字典形式)###
        self.num_of_samples=len(y) #总样本数
 y=y.reshape(X.shape[0],1) data=np.hstack((X,y)) #把特征和目标合并成完整数据
 data_byclass={} #初始化分类数据,为一个空字典
        #提取各种别数据,字典的键为类别名,值为对应的分类数据
        for i in range(len(data[:,-1])): if i in data[:,-1]: data_byclass[i]=data[data[:,-1]==i] self.class_name=list(data_byclass.keys()) #类别名
        self.num_of_class=len(data_byclass.keys()) #类别总数
        
        return data_byclass def CalPriorProb(self, y_byclass): ###计算y的先验几率(使用拉普拉斯平滑)###
        ###输入当前类别下的目标,输出该目标的先验几率###
        #计算公式:(当前类别下的样本数+1)/(总样本数+类别总数)
        return (len(y_byclass)+1)/(self.num_of_samples+self.num_of_class) def CalXMean(self, X_byclass): ###计算各种别特征各维度的平均值###
        ###输入当前类别下的特征,输出该特征各个维度的平均值###
        X_mean=[] for i in range(X_byclass.shape[1]): X_mean.append(np.mean(X_byclass[:,i])) return X_mean def CalXVar(self, X_byclass): ###计算各种别特征各维度的方差###
        ###输入当前类别下的特征,输出该特征各个维度的方差###
        X_var=[] for i in range(X_byclass.shape[1]): X_var.append(np.var(X_byclass[:,i])) return X_var def CalGaussianProb(self, X_new, mean, var): ###计算训练集特征(符合正态分布)在各种别下的条件几率###
        ###输入新样本的特征,训练集特征的平均值和方差,输出新样本的特征在相应训练集中的分布几率###
        #计算公式:(np.exp(-(X_new-mean)**2/(2*var)))*(1/np.sqrt(2*np.pi*var))
        gaussian_prob=[] for a,b,c in zip(X_new, mean, var): formula1=np.exp(-(a-b)**2/(2*c)) formula2=1/np.sqrt(2*np.pi*c) gaussian_prob.append(formula2*formula1) return gaussian_prob def fit(self, X, y): ###训练数据###
        ###输入训练集特征和目标,输出目标的先验几率,特征的平均值和方差###
        #将输入的X,y转换为numpy数组
        X, y = np.asarray(X, np.float32), np.asarray(y, np.float32) data_byclass=Gaussian_NB.SepByClass(X,y) #将数据分类
        #计算各种别数据的目标先验几率,特征平均值和方差
        for data in data_byclass.values(): X_byclass=data[:,:-1] y_byclass=data[:,-1] self.prior_prob.append(Gaussian_NB.CalPriorProb(y_byclass)) self.X_mean.append(Gaussian_NB.CalXMean(X_byclass)) self.X_var.append(Gaussian_NB.CalXVar(X_byclass)) return self.prior_prob, self.X_mean, self.X_var def predict(self,X_new): ###预测数据###
        ###输入新样本的特征,输出新样本最有可能的目标###
        #将输入的x_new转换为numpy数组
        X_new=np.asarray(X_new, np.float32) posteriori_prob=[] #初始化极大后验几率
        
        for i,j,o in zip(self.prior_prob, self.X_mean, self.X_var): gaussian=Gaussian_NB.CalGaussianProb(X_new,j,o) posteriori_prob.append(np.log(i)+sum(np.log(gaussian))) idx=np.argmax(posteriori_prob) return self.class_name[idx] if __name__=="__main__": Gaussian_NB=Gaussian_NB() #实例化Gaussian_NB
    Gaussian_NB.fit(X_train,y_train) #使用Gaussian_NB模型训练数据
    acc=0 TP=0 FP=0 FN=0 for i in range(len(X_test)): predict=Gaussian_NB.predict(X_test.iloc[i,:]) target=np.array(y_test)[i] if predict==1 and target==1: TP+=1
        if predict==0 and target==1: FP+=1
        if predict==target: acc+=1
        if predict==1 and target==0: FN+=1
    print("准确率:",acc/len(X_test)) print("查准率:",TP/(TP+FP)) print("查全率:",TP/(TP+FN)) print("F1:",2*TP/(2*TP+FP+FN))

 

结果以下:

准确率: 0.7611940298507462 查准率: 0.7391304347826086 查全率: 0.6296296296296297 F1: 0.68

因为样本不均衡,所以光看准确率是不够的,在这里一并计算了查准率和查全率以及F1值。能够看出这个分类器的效果不是很好,一方面是由于样本数量较少,另外一方面朴素贝叶斯的预测能力确实比不上复杂模型,只能提供一个粗略的判断。

相关文章
相关标签/搜索