一、朴素贝叶斯(Naive Bayes)简介
1.原理
相信大家都学过贝叶斯公式,见下:
而我们正是用这条公式来实现朴素贝叶斯。现要实现这样一个分类任务:对于一条数据,我们希望预测它属于m个类别中的哪个类别 。 所以我们就是要求这些条件概率 , 并比较哪一个最大,那么x就属于概率最大那一类。根据贝叶斯公式,有:(式中用c代表某一类别)
该算法之所以被称为朴素贝叶斯,是因为它采用了属性条件独立性假设,也就是假设x中的所有属性x1,x2,x3...xn它们之间是独立的,不相互影响的,都独立的对分类结果产生影响,基于这样的假设,上式变为:
在这里有一个值得思考的问题。即在上式当中,我们可不可以根据x的各个属性的取值,直接统计x出现的个数,进而得到P(x|c), 而不需要将所有的属性分开考虑呢?实际上这往往是不行的。由于训练集是有限的,而且往往不大,很多样本取值在训练集中根本没有出现,但这不代表它的出现概率为零,所以直接使用频率来估计P(x|c)是不可行的。
接下来,由于对于所有类别来说,P(x)相同,根据上面的式子,我们只需要计算、比较分子即可:
令Dc表示训练集D中第c类样本组成的集合,若有充足的独立同分布样本,则可容易地估算出该类的先验概率:
对于离散属性而言,令表示Dc中在第i个属性上取值为的样本组成的集合,则:
对于连续属性可以考虑概率密度函数,假定, 即服从正态分布,那么有:
有人可能发现如果用于连乘的条件概率为零的话, 整个结果就会变为零。所以在估计概率值的时候通常要进行smoothing,常用拉普拉斯修正。具体来说,令N表示训练D中可能的类别数,Ni表示第i个属性可能的取值数,则上面的式子可以修正为:
另外,当我们进行连乘操作,往往由于概率的值小而且连乘的概率值很多,或造成数值下溢,所以通常通过取对数的方式,将连乘转化成连加,下面自己实现的版本就采用的就是连加操作。
2.优点
1. 尽管假设简单,naive Bayes分类器在很多真实的分类问题,例如文档分类、垃圾邮件过滤,效果很理想。
2.在数据较少的情况下仍然有效,它只需要少量的训练数据去估计必要的参数,可以处理多类别问题。
3.相比其它复杂的方法, naive Bayes分类器执行非常快,类条件特征分布的可分解性意味着,每一个分布都可以作为一维分布独立地估计。
3.缺点
尽管naive Bayes是一个适宜的分类器,但是它的估计量并不好,因此,来自predict_proba的概率输出结果建议只作为参考。
在现实中,属性条件独立性假设往往不成立,属性和属性之间往往有一定的关系,所以朴素贝叶斯过于理想,在某些属性独立性很弱的数据集上效果往往不够好。因此也催生了半朴素贝叶斯和贝叶斯网络算法来解决这个问题。
二、代码实现
下面是一个用于文档分类的贝叶斯分类器的实现。
import numpy as np
def load_dataset():
#返回一些自己构造的已经进行了分词的句子(这里称它们为document)
documents = [['my','dog','has','flea',\
'problems', 'help','please'],
['maybe','not','take','him',\
'to','dog','park','stupid'],
['my','dalmation','is','so','cute',\
'I','love','him'],
['stop','posting','stupid','worthless','garbage'],
['mr','licks','ate','my','steak','how',\
'to','stop','him'],
['quit','buying','worthless','dog','food','stupid']]
labels = [0,1,0,1,0,1]
return documents, labels
def create_vocabulary(dataset):
#创建词汇表
vocab_set = set([])
for document in dataset:
vocab_set = vocab_set |set(document)
return list(vocab_set)
def words2vect(vocabulary, input_list):
#这里使用的是独热编码的方式
return_vector = [0]*len(vocabulary)
for word in input_list:
if word in vocabulary:
return_vector[vocabulary.index(word)] = 1
else:
print("the word %s is not in my vocabulary"%word)
return return_vector
def trainNB0(train_matrix, train_category):
#训练分类器
num_traindocs = len(train_matrix)
num_words = len(train_matrix[0])
# num_abusive代表带有侮辱性的文档的数目
num_abusive = np.sum(train_category)
# 这里使用了拉普拉斯平滑
prob_abusive = (num_abusive+1)/(float(num_traindocs)+2)
p0_num = np.ones(num_words)
p1_num = np.ones(num_words)
for i in range(num_traindocs):
if train_category[i] == 1:
p1_num += train_matrix[i]
else:
p0_num += train_matrix[i]
p1_vect = p1_num/(num_abusive+2)
p0_vect = p0_num/(num_traindocs-num_abusive+2)
return p0_vect , p1_vect, prob_abusive
def classifyNB(test_vector, p0_vect, p1_vect, p_class1):
#进行分类
p1 = np.log(p_class1)
p0 = np.log(1.0-p_class1)
for i in range(len(test_vector)):
if test_vector[i] == 1:
p1 += np.log(p1_vect[i])
p0 += np.log(p0_vect[i])
else:
p1 += np.log(1-p1_vect[i])
p0 += np.log(1-p0_vect[i])
if p1 > p0:
return 1
else:
return 0
def testingNB(data_test):
#进行测试
documents, labels = load_dataset()
vocabulary = create_vocabulary(documents)
train_matrix = []
for doc in documents:
train_matrix.append(words2vect(vocabulary,doc))
p0_vect, p1_vect, prob_abusive = trainNB0(np.array(train_matrix),\
np.array(labels))
for entry in data_test:
entry_vect = np.array(words2vect(vocabulary, entry))
entry_class = classifyNB(entry_vect, p0_vect, p1_vect, prob_abusive)
print("{} classify as {}".format(entry, entry_class))
data_test = [['please,my'],['garbage, trash']]
testingNB(data_test)
======OUTPUT========
[0,1]
三、使用sklearn中的贝叶斯分类器
当我们需要使用贝叶斯分类器时,我们可以直接调用sklearn中的分类器,而不需要自己实现。在sklearn中总共有三种贝叶斯分类器, 分别是BernoulliNB,MultinomialNB和GaussianNB。
1. 三种贝叶斯分类器的区别:
BernoulliNB假设数据的各个属性都是服从伯努利分布的,也就是二项分布,上面的数据就是这样的分布。
MultinomialNB假设数据的各个属性是离散的,就是取有限多个的值。
GaussianNB假设数据各个属性是连续的。
2.三种贝叶斯分类器的应用场景:
如果样本特征是二元离散值,应该使用BernoulliNB。
如果如果样本特征的分大部分是多元离散值,使用MultinomialNB比较合适。
一般来说,如果样本特征的分布大部分是连续值,使用GaussianNB会比较好。
贝叶斯分类器一般会对不满足假设条件的数据进行预处理,使其满足假设条件。但处理之后效果不一定很好,多元离散值类型的iris数据用BernoulliNB进行分类,准确率极差。
3.代码
from sklearn.naive_bayes import BernoulliNB, MultinomialNB, GaussianNB
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
iris = datasets.load_iris()
trainX, testX, trainY, testY = train_test_split(iris.data, iris.target,test_size=0.2)
model1 = BernoulliNB().fit(trainX, trainY)
y_pred = model1.predict(testX)
print(classification_report(testY, y_pred))
model2 = MultinomialNB().fit(trainX, trainY)
y_pred = model2.predict(testX)
print(classification_report(testY, y_pred))
model3 = GaussianNB().fit(trainX, trainY)
y_pred = model3.predict(testX)
print(classification_report(testY, y_pred))
下面是三个模型的分类报告:
四、参考资料
【1】《机器学习实战》Peter+Harrington
【2】《机器学习》周志华
【3】https://blog.csdn.net/Trance95/article/details/104877399/