Python实现朴素贝叶斯分类器

时间:2022-12-20 13:04:30

1.朴素贝叶斯概念

在所有的机器学习分类算法中,朴素贝叶斯和其他绝大多数的分类算法都不同。对于大多数的分类算法,比如决策树,KNN,逻辑回归,支持向量机等,他们都是判别方法,也就是直接学习出特征输出Y和特征X之间的关系,要么是决策函数Y=f(X),要么是条件分布P(Y|X)。但是朴素贝叶斯却是生成方法,也就是直接找出特征输出Y和特征X的联合分布P(X,Y),然后用P(Y|X)=P(X,Y)/P(X)得出。
具体算法思想过程如下图(《统计学习方法》李航,P50):
Python实现朴素贝叶斯分类器

2.朴素贝叶斯算法

根据以上公式算法,python实现朴素贝叶斯算法思想过程如下:
我们先计算 P ( Y = c k ) P(Y=c_k) 的概率,再计算每一个特征 P ( x y ) P(x|y) 的概率得到每一列x中的特征出现概率,最终形成y_p及yx_p概率矩阵,针对新数据查询这两个概率矩阵,得到每一种Y=c_k时的概率,概率最大的那个分类就是预测结果,需要注意的是: P ( c x ) = P ( c ) P ( x c ) / P ( x ) P(c|x)=P(c)P(x|c)/P(x) ,同一组数据,对所有分类来说分母 P ( x ) P(x) 均相同,所已只比较分子大小即可。
#极大似然估计 朴素贝叶斯算法,计算结果与《统计学习方法》李航,P51页一致。

#朴素贝叶斯算法 极大似然估计,计算结果与《统计学习方法》李航,P51页一致
def get_p_matrix(arr_data):
    # 获得y中分类标签的唯一值
    y_lables = np.unique(arr_data[:,-1]) 
    #y_lables = set(arr_data[:,-1])  # 同上,两种写法均可
    
    y_counts=len(arr_data) # y总数据条数
    y_p={}             # y中每一个分类的概率,字典初始化为空,y分类数是不定的,按字典存储更方便取值
    for y_lable in y_lables:
        y_p[y_lable]=len(arr_data[arr_data[:,-1]==y_lable])/y_counts  # y中每一个分类的概率(其实就是频率)
        
    yx_cnt=[] # 固定y以后的,x中每一种特征出现的次数,此数据量并不大,y分类数*x维度列数,按list存储即可
    for y_lable in y_p.keys():# 先固定y,遍历y中每一个分类
        y_lable_cnt=len(arr_data[arr_data[:,-1]==y_lable]) # 此y分类数据条数
        for x_j in range(0,arr_data.shape[1]-1): # 在固定x特征列,遍历每列x中的特征
            x_j_count=Counter(arr_data[arr_data[:,-1]==y_lable][:,x_j]) # 按列统计每种特征出现的次数,因为某一列的特征数是不固定的,所以按dict类型存储
            yx_cnt.append([y_lable,y_lable_cnt,x_j,dict(x_j_count)])
            
    yx_p=[] # 将统计次数处理为概率
    for i in range(0,len(yx_cnt)):
        #print(yx_cnt[i])
        #print(yx_cnt[i][3])
        p={} # 将每列x特征出现的次数转换为概率
        for key in yx_cnt[i][3].keys():
            p[key]=yx_cnt[i][3][key] / yx_cnt[i][1]
        yx_p.append([yx_cnt[i][0],yx_cnt[i][1],yx_cnt[i][2],p])
    return y_p,yx_p

#朴素贝叶斯算法 贝叶斯估计, λ=1, k=2, s=3; λ=1 拉普拉斯平滑,计算结果与《统计学习方法》李航,P52页一致。

#朴素贝叶斯算法   贝叶斯估计, λ=1, k=2, s=3; λ=1 拉普拉斯平滑,计算结果与《统计学习方法》李航,P52页一致。
def get_p_matrix_laplace(arr_data):
    # 获得y中分类标签的唯一值
    y_lables = np.unique(arr_data[:,-1]) 
    #y_lables = set(arr_data[:,-1])  # 同上,两种写法均可
    lambda1=1 #λ=1 拉普拉斯平滑
    k=len(y_lables) # y分类个数k,用于拉普拉斯平滑
    
    y_counts=len(arr_data) # y总数据条数
    y_p={}             # y中每一个分类的概率,字典初始化为空,y分类数是不定的,按字典存储更方便取值
    for y_lable in y_lables:
        y_p[y_lable]=(len(arr_data[arr_data[:,-1]==y_lable])+lambda1)/(y_counts+k*lambda1)  # y中每一个分类的概率(其实就是频率)
        
    yx_cnt=[] # 固定y以后的,x中每一种特征出现的次数,此数据量并不大,y分类数*x维度列数,按list存储即可
    for y_lable in y_p.keys():# 先固定y,遍历y中每一个分类
        y_lable_cnt=len(arr_data[arr_data[:,-1]==y_lable]) # 此y分类数据条数,N
        for x_j in range(0,arr_data.shape[1]-1): # 在固定x特征列,遍历每列x中的特征
            x_j_count=Counter(arr_data[arr_data[:,-1]==y_lable][:,x_j]) # 按列统计每种特征出现的次数,因为某一列的特征数是不固定的,所以按dict类型存储
            yx_cnt.append([y_lable,y_lable_cnt,x_j,dict(x_j_count)])
            
    yx_p=[] # 将统计次数处理为概率
    for i in range(0,len(yx_cnt)):
        #print(yx_cnt[i])
        #print(yx_cnt[i][3])
        p={} # 将每列x特征出现的次数转换为概率
        s=len(yx_cnt[i][3].keys())
        for key in yx_cnt[i][3].keys():
            p[key]=(yx_cnt[i][3][key]+lambda1) / (yx_cnt[i][1]+s*lambda1)
        yx_p.append([yx_cnt[i][0],yx_cnt[i][1],yx_cnt[i][2],p])
    return y_p,yx_p

预测结果图:
Python实现朴素贝叶斯分类器
所有代码如下:

# -*- coding: utf-8 -*-
"""
 @Time    : 2018/11/09 09:07
 @Author  : hanzi5
 @Email   : **@163.com
 @File    : NaiveBayes.py
 @Software: PyCharm
"""

import numpy as np
import pandas as pd
#import matplotlib.pyplot as plt
from collections import Counter

# 正态分布(高斯分布)函数,输入值x,miu均值,rou方差
def Gaussian(x,miu,rou):
    p_xc=1/(np.sqrt(2*np.pi)*rou)*np.exp(-(x-miu)**2/(2*rou**2))
    return p_xc

#朴素贝叶斯算法 极大似然估计,计算结果与《统计学习方法》李航,P51页一致
def get_p_matrix(arr_data):
    # 获得y中分类标签的唯一值
    y_lables = np.unique(arr_data[:,-1]) 
    #y_lables = set(arr_data[:,-1])  # 同上,两种写法均可
    
    y_counts=len(arr_data) # y总数据条数
    y_p={}             # y中每一个分类的概率,字典初始化为空,y分类数是不定的,按字典存储更方便取值
    for y_lable in y_lables:
        y_p[y_lable]=len(arr_data[arr_data[:,-1]==y_lable])/y_counts  # y中每一个分类的概率(其实就是频率)
        
    yx_cnt=[] # 固定y以后的,x中每一种特征出现的次数,此数据量并不大,y分类数*x维度列数,按list存储即可
    for y_lable in y_p.keys():# 先固定y,遍历y中每一个分类
        y_lable_cnt=len(arr_data[arr_data[:,-1]==y_lable]) # 此y分类数据条数
        for x_j in range(0,arr_data.shape[1]-1): # 在固定x特征列,遍历每列x中的特征
            x_j_count=Counter(arr_data[arr_data[:,-1]==y_lable][:,x_j]) # 按列统计每种特征出现的次数,因为某一列的特征数是不固定的,所以按dict类型存储
            yx_cnt.append([y_lable,y_lable_cnt,x_j,dict(x_j_count)])
            
    yx_p=[] # 将统计次数处理为概率
    for i in range(0,len(yx_cnt)):
        #print(yx_cnt[i])
        #print(yx_cnt[i][3])
        p={} # 将每列x特征出现的次数转换为概率
        for key in yx_cnt[i][3].keys():
            p[key]=yx_cnt[i][3][key] / yx_cnt[i][1]
        yx_p.append([yx_cnt[i][0],yx_cnt[i][1],yx_cnt[i][2],p])
    return y_p,yx_p

#朴素贝叶斯算法   贝叶斯估计, λ=1  K=2, S=3; λ=1 拉普拉斯平滑,计算结果与《统计学习方法》李航,P52页一致。
def get_p_matrix_laplace(arr_data):
    # 获得y中分类标签的唯一值
    y_lables = np.unique(arr_data[:,-1]) 
    #y_lables = set(arr_data[:,-1])  # 同上,两种写法均可
    lambda1=1 #λ=1 拉普拉斯平滑
    k=len(y_lables) # y分类个数k,用于拉普拉斯平滑
    
    y_counts=len(arr_data) # y总数据条数
    y_p={}             # y中每一个分类的概率,字典初始化为空,y分类数是不定的,按字典存储更方便取值
    for y_lable in y_lables:
        y_p[y_lable]=(len(arr_data[arr_data[:,-1]==y_lable])+lambda1)/(y_counts+k*lambda1)  # y中每一个分类的概率(其实就是频率)
        
    yx_cnt=[] # 固定y以后的,x中每一种特征出现的次数,此数据量并不大,y分类数*x维度列数,按list存储即可
    for y_lable in y_p.keys():# 先固定y,遍历y中每一个分类
        y_lable_cnt=len(arr_data[arr_data[:,-1]==y_lable]) # 此y分类数据条数,N
        for x_j in range(0,arr_data.shape[1]-1): # 在固定x特征列,遍历每列x中的特征
            x_j_count=Counter(arr_data[arr_data[:,-1]==y_lable][:,x_j]) # 按列统计每种特征出现的次数,因为某一列的特征数是不固定的,所以按dict类型存储
            yx_cnt.append([y_lable,y_lable_cnt,x_j,dict(x_j_count)])
            
    yx_p=[] # 将统计次数处理为概率
    for i in range(0,len(yx_cnt)):
        #print(yx_cnt[i])
        #print(yx_cnt[i][3])
        p={} # 将每列x特征出现的次数转换为概率
        s=len(yx_cnt[i][3].keys())
        for key in yx_cnt[i][3].keys():
            p[key]=(yx_cnt[i][3][key]+lambda1) / (yx_cnt[i][1]+s*lambda1)
        yx_p.append([yx_cnt[i][0],yx_cnt[i][1],yx_cnt[i][2],p])
    return y_p,yx_p


if __name__ == "__main__":
    df_data=pd.read_csv('D:/python_data/naivebayes_data.csv')
    arr_data=np.array(df_data.values) # 数据处理为numpy.array类型,其实pandas.Dataframe类型更方便计算
    # 测试数据一条,x1,x2
    features = [2,'S']
    
    #1、素贝叶斯算法,极大似然估计,调用函数,计算概率矩阵
    y_p,yx_p=get_p_matrix(arr_data)
    # 查看概率矩阵
    print('1、素贝叶斯算法,极大似然估计:')
    print('y_p:\n',y_p)
    print('yx_p:\n',yx_p)
    
    # 朴素贝叶斯分类器,数据[2,'S']手动计算过程
    # P(c|x)=P(c)P(x|c)/P(x),同一组数据,对所有分类来说分母相同,所已只比较分子大小即可
    # c=1, 0.6*0.3333*0.1111=0.022217778
    # c=-1,0.4*0.3333*0.5=0.06666
    # 比较c=1及c=-1时概率大小,数据[2,'S']数据c=-1类
    
    yx_p_arr=np.array(yx_p) # list类型不好按列取值,转换为array类型
    # 编程计算每个分类的概率值
    res1={}
    for key in y_p.keys():
        res1[key]=1*y_p[key]
        for i in range(0,len(features)):
            res1[key]=res1[key]*yx_p_arr[(yx_p_arr[:,0]==key)&(yx_p_arr[:,2]==i)][:,3][0][features[i]]
            
    print('测试数据:',features,'各分类概率:',res1,',预测结果为:',max(res1,key=res1.get))
    
    #2、朴素贝叶斯算法,贝叶斯估计,调用函数,计算概率矩阵
    y_p_ll,yx_p_ll=get_p_matrix_laplace(arr_data)
    print('\n 2、朴素贝叶斯算法,贝叶斯估计')
    print('y_p_ll:\n',y_p_ll)
    print('yx_p_ll:\n',yx_p_ll)
    
    yx_p_ll_arr=np.array(yx_p_ll) # list类型不好按列取值,转换为array类型
    # 编程计算每个分类的概率值
    res2={}
    for key in y_p_ll.keys():
        res2[key]=1*y_p_ll[key]
        for i in range(0,len(features)):
            res2[key]=res2[key]*yx_p_ll_arr[(yx_p_ll_arr[:,0]==key)&(yx_p_ll_arr[:,2]==i)][:,3][0][features[i]]
            
    print('测试数据:',features,'各分类概率:',res2,',预测结果为:',max(res2,key=res2.get))
    
    

数据来源自《统计学习方法》李航,表4.1,复制以下数据另存为naivebayes_data.csv文件即可。

x1,x2,Y
1,S,-1
1,M,-1
1,M,1
1,S,1
1,S,-1
2,S,-1
2,M,-1
2,M,1
2,L,1
2,L,1
3,L,1
3,M,1
3,M,1
3,L,1
3,L,-1

参考资料:
1、Machine-Learning-With-Python
2、《机器学习实战》Peter Harrington著
3、《机器学习》西瓜书,周志华著
4、 斯坦福大学公开课 :机器学习课程
5、机器学习视频,邹博
6、[吴恩达_UFLDL中文教程]
7、朴素贝叶斯算法–python实现
8、《统计学习方法》李航