机器学习实战---朴素贝叶斯算法实现+使用K折交叉验证(代码详解+创新)

《机器学习实战朴素贝叶斯算法实现+使用K折交叉验证》

未经允许,不得擅自转载!

提供数据集如下(永久有效,需要的自行下载):
链接:https://pan.baidu.com/s/1Sv0ZL3MWqmKDCJEPARibJQ
提取码:v866

另外,完整版学习笔记业已上传,需要的自提!

笔记截图展示(内容详细,多方面解读!):
1.

2.
在这里插入图片描述
3.

4.

朴素贝叶斯 工作原理

提取所有文档中的词条并进行去重
获取文档的所有类别
计算每个类别中的文档数目
对每篇训练文档: 
    对每个类别: 
        如果词条出现在文档中-->增加该词条的计数值(for循环或者矩阵相加)
        增加所有词条的计数值(此类别下词条总数)
对每个类别: 
    对每个词条: 
        将该词条的数目除以总词条数目得到的条件概率(P(词条|类别))
返回该文档属于每个类别的条件概率(P(类别|文档的所有词条)

朴素贝叶斯 算法特点

优点: 在数据较少的情况下仍然有效,可以处理多类别问题。
缺点: 对于输入数据的准备方式较为敏感。
适用数据类型: 标称型数据。

朴素贝叶斯 项目案例

屏蔽社区留言板的侮辱性言论

项目概述
构建一个快速过滤器来屏蔽在线社区留言板上的侮辱性言论。如果某条留言使用了负面或者侮辱性的语言,那么就将该留言标识为内容不当。对此问题建立两个类别:侮辱类和非侮辱类,使用 1 和 0 分别表示。

开发流程

收集数据: 可以使用任何方法
准备数据: 从文本中构建词向量
分析数据: 检查词条确保解析的正确性
训练算法: 从词向量计算概率
测试算法:根据现实情况修改分类器
使用算法: 对社区留言板言论进行分类

要从文本中获取特征,需要先拆分文本。这里的特征是来自文本的词条(token),一个词条是字符的任意组合。可以把词条想象成单词。

以在线社区的留言板为例,为了不影响社区的发展,我们要屏蔽侮辱性的言论,所以要构建一个快速过滤器,如果某条留言使用了负面或者侮辱性的语言,那么就将该留言标识为内容不当。

下面是实战代码。

准备数据: 从文本中构建词向量

我们将把文本看成单词向量或者词条向量,也就是说将句子转换为向量。

考虑出现在所有文档中的所有单词,再决定将哪些词纳入词汇表或者说所要的词汇集合,然后必须要将每一篇文档转换为词汇表上的向量。

def load_dataset():
    dataset = [['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']]
    class_vector = [0, 1, 0, 1, 0, 1]  # 1 is abusive, 0 not
    return dataset,class_vector

展示数据集和数据标签为:

数据集为:
 [['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, 0, 1, 0, 1]

接下来需要创建一个包含在所有文档中出现的不重复词的列表,为此使用了Python的set 数据类型。将词条列表输给set 构造函数,set 就会返回一个不重复列表。首先,创建一个空集合,然后将每篇文档返回的新词集合添加到该集合中

def create_vocab_list(dataset):
    """创建一个包含所有文档且不出现重复词的列表"""
    vocab_set = set([])  #create empty set
    for document in dataset:
        vocab_set = vocab_set | set(document) #set()去掉列表中的重复词
    return list(vocab_set)

我们的结果为:

['cute', 'park', 'quit', 'help', 'stop', 'take', 'dalmation', 'buying', 'not', 'has', 'my', 'I', 'mr', 'how', 'ate', 'please', 'love', 'to', 'stupid', 'so', 'him', 'worthless', 'is', 'posting', 'steak', 'flea', 'problems', 'garbage', 'maybe', 'licks', 'dog', 'food']

获得词汇表后,我们需要使用新的函数,输入参数为词汇表及某个文档,输出的是文档向量,向量的每一元素为1 或者0,分别表示词汇表的单词在输入文档中是否出现。

函数首先创建一个和词汇表等长的向量,并将其元素都设置为0。接着,遍历文档中的所有单词,如果出现了文档中的单词,将其输出的文档向量中的对应值设为1。

#词集模型
"""
输入为词汇表和文档,检查文档中的单词是否在词汇表中
采用词集模型:即对每条文档只记录某个词汇是否存在,而不记录出现的次数
创建一个与词汇表长度一致的0向量,在当前样本中出现的词汇标记为1
将一篇文档转换为词向量
"""
def set_of_words_vector(vocab_list, input_set):
    return_vector = [0]*len(vocab_list)
    for word in input_set:
        if word in vocab_list:
            return_vector[vocab_list.index(word)] = 1
        else:
            print("the word: %s is not in my Vocabulary!" % word)
    return return_vector

我们看一下函数的运行效果:

[0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0]

该函数使用词汇表或者想要检查的所有单词作为输入,然后为其中每一个单词构建一个特征。一旦给定一篇文档,该文档就会被转换为词向量。

训练算法: 从词向量计算概率




我们实现朴素贝叶斯分类器训练函数:

"""
朴素贝叶斯分类器训练函数
"""
def train_native_bayes(train_matrix,train_category):
    num_train_docs=len(train_matrix)
    num_words=len(train_matrix[0])
    p=sum(train_category)/float(num_train_docs)
    p_0_num=zeros(num_words)
    p_1_num=zeros(num_words)
    p_0_denom=0.0
    p_1_denom=0.0
    for i in range(num_train_docs):
        if train_category[i]==1:
            p_1_num+=train_matrix[i]
            p_1_denom+=sum(train_matrix[i])
        else:
            p_0_num+=train_matrix[i]
            p_0_denom+=sum(train_matrix[i])
    p_1_vector=(p_1_num/p_1_denom)
    p_0_vector=(p_0_num/p_0_denom)
    return p_0_vector,p_1_vector,p

代码函数中的输入参数为文档矩阵train matrix,以及由每篇文档类别标签所构成的向量train category。首先,计算文档属于侮辱性文档(class=1)的概率,即P(1)。

因为这是一个二分类问题,所以可以通过1-P(1)来得到P(0)。

接下来计算P(ωi|c1),P(ωi|c2),需要初始化程序中的分子变量和分母变量。在for循环中,要遍历训练集train matrix 中的所有文档。一旦某个词语(侮辱性或正常词语)在某一文档中出现,则该词对应的个数(P1num) 或者另一类加加1,而且在所有文档中,该文档的总词数也相应加1。对于两个类别都要进行同样的计算处理。最后,对每个元素除以该类别中的总次数即可。

接下来试验一下:

train_mat=[]
for i in dataset:
    train_mat.append(set_of_words_vector(my_vacab_set,i))
p_0_vector,p_1_vector,p=train_native_bayes(train_mat,class_vector)
print(p)

接下来看这些变量的内部值:

0.5

我们输出一下P(0) 的概率如下:

[0.04166667 0.04166667 0. 0.04166667 0.04166667 0.
0. 0.04166667 0.04166667 0. 0. 0.04166667
0.04166667 0.04166667 0. 0.04166667 0. 0.04166667
0.04166667 0. 0.04166667 0.04166667 0. 0.04166667
0. 0.04166667 0.04166667 0. 0.125 0.04166667
0.04166667 0.08333333]

首先,我们发现文档属于侮辱类的概率p 为0.5,该值是正确的。接下来,看一看在给定文档类别条件下词汇表中单词的出现概率,看看是否正确。词汇表中的第一个词是cute,其在类别0 中出现1 次,而在类别1 中从未出现。对应的条件概率分别为0.041666 67 与0.0。该计算是正确的。

测试算法: 根据现实情况修改分类器




检查这两条曲线,就会发现它们在相同区域内同时增加或者减少,并且在相同点上取到极值。它们的取值虽然不同,但不影响最终结果。

我们修改后的分类器为:

"""
朴素贝叶斯分类器训练函数
"""
def train_native_bayes(train_matrix,train_category):
    num_train_docs=len(train_matrix)
    num_words=len(train_matrix[0])
    p=sum(train_category)/float(num_train_docs)
    p_0_num=ones(num_words)
    p_1_num=ones(num_words)
    p_0_denom=2.0
    p_1_denom=2.0
    for i in range(num_train_docs):
        if train_category[i]==1:
            p_1_num+=train_matrix[i]
            p_1_denom+=sum(train_matrix[i])
        else:
            p_0_num+=train_matrix[i]
            p_0_denom+=sum(train_matrix[i])
    p_1_vector=log(p_1_num/p_1_denom)
    p_0_vector=log(p_0_num/p_0_denom)
    return p_0_vector,p_1_vector,p

朴素贝叶斯分类函数

def classify_native_bayes(need_to_classify_vector, p_0_vector, p_1_vector, p_class):
    p_1 = sum(need_to_classify_vector * p_1_vector) + log(p_class)    #element-wise mult
    p_0 = sum(need_to_classify_vector * p_0_vector) + log(1.0 - p_class)
    if p_1 > p_0:
        return 1
    else:
        return 0

要分类的向量need to classify vector 以及使用函数train native bayes() 计算得到的三个概率。使用NumPy 的数组来计算两个向量相乘的结果。这里的相乘是指对应元素相乘,即先将两个向量中的第1 个元素相乘,然后将第2 个元素相乘,以此类推。接下来将词汇表中所有词的对应值相加,然后将该值加到类别的对数概率上。最后,比较类别的概率返回大概率对应的类别标签。

def testing_native_bayes():
    dataset,class_vector=load_dataset()
    my_vacab_set = create_vocab_list(dataset)
    my_vacab_set.sort()
    train_mat=[]
    for i in dataset:
        train_mat.append(set_of_words_vector(my_vacab_set, i))
    p_0_vector,p_1_vector,p = train_native_bayes(array(train_mat),array(class_vector))
    test_entry = ['love','my']
    this_doc = array(set_of_words_vector(my_vacab_set, test_entry))
    print(test_entry,'classified as: ',classify_native_bayes(this_doc,p_0_vector,p_1_vector,p))
    test_entry_1 = ['stupid','garbage']
    this_doc_1 = array(set_of_words_vector(my_vacab_set, test_entry_1))
    print(test_entry_1,'classified as: ',classify_native_bayes(this_doc_1,p_0_vector,p_1_vector,p))

下面来看看实际结果:

['love', 'my'] classified as:  0
['stupid', 'garbage'] classified as:  1

准备数据:文档词袋模型

目前为止,我们将每个词的出现与否作为一个特征,这可以被描述为词集模型(setof-words model)。如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现在文档中所不能表达的某种信息,这种方法被称为词袋模型(bag-of-words model)。

在词袋中,每个单词可以出现多次,而在词集中,每个词只能出现一次。为适应词袋模型,需要对函数set of words vector() 稍加修改,修改后的函数称为bag of words vector()。

下面的程序给出了基于词袋模型的朴素贝叶斯代码。它与函数set of words vector()几乎完全相同,唯一不同的是每当遇到一个单词时,它会增加词向量中的对应值,而不只是将对应的数值设为1。

"""
朴素贝叶斯词袋模型
如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现中文档中所不能表达的某种信息
"""
def bag_word_vector(vocab_list,input_set):
    return_vector=[0]*len(vocab_list)
    for word in input_set:
        if word in vocab_list:
            return_vector[vocab_list.index(word)]+=1
    return return_vector

使用朴素贝叶斯过滤垃圾邮件

在前面那个简单的例子中,我们引入了字符串列表。使用朴素贝叶斯解决一些现实生活中的问题时,需要先从文本内容得到字符串列表,然后生成词向量。

下面这个例子中,我们将了解朴素贝叶斯的一个最著名的应用:电子邮件垃圾过滤。

  1. 准备数据:切分文本

前一节中的词向量是预先给定的,下面介绍如何从文本文档中构建自己的词列表。

对于一个文本字符串,可以使用Python 的string.split() 方法将其切分。但是标点符号也被当成了词的一部分。

我们可以使用正则表示式来切分句子,其中分隔符是除单词、数字外的任意字符串。

但是里面的空字符串需要去掉。可以计算每个字符串的长度,只返回长度大于0 的字符串。

我们的Python 代码实现如下:

"""
函数说明:接收一个大字符串并将其解析为字符串列表
"""
def text_parse(big_string):
    # 将字符串转换为字符列表
    list_of_tokens = re.split(r"[0-9!@#$%^&*()?\n~]",big_string) # 将特殊符号作为切分标志进行字符串切分,即非字母、非数字
    return [tok.lower() for tok in list_of_tokens if len(tok) > 2] # 除了单个字母,例如大写的I,其它单词变成小写

2.测试算法:使用朴素贝叶斯进行交叉验证
现在来看数据集中一封完整的电子邮件的实际处理结果。该数据集放在email 文件夹中,该文件夹又包含两个子文件夹,分别是spam 与ham。

我们的分类函数如下:

"""
函数说明:测试朴素贝叶斯分类器,使用朴素贝叶斯进行交叉验证
"""
def spam_test():
    doc_list=[]
    class_vector=[]
    full_text=[]
    for i in range(1,26): # 遍历25个txt文件
        word_list=text_parse(open('native_bayes  email dataset/spam/%d.txt'%i,'r').read()) # 读取每个垃圾邮件,并字符串转换成字符串列表
        doc_list.append(word_list)
        full_text.extend(word_list)
        class_vector.append(1) # 标记垃圾邮件,1表示垃圾文件
        word_list=text_parse(open('native_bayes  email dataset/ham/%d.txt'%i,'r').read()) # 读取每个非垃圾邮件,并字符串转换成字符串列表
        doc_list.append(word_list)
        full_text.extend(word_list)
        class_vector.append(0) # 标记正常邮件,0表示正常文件
    vocab_list=create_vocab_list(doc_list) # 创建词汇表,不重复
    training_set=list(range(50))
    test_set=[] # 创建存储训练集的索引值的列表和测试集的索引值的列表
    for i in range(0,10): # 从50个邮件中,随机挑选出40个作为训练集,10个做测试集
        rand_index=int(random.uniform(0,len(training_set))) # 随机选取索索引值
        test_set.append(training_set[rand_index]) # 添加测试集的索引值
        del (training_set[rand_index]) # 在训练集列表中删除添加到测试集的索引值
    train_mat=[]
    train_class=[] # 创建训练集矩阵和训练集类别标签系向量
    for doc_index in training_set: # 遍历训练集
        train_mat.append(set_of_words_vector(vocab_list,doc_list[doc_index])) # 将生成的词集模型添加到训练矩阵中
        train_class.append(class_vector[doc_index]) # 将类别添加到训练集类别标签系向量中
    p_0_vector,p_1_vector,p=train_native_bayes(array(train_mat),array(train_class)) # 训练朴素贝叶斯模型
    error_count=0 # 错误分类计数
    for doc_index in test_set: # 遍历测试集
        word_vector=set_of_words_vector(vocab_list,doc_list[doc_index]) # 测试集的词集模型
        if classify_native_bayes(array(word_vector),p_0_vector,p_1_vector,p)!=class_vector[doc_index]: # 如果分类错误
            error_count+=1 # 错误计数加1
            print('classify error:',doc_list[doc_index])
    print('the error rate is:',float(error_count)/len(test_set))

导入文件夹spam 与ham 下的文本文件,并将它们解析为词列表。接下来构建一个测试集与一个训练集,两个集合中的邮件都是随机选出的。

本例中共有50 封电子邮件,并不是很多,其中的10 封电子邮件被随机选择为测试集。分类器所需要的概率计算只利用训练集中的文档来完成。Python 变量training set 是一个整数列表,其中的值从0 到49。接下来,随机选择其中10 个文件。选择出的数字所对应的文档被添加到测试集,同时也将其从训练集中剔除。这种随机选择数据的一部分作为训练集,而剩余部分作为测试集的过程称为留存交叉验证(hold-out cross validation)。

我们得到的运行结果如下:

classify error: ['get up to ', ' off at online watchesstore', 'discount watches for all famous brands', ' watches: arolexbvlgari, dior, hermes, oris, cartier, ap and more brands', ' louis vuitton bags ', ' wallets', ' gucci bags', ' tiffany ', ' co jewerly', 'enjoy a full ', ' year warranty', 'shipment via reputable courier: fedex, ups, dhl and ems speedpost', 'you will ', ' recieve your order']
classify error: ['get up to ', ' off at online watchesstore', 'discount watches for all famous brands', ' watches: arolexbvlgari, dior, hermes, oris, cartier, ap and more brands', ' louis vuitton bags ', ' wallets', ' gucci bags', ' tiffany ', ' co jewerly', 'enjoy a full ', ' year warranty', 'shipment via reputable courier: fedex, ups, dhl and ems speedpost', 'you will ', ' recieve your order', 'save up to ', ' off quality watches']
classify error: ['ordercializviagra online ', ' save ', 'nline pharmacy noprescription required', 'buy canadian drugs at wholesale prices and save ', 'fda-approved drugs + superb quality drugs only', 'accept all major credit cards', '        order today', ' from ']
the error rate is: 0.3

3.K 折交叉验证
假定现在只完成了一次迭代,那么为了更精确地估计分类器的错误率,就应该进行多次迭代后求出平均错误率。

def one_cross_validate(train_set,train_class,test_set,test_class):
    #训练模型
    p_0_vector,p_1_vector,p_c_1 = train_native_bayes(array(train_set),array(train_class))
    error_count = 0
    #验证集进行测试
    for i in range(10):
        c = classify_native_bayes(array(test_set[i]),p_0_vector,p_1_vector,p_c_1)
        if c != test_class[i]:
            error_count += 1
    return error_count/10

def K_Cross_Validate(train_mat,train_class_vector):  #K折交叉验证 5
    rand_index = list(range(50))
    random.shuffle(rand_index)
    error_radio = 0.0
    for i in range(5):  #5次
        index = rand_index #随机索引
        #选取训练集、验证集索引
        train_set = []
        train_cls = []
        test_set = []
        test_cls = []
        test_set_index = set(rand_index[10*i:10*i+10])  # 测试集10
        train_set_index = set(index)-test_set_index  # 验证集
        #选取训练集、验证集数据
        for idx in train_set_index:
            train_set.append(train_mat[idx])
            train_cls.append(train_class_vector[idx])
        for idx in test_set_index:
            test_set.append(train_mat[idx])
            test_cls.append(train_class_vector[idx])
        print('第%d个子集的误差率为:'%(i+1),one_cross_validate(train_set,train_cls,test_set,test_cls))
        error_radio += one_cross_validate(train_set,train_cls,test_set,test_cls)
    return error_radio/5

我们最后是求五次的平均误差率。

我们导入需要的数据集并输出最终的结果为:

def create_dataset():
    data_set_list=[]  #全部数据集
    class_vector = []    #标签值
    #获取数据
    spam_path = "native_bayes  email dataset/spam/{}.txt"  #获取文件路径
    ham_path = "native_bayes  email dataset/ham/{}.txt"
    for i in range(1, 26):  # 两个路径各有25个文件
        document_data_1 = open(spam_path.format(i), 'r').read()
        # 使用正则进行分割,除了空格、还有标点都可以用于分割
        word_vector = text_parse(document_data_1) # \W*表示匹配多个非字母、数字、下划线的字符
        data_set_list.append([item for item in word_vector if len(item) > 0])
        class_vector.append(1)
        document_data_2 = open(ham_path.format(i), 'r').read()
        # 使用正则进行分割,除了空格、还有标点都可以用于分割
        word_vector_2 = text_parse(document_data_2)  # \W*表示匹配多个非字母、数字、下划线的字符
        data_set_list.append([item for item in word_vector_2 if len(item) > 0])
        class_vector.append(0)
    return data_set_list, class_vector

运行结果如下:

1个子集的误差率为: 0.22个子集的误差率为: 0.43个子集的误差率为: 0.24个子集的误差率为: 0.25个子集的误差率为: 0.4
5折交叉验证的错误率为:
 0.27999999999999997

需要注意的是,因为我们对数据集的划分是随机的,所以每次运行的结果会不相同。

完整代码

from numpy import *
import re

def load_dataset():
    dataset = [['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']]
    class_vector = [0, 1, 0, 1, 0, 1]  # 1 is abusive, 0 not
    return dataset,class_vector
dataset,class_vector=load_dataset()
print('数据集为:\n',dataset)
print('=='*30)
print('数据标签为:\n',class_vector)
print('=='*30)

def create_vocab_list(dataset):
    """创建一个包含所有文档且不出现重复词的列表"""
    vocab_set = set([])  #create empty set
    for document in dataset:
        vocab_set = vocab_set | set(document) #set()去掉列表中的重复词
    return list(vocab_set)

my_vacab_set=create_vocab_list(dataset)
print(my_vacab_set)

#词集模型
"""
输入为词汇表和文档,检查文档中的单词是否在词汇表中
采用词集模型:即对每条文档只记录某个词汇是否存在,而不记录出现的次数
创建一个与词汇表长度一致的0向量,在当前样本中出现的词汇标记为1
将一篇文档转换为词向量
"""
def set_of_words_vector(vocab_list, input_set):
    return_vector = [0]*len(vocab_list)
    for word in input_set:
        if word in vocab_list:
            return_vector[vocab_list.index(word)] = 1
        else:
            print("the word: %s is not in my Vocabulary!" % word)
    return return_vector
print(set_of_words_vector(my_vacab_set,dataset[0]))
"""
朴素贝叶斯词袋模型
如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现中文档中所不能表达的某种信息
"""
def bag_word_vector(vocab_list,input_set):
    return_vector=[0]*len(vocab_list)
    for word in input_set:
        if word in vocab_list:
            return_vector[vocab_list.index(word)]+=1
    return return_vector
print(bag_word_vector(my_vacab_set,dataset[0]))
"""
朴素贝叶斯分类器训练函数
"""
def train_native_bayes(train_matrix,train_category):
    num_train_docs=len(train_matrix)
    num_words=len(train_matrix[0])
    p=sum(train_category)/float(num_train_docs)
    p_0_num=ones(num_words)
    p_1_num=ones(num_words)
    p_0_denom=2.0
    p_1_denom=2.0
    for i in range(num_train_docs):
        if train_category[i]==1:
            p_1_num+=train_matrix[i]
            p_1_denom+=sum(train_matrix[i])
        else:
            p_0_num+=train_matrix[i]
            p_0_denom+=sum(train_matrix[i])
    p_1_vector=log(p_1_num/p_1_denom)
    p_0_vector=log(p_0_num/p_0_denom)
    return p_0_vector,p_1_vector,p

train_mat=[]
for i in dataset:
    train_mat.append(set_of_words_vector(my_vacab_set,i))
p_0_vector,p_1_vector,p=train_native_bayes(train_mat,class_vector)
print(p)
print(p_0_vector)

def classify_native_bayes(need_to_classify_vector, p_0_vector, p_1_vector, p_class):
    p_1 = sum(need_to_classify_vector * p_1_vector) + log(p_class)    #element-wise mult
    p_0 = sum(need_to_classify_vector * p_0_vector) + log(1.0 - p_class)
    if p_1 > p_0:
        return 1
    else:
        return 0

def testing_native_bayes():
    dataset,class_vector=load_dataset()
    my_vacab_set = create_vocab_list(dataset)
    my_vacab_set.sort()
    train_mat=[]
    for i in dataset:
        train_mat.append(set_of_words_vector(my_vacab_set, i))
    p_0_vector,p_1_vector,p = train_native_bayes(array(train_mat),array(class_vector))
    test_entry = ['love','my']
    this_doc = array(set_of_words_vector(my_vacab_set, test_entry))
    print(test_entry,'classified as: ',classify_native_bayes(this_doc,p_0_vector,p_1_vector,p))
    test_entry_1 = ['stupid','garbage']
    this_doc_1 = array(set_of_words_vector(my_vacab_set, test_entry_1))
    print(test_entry_1,'classified as: ',classify_native_bayes(this_doc_1,p_0_vector,p_1_vector,p))
print(testing_native_bayes())

"""
函数说明:接收一个大字符串并将其解析为字符串列表
"""
def text_parse(big_string):
    # 将字符串转换为字符列表
    list_of_tokens = re.split(r"[0-9!@#$%^&*()?\n~]",big_string) # 将特殊符号作为切分标志进行字符串切分,即非字母、非数字
    return [tok.lower() for tok in list_of_tokens if len(tok) > 2] # 除了单个字母,例如大写的I,其它单词变成小写

"""
函数说明:测试朴素贝叶斯分类器,使用朴素贝叶斯进行交叉验证
"""
def spam_test():
    doc_list=[]
    class_vector=[]
    full_text=[]
    for i in range(1,26): # 遍历25个txt文件
        word_list=text_parse(open('native_bayes  email dataset/spam/%d.txt'%i,'r').read()) # 读取每个垃圾邮件,并字符串转换成字符串列表
        doc_list.append(word_list)
        full_text.extend(word_list)
        class_vector.append(1) # 标记垃圾邮件,1表示垃圾文件
        word_list=text_parse(open('native_bayes  email dataset/ham/%d.txt'%i,'r').read()) # 读取每个非垃圾邮件,并字符串转换成字符串列表
        doc_list.append(word_list)
        full_text.extend(word_list)
        class_vector.append(0) # 标记正常邮件,0表示正常文件
    vocab_list=create_vocab_list(doc_list) # 创建词汇表,不重复
    training_set=list(range(50))
    test_set=[] # 创建存储训练集的索引值的列表和测试集的索引值的列表
    for i in range(0,10): # 从50个邮件中,随机挑选出40个作为训练集,10个做测试集
        rand_index=int(random.uniform(0,len(training_set))) # 随机选取索索引值
        test_set.append(training_set[rand_index]) # 添加测试集的索引值
        del (training_set[rand_index]) # 在训练集列表中删除添加到测试集的索引值
    train_mat=[]
    train_class=[] # 创建训练集矩阵和训练集类别标签系向量
    for doc_index in training_set: # 遍历训练集
        train_mat.append(set_of_words_vector(vocab_list,doc_list[doc_index])) # 将生成的词集模型添加到训练矩阵中
        train_class.append(class_vector[doc_index]) # 将类别添加到训练集类别标签系向量中
    p_0_vector,p_1_vector,p=train_native_bayes(array(train_mat),array(train_class)) # 训练朴素贝叶斯模型
    error_count=0 # 错误分类计数
    for doc_index in test_set: # 遍历测试集
        word_vector=set_of_words_vector(vocab_list,doc_list[doc_index]) # 测试集的词集模型
        if classify_native_bayes(array(word_vector),p_0_vector,p_1_vector,p)!=class_vector[doc_index]: # 如果分类错误
            error_count+=1 # 错误计数加1
            print('classify error:',doc_list[doc_index])
    print('the error rate is:',float(error_count)/len(test_set))

spam_test()

def one_cross_validate(train_set,train_class,test_set,test_class):
    #训练模型
    p_0_vector,p_1_vector,p_c_1 = train_native_bayes(array(train_set),array(train_class))
    error_count = 0
    #验证集进行测试
    for i in range(10):
        c = classify_native_bayes(array(test_set[i]),p_0_vector,p_1_vector,p_c_1)
        if c != test_class[i]:
            error_count += 1
    return error_count/10

def K_Cross_Validate(train_mat,train_class_vector):  #K折交叉验证 5
    rand_index = list(range(50))
    random.shuffle(rand_index)
    error_radio = 0.0
    for i in range(5):  #5次
        index = rand_index #随机索引
        #选取训练集、验证集索引
        train_set = []
        train_cls = []
        test_set = []
        test_cls = []
        test_set_index = set(rand_index[10*i:10*i+10])  # 测试集10
        train_set_index = set(index)-test_set_index  # 验证集
        #选取训练集、验证集数据
        for idx in train_set_index:
            train_set.append(train_mat[idx])
            train_cls.append(train_class_vector[idx])
        for idx in test_set_index:
            test_set.append(train_mat[idx])
            test_cls.append(train_class_vector[idx])
        print('第%d个子集的误差率为:'%(i+1),one_cross_validate(train_set,train_cls,test_set,test_cls))
        error_radio += one_cross_validate(train_set,train_cls,test_set,test_cls)
    return error_radio/5

def create_dataset():
    data_set_list=[]  #全部数据集
    class_vector = []    #标签值
    #获取数据
    spam_path = "native_bayes  email dataset/spam/{}.txt"  #获取文件路径
    ham_path = "native_bayes  email dataset/ham/{}.txt"
    for i in range(1, 26):  # 两个路径各有25个文件
        document_data_1 = open(spam_path.format(i), 'r').read()
        # 使用正则进行分割,除了空格、还有标点都可以用于分割
        word_vector = text_parse(document_data_1) # \W*表示匹配多个非字母、数字、下划线的字符
        data_set_list.append([item for item in word_vector if len(item) > 0])
        class_vector.append(1)
        document_data_2 = open(ham_path.format(i), 'r').read()
        # 使用正则进行分割,除了空格、还有标点都可以用于分割
        word_vector_2 = text_parse(document_data_2)  # \W*表示匹配多个非字母、数字、下划线的字符
        data_set_list.append([item for item in word_vector_2 if len(item) > 0])
        class_vector.append(0)
    return data_set_list, class_vector
data_set_list, class_vector=create_dataset()
vocab_list = create_vocab_list(data_set_list)
trainMulList = []
for doc in data_set_list:
    trainMulList.append(set_of_words_vector(vocab_list,doc))
print('=='*30)
print('5折交叉验证的错误率为:\n',K_Cross_Validate(trainMulList,class_vector))
  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旅途中的宽~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值