朴素贝叶斯

朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法。

条件概率就是事件X在另外一个事件Y已经发生条件下的概率。条件概率表示为P(X|Y)。

三种朴素贝叶斯算法

不同的朴素贝叶斯算法,主要是对P(xi|y)的分布假设不同,进而采用不同的参数估计方式。我们能够发现,朴素贝叶斯算法,主要就是计算P(xi|y),一旦P(xi|y)确定,最终属于每个类别的概率,自然也就迎刃而解了。

高斯型朴素贝叶斯(GaussianNB)

适用于连续变量,其假定各个特征xi在各个类别y下是正态分布的。

伯努利型朴素贝叶斯(BernoulliNB)

这个算法假设样本每个特征只有两种可能,比如:0、1。

适用于离散变量,通常需要对数据做预处理,即把样本特征二值化转换为0和1。

多项式型朴素贝叶斯(MultinomailNB)

适用于多元离散变量。

比较

一般来说,如果样本特征的分布大部分是连续值,适用GaussianNB会比较好。如果样本特征的分布大部分是多元离散值,使用MultinomialNB比较合适。而如果样本特征是二元离散值或者很稀疏的多元离散值,应该使用BernoulliNB。

优点

1,朴素贝叶斯模型发源于古典数学理论,有稳定的分类效率

2,对小规模的数据表现很好,能处理多分类任务,适合增量式训练,尤其是数据量超出内存时,我们可以一批批的去增量训练

3,对缺失数据不太敏感,算法也比较简单,常用于文本分类。

缺点

1,理论上,朴素贝叶斯模型与其他分类方法相比具有最小的误差率。但是实际上并非总是如此,这是因为朴素贝叶斯模型给定输出类别的情况下,假设属性之间相互独立,这个假设在实际应用中往往是不成立的,在属性个数比较多或者属性之间相关性较大时,分类效果不好。而在属性相关性较小的时,朴素贝叶斯性能最为良好。对于这一点,有半朴素贝叶斯之类的算法通过考虑部分关联性适度改进。

2,需要知道先验概率,且先验概率很多时候取决于假设,假设的模型可以有很多种,因此在某些时候会由于假设的先验模型的原因导致预测效果不佳。

3,由于我们是通过先验和数据来决定后验的概率从而决定分类,所以分类决策存在一定的错误率。

4,对输入数据的表达形式很敏感。

实现

对于给出的待分类项,求解在此项出现的条件下各个类别出现的概率,哪个最大,就认为此待分类项属于哪个类别。

基于伯努利的sklearn实现

import pandas as pd
from sklearn.naive_bayes import BernoulliNB
trainfile = '../data/mnist_train.csv'
testfile = '../data/mnist_test.csv'

# 第0列是手写体数字,从第1列到第784列是28*28的像素,每个特征代表一个像素值
df_train = pd.read_csv(trainfile, header=None)
df_test = pd.read_csv(testfile, header=None)

# 伯努利算法模型要求每个特征只有2种值类型,这个案例中就是要把像素值这个特征转换为0和1来满足该算法模型要求
def transform(x):
    if x<255/2:
        return 0
    else:
        return 1

# 把像素特征二值化,所有的行都需要操作,所有的列操作第1列到最后一列,即为[:,1:],逗号前操作行,逗号后操作列
df_train.iloc[:,1:] = df_train.iloc[:,1:].applymap(transform)
df_test.iloc[:,1:] = df_test.iloc[:,1:].applymap(transform)

train_y = df_train.iloc[:,0]  # 选中所有行中的第0列,该列含义是手写体的数值0-9,数字就这10类
train_x = df_train.iloc[:,1:]  # 所有行的像素特征

model = BernoulliNB()
# 每一个手写体对应的像素数据 train_x -> 手写体数值 train_y,拟合数据
model.fit(train_x, train_y)

sample_0 = df_test.iloc[0,1:].values.reshape(1,-1)  # 取一个测试样本
model.predict(sample_0)  # 预测它
df_test.iloc[0][0]  # 查看该测试样本真实值

# 计算在全部测试样本下的准确率
model.score(df_test.iloc[:,1:].values, df_test.iloc[:,0])

基于伯努利不用sklearn的python实现

import pandas as pd
import numpy as np
trainfile = '../data/mnist_train.csv'
testfile = '../data/mnist_test.csv'
df_train = pd.read_csv(trainfile,header=None)
df_test = pd.read_csv(testfile,header=None)
def transform(x):
    if x<255/2:
        return 0
    else:
        return 1
df_train.iloc[:,1:] = df_train.iloc[:,1:].applymap(transform)
df_test.iloc[:,1:] = df_test.iloc[:,1:].applymap(transform)
train_y = df_train.iloc[:,0]
train_x = df_train.iloc[:,1:]
P_class = train_y.value_counts(sort=False)/len(train_y)
f_sum = train_x[train_y==0].apply(np.sum,axis=0)
c_num = train_y.value_counts(sort=False)
c_0 = c_num[0]
k,N = 1,2
P_fi_c0 = (f_sum +k)/(c_0+k*N)
P_fi_c0[:5]
def train(X, Y, k=1,N=2,C=10):
    #  参数说明:
    #  X:样本特征,pandas.DataFrame类型
    #  Y: 样本标签,pandas.Series类型
    #  k,N : 拉普拉斯平滑参数
    #  C: 类别数
    
    #用numpy.array计算,
    #都返回array类型
    '''
    P_class = np.array(Y.value_counts(sort=False)) #shape:(C,)
    I_fc    = np.array([X[Y==i].apply(np.sum,axis=0).tolist() for i in range(10)]) + k #shape:(10,784)
    I_c     = P_class + N*k
    E       = I_fc/I_c[:,None] 
    
    #用pandas中DataFrame,Series计算,
    #返回:P_class(Series),E(DataFrame)
    '''
    P_class = Y.value_counts(sort=False)
    I_fc    = pd.DataFrame([X[Y==i].apply(np.sum,axis=0).tolist() for i in range(C)]) + k
    I_c     = P_class + N*k
    E       = I_fc.div(I_c,axis=0).values  #结果为 np.array 类型
    return P_class,E
P_class,E = train(train_x,train_y)
pd.DataFrame(E)
sample_x = df_test.iloc[0,1:].values.reshape(1,-1) #取一个测试样本
sample_y = df_test.iloc[0,0]                       #样本标签
P_class,E = train(train_x,train_y)
bz1 = np.subtract(sample_x,1)
bz2 = np.abs(bz1)
bz3 = np.subtract(bz2,E)
bz4 = np.abs(bz3)
log_p_fi_c0 = np.log(bz4)       #取对数
log_p_c0    = np.log(P_class)
P_c0_X      = log_p_c0 + np.sum(log_p_fi_c0,axis=1)
def predict(X,P_class,E):
    bz1 = np.subtract(X,1)
    bz2 = np.abs(bz1)
    bz3 = np.subtract(bz2,E)
    bz4 = np.abs(bz3)
    log_p_fi_cj = np.log(bz4) 
    log_p_cj    = np.log(P_class.values)
    P_cj_X      = np.add(log_p_cj, np.sum(log_p_fi_cj,axis=1))
    return np.argmax(P_cj_X)
max_class = predict(sample_x,P_class,E)
pre_list = []
for i in range(df_test.shape[0]):
    sample_x = df_test.iloc[i,1:].values.reshape(1,-1)
    pre_list.append(predict(sample_x,P_class,E))
y = df_test.iloc[:,0]
acc = len(y[y==pd.Series(pre_list)])/len(y)

参考1参考2

Last updated

Was this helpful?