机器学习:自己动手实现朴素贝叶斯和使用skearn中的朴素贝叶斯

一、朴素贝叶斯(Naive Bayes)简介

1.原理

    相信大家都学过贝叶斯公式,见下:

    P(X|Y)=\frac{P(Y|X)P(X)}{P(Y)}

    而我们正是用这条公式来实现朴素贝叶斯。现要实现这样一个分类任务:对于一条数据x =(x_1,x_2,...,x_n),我们希望预测它属于m个类别中的哪个类别 \gamma = (c_1, c_2, ...c_m)。 所以我们就是要求这些条件概率 P(c_1|x), P(c_2|x),..., P(c_m|x), 并比较哪一个最大,那么x就属于概率最大那一类。根据贝叶斯公式,有:(式中用c代表某一类别)

    P(c|x) = \frac{P(c)P(x|c)}{P(x)}

    该算法之所以被称为朴素贝叶斯,是因为它采用了属性条件独立性假设,也就是假设x中的所有属性x1,x2,x3...xn它们之间是独立的,不相互影响的,都独立的对分类结果产生影响,基于这样的假设,上式变为:

    P(c|x)=\frac{P(c)P(x|c)}{P(x)} =\frac{P(c)P(x_1,x_2,...,x_n|c)}{P(x)} = \frac{P(c)}{P(x)}\prod _{i=1}^np(x_i|c)

    在这里有一个值得思考的问题。即在上式当中,我们可不可以根据x的各个属性的取值,直接统计x出现的个数,进而得到P(x|c), 而不需要将所有的属性分开考虑呢?实际上这往往是不行的。由于训练集是有限的,而且往往不大,很多样本取值在训练集中根本没有出现,但这不代表它的出现概率为零,所以直接使用频率来估计P(x|c)是不可行的。

    接下来,由于对于所有类别来说,P(x)相同,根据上面的式子,我们只需要计算、比较分子即可:

    h_{nb}(x)=arg \max_{c\in\gamma} P(c)\prod_{i=1}^nP(x_i|c)

    令Dc表示训练集D中第c类样本组成的集合,若有充足的独立同分布样本,则可容易地估算出该类的先验概率:

    P(c)=\frac{\vert D_c\vert }{\vert D\vert}

    对于离散属性而言,令D_{c,x_i}表示Dc中在第i个属性上取值为x_i的样本组成的集合,则:

    P(x_i|c)= \frac{\vert D_{c,x_i}\vert }{\vert D_c \vert }

    对于连续属性可以考虑概率密度函数,假定p(x_i|c)\sim N(\mu _{c,i}, \sigma _{c,i}^2),  即服从正态分布,那么有:

    p(x_i|c) = \frac{1}{\sqrt {2\pi }\sigma _{c,i}} exp(-\frac{(x_i-\mu _{c,i})^2}{2\sigma _{c,i}^2})

    有人可能发现如果用于连乘的条件概率P(x_i|c)为零的话, 整个结果就会变为零。所以在估计概率值的时候通常要进行smoothing,常用拉普拉斯修正。具体来说,令N表示训练D中可能的类别数,Ni表示第i个属性可能的取值数,则上面的式子可以修正为:

    \hat P(c) = \frac{\vert D_c \vert +1 }{\vert D\vert +N}

    \hat P(x_i|c) = \frac{\vert D_{c,x_i}\vert +1}{\vert D_c \vert +N_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/

【4】https://blog.csdn.net/wong2016/article/details/80719885

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章