朴素贝叶斯
朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法。
条件概率就是事件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)
Last updated
Was this helpful?