使用 python 基于朴素贝叶斯进行文本分类学习笔记之二:将原书程序修改并转换为类

朴素贝叶斯方法在文本分类中应用广泛,什么是朴素贝叶斯方法呢,wiki 上的定义如下:

在机器学习中,朴素贝叶斯分类器是一系列以假设特征之间强(朴素)独立下运用贝叶斯定理为基础的简单概率分类器。

所有朴素贝叶斯分类器都假定样本每个特征与其他特征都不相关,这是朴素(Naive)说法的由来,也是该算法的成立之本。

数学学得不太好,对于公式的理解有限,有些概念只能囫囵吞枣了,我更加关心的是应用场景和应用方法。

其实简单的想想也会知道,在文本分类中,我们假设词和词之间是不相关的,其实不太正确。

尽管是带着这些朴素思想和过于简单化的假设,但朴素贝叶斯分类器在很多复杂的现实情形中仍能够取得相当好的效果。

想看朴素贝叶斯整套公式推导过程的,可以移步这里,或者机器学习实战这本书上也有,此处就不再赘述了。

网上中文的介绍朴素贝叶斯用于文本分类的文章还是有一些,有兴趣的可以google 一下。

我们的目标是通过书中介绍的程序,来完成文本的倾向性分类,目前先按照两类进行分类,我简单的称之为正面和和负面的。

大致过程就是先进行正面和负面材料训练,计算得到 p0,p1,然后对材料进行测试,获得分类结果。

书中的源代码可以在这里下载,没有书也可以下载。代码属于第四章,不过书中举例主要还是在命令行环境下进行的,不是一个完整的可运行代码。

我做了如下修改:

  • 修改为 class 方式,方便程序调用
  • 修改为支持 python 3
  • 函数名称、变量名称和写法修改为支持 pep-8 标准

逐一解释一下代码:

下面是 class 类的一些变量,包括默认的训练文本和设定的倾向性。

# 训练 list, 默认内容, 原书中的内容
    train_doc_list = [['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']]
    # 倾向性向量, 0:正面 1:负面
    train_doc_sent_vec = [0, 1, 0, 1, 0, 1]

    # 单词列表集合
    word_list = []

    # 正面和负面概率, 先验概率
    p0_v = 0
    p1_v = 0
    p_ab = 0

 

下面是创建单词的集合,也就是将训练文本中所有的词合并到一起,不管出现几次都只保留一次。

    @staticmethod
    def create_word_list(data_list):
        # create empty set
        word_set = set([])
        for document in data_list:
            # union of the two sets
            word_set = word_set | set(document)
        return list(word_set)

 

下面这个函数是将输入的单词列表(需要训练或者测试的)根据上面的单词集合生成向量。

 @staticmethod
    def words_to_vec(word_list, new_word_list):
        vec = [0] * len(word_list)
        for word in new_word_list:
            if word in word_list:
                vec[word_list.index(word)] += 1
            else:
                pass
                # print("the word: %s is not in my Vocabulary!" % word)
        return vec

 

下面这个是朴素贝叶斯的算法,根据训练矩阵数据和预先设定的倾向性,来得到 p0,p1 概率。

@staticmethod
    def train_nb0(train_matrix, train_category):

        num_train_docs = len(train_matrix)
        num_words = len(train_matrix[0])
        p_ab = sum(train_category) / float(num_train_docs)

        # 创建给定长度的填满1的数组
        p0_num = ones(num_words)
        p1_num = ones(num_words)

        p0_d = 2.0
        p1_d = 2.0
        for i in range(num_train_docs):
            if train_category[i] == 1:
                p1_num += train_matrix[i]
                p1_d += sum(train_matrix[i])
            else:
                p0_num += train_matrix[i]
                p0_d += sum(train_matrix[i])

        p1_v = log(p1_num / p1_d)
        p0_v = log(p0_num / p0_d)
        return p0_v, p1_v, p_ab

 

然后是分类函数,根据上面计算得到的 p0,p1 概率,以及先验概率(如果正面和负面训练文本数量一样,就是 0.5),将输入的文本向量得到分类结果

    @staticmethod
    def classify_nb(vec, p0_vec, p1_vec, p_class1):
        # element-wise mult
        p0 = sum(vec * p0_vec) + log(1.0 - p_class1)
        p1 = sum(vec * p1_vec) + log(p_class1)
        print('p0:', p0, 'p1:', p1)
        if p1 > p0:
            return 1
        else:
            return 0

 

将上面的函数组合起来,得到下面这个完整的训练函数,上面大部分都是静态函数,外部不需要访问。

def train(self):
        # 生成单词列表集合
        self.word_list = self.create_word_list(self.train_doc_list)

        # 训练矩阵初始化
        train_matrix = []

        # 根据训练文档进行循环
        for post_in_doc in self.train_doc_list:
            # 构建训练矩阵, 将单词列表转化为向量
            train_matrix.append(self.words_to_vec(self.word_list, post_in_doc))

        # 根据训练矩阵和情感分析向量进行训练,得到
        self.p0_v, self.p1_v, self.p_ab = self.train_nb0(array(train_matrix), array(self.train_doc_sent_vec))

 

训练好了,就是测试函数,这是用来外部调用真正传入需要分类的文档或者校验正确率时候使用的。

 def testing_nb(self, test_word_list):

        # 对输入的内容转化为向量
        this_post_vec = array(self.words_to_vec(self.word_list, test_word_list))

        # 返回分类的值
        return self.classify_nb(this_post_vec, self.p0_v, self.p1_v, self.p_ab)

 

这个 navie bayes 代码会合并到我之前写的 fish_base 包中,代码也会放在 github 上,目前还在整理中。

刚才的 class 代码加上一个头,别忘记

from numpy import *


class ClassNaiveBayes:

 

其中的矩阵相关运算是通过 numpy 进行的,这个 python 下最好的数学计算库,性能不用怀疑。

调用上面 bayes 代码的例子代码,我将书中分散的测试代码合并了,并且通过类的方式调用:

import bayes

nb = bayes.ClassNaiveBayes()

nb.train()

test_list = ['love', 'my', 'dalmation']
print(test_list, 'classified as: ', nb.testing_nb(test_list))

test_list = ['stupid', 'garbage']
print(test_list, 'classified as: ', nb.testing_nb(test_list))

 

应该会得到书中一样的结果:

p0: -7.69484807238 p1: -9.82671449373
['love', 'my', 'dalmation'] classified as:  0
p0: -7.2093402566 p1: -4.70275051433
['stupid', 'garbage'] classified as:  1

 

接下来我尝试修改为针对中文内容进行分类,测试样本考虑用对于一款手机的评价,通过训练来区分是正面和还是负面的评论。

使用 python 基于朴素贝叶斯进行文本分类学习笔记之一:开始
使用 python 基于朴素贝叶斯进行文本分类学习笔记之二:将原书程序修改并转换为类
使用 python 基于朴素贝叶斯进行文本分类学习笔记之三:中文内容的倾向性判断初步
使用 python 基于朴素贝叶斯进行文本分类学习笔记之四:增加测试文本和计算正确率
使用 python 基于朴素贝叶斯进行文本分类学习笔记之五:增加停用词

Leave a Reply