优点:在数据较少的情况下仍然有效,可以处理多类别问题。
缺点:对于输入数据的准备方式较为敏感。
适用数据类型:标称型数据。3.1 准备数据:从文本中构建词向量
# 词表到向量的转换函数 def load_data_set(): # 词条切分后的文档集合,列表每一行代表一个文档 posting_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'], ['my', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'], ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']] # 1 代表侮辱性文字, 0代表正常言论 class_vec = [0, 1, 0, 1, 0, 1] return posting_list, class_vec # 创建一个包含在所有文档中出现的不重复词的列表 def create_vocab_list(data_set): vocab_set = set([]) # 创建一个空集 for document in data_set: vocab_set = vocab_set | set(document) # 创建两个集合的并集 return list(vocab_set) def set_of_words_to_vec(vocab_list, input_set): """ 该函数的输入参数为词汇表及某个文档,输出的是文档向量,向量的每一元素为1或0, 分别表示词汇表中的单词在输入文档中是否出现。函数首先创建一个和词汇表等长的 向量,并将其元素都设置为0 。接着,遍历文档中的所有单词,如果出现了词汇表中 的单词,则将输出的文档向量中的对应值设为1。一切都顺利的话,就不需要检查某 个词是否还在vocab_list中。 """ return_vec = [0] * len(vocab_list) for word in input_set: if word in vocab_list: return_vec[vocab_list.index(word)] = 1 else: print("the word: %s is not in my Vocabulary!" % word) return return_vec
3.2 训练算法:从词向量计算概率
该函数的伪代码如下: 计算每个类别中的文档数目 对每篇训练文档: 对每个类别: 如果词条出现在文档中→ 增加该词条的计数值 增加所有词条的计数值 对每个类别: 对每个词条: 将该词条的数目除以总词条数目得到条件概率 返回每个类别的条件概率
# 朴素贝叶斯分类器训练函数 def train_nb0(train_matrix, train_category): # 获取文档矩阵中文档的数目 num_train_docs = len(train_matrix) # 获取词条向量的长度 num_words = len(train_matrix[0]) p_abusive = sum(train_category) / float(num_train_docs) p0_num = np.ones(num_words) p1_num = np.ones(num_words) # change to ones() p0_denom = 2.0 p1_denom = 2.0 # change to 2.0 for i in range(num_train_docs): if train_category[i] == 1: # 统计所有类别为1的词条向量中各个词条出现的次数 p1_num += train_matrix[i] # 统计类别为1的词条向量中出现的所有词条的总数 # 即统计类1所有文档中出现单词的数目 p1_denom += sum(train_matrix[i]) else: p0_num += train_matrix[i] p0_denom += sum(train_matrix[i]) # 利用NumPy数组计算p(wi|c1), 为避免下溢出问题,改为log() p1_vect = np.log(p1_num / p1_denom) # change to log() p0_vect = np.log(p0_num / p0_denom) # change to log() return p0_vect, p1_vect, p_abusive
# 朴素贝叶斯分类函数 def classify_nb(vec_to_classify, p0_vec, p1_vec, p_class1): p1 = sum(vec_to_classify * p1_vec) + np.log(p_class1) # 元素相乘 p0 = sum(vec_to_classify * p0_vec) + np.log(1.0 - p_class1) if p1 > p0: return 1 else: return 0 def bag_of_words_to_vec(vocab_list, input_set): return_vec = [0] * len(vocab_list) for word in input_set: if word in vocab_list: return_vec[vocab_list.index(word)] += 1 return return_vec def testing_nb(): list_posts, list_classes = load_data_set() my_vocab_list = create_vocab_list(list_posts) train_mat = [] for postin_doc in list_posts: train_mat.append(set_of_words_to_vec(my_vocab_list, postin_doc)) p0_v, p1_v, p_ab = train_nb0(np.array(train_mat), np.array(list_classes)) test_entry = ['love', 'my', 'dalmation'] this_doc = np.array(set_of_words_to_vec(my_vocab_list, test_entry)) print(test_entry, 'classified as: ', classify_nb(this_doc, p0_v, p1_v, p_ab)) test_entry = ['stupid', 'garbage'] this_doc = np.array(set_of_words_to_vec(my_vocab_list, test_entry)) print(test_entry, 'classified as: ', classify_nb(this_doc, p0_v, p1_v, p_ab))
# 文件解析及完整的垃圾邮件测试函数 def text_parse(big_string): """ 接受一个大字符串并将其解析为字符串列表。该函数去掉少于两 个字符的字符串,并将所有字符串转换为小写。 """ list_of_tokens = re.split(r'\W*', big_string) return [tok.lower() for tok in list_of_tokens if len(tok) > 2] def spam_test(): """ 对贝叶斯垃圾邮件分类器进行自动化处理。导入文件夹spam与ham下的文本文件,并将它们解析为词列表 。 接下来构建一个测试集与一个训练集,两个集合中的邮件都是随机选出的。本例中共有50封电子邮件,其中 的10封电子邮件被随机选择为测试集。分类器所需要的概率计算只利用训练集中的文档来完成。 Python变 量training_set是一个整数列表,其中的值从0到49。接下来,随机选择其中10个文件 。选择出的数字所 对应的文档被添加到测试集,同时也将其从训练集中剔除。这种随机选择数据的一部分作为训练集,而剩余 部分作为测试集的过程称为留存交叉验证。假定现在只完成了一次迭代,那么为了更精确地估计分类器的错 误率,就应该进行多次迭代后求出平均错误率。接下来的for循环遍历训练集的所有文档,对每封邮件基于 词汇表并使用set_of_words_to_vec()函数来构建词向量。这些词在traind_nb0()函数中用于计算分类 所需的概率。然后遍历测试集,对其中每封电子邮件进行分类 。如果邮件分类错误,则错误数加1,最后 给出总的错误百分比。 """ doc_list = [] class_list = [] full_text = [] for i in range(1, 26): word_list = text_parse(open('email/spam/%d.txt' % i).read()) doc_list.append(word_list) full_text.extend(word_list) class_list.append(1) word_list = text_parse(open('email/ham/%d.txt' % i).read()) doc_list.append(word_list) full_text.extend(word_list) class_list.append(0) vocab_list = create_vocab_list(doc_list) # create vocabulary training_set = list(range(50)) test_set = [] # create test set for i in range(10): rand_index = int(np.random.uniform(0, len(training_set))) test_set.append(training_set[rand_index]) del (training_set[rand_index]) train_mat = [] train_classes = [] for doc_index in training_set: train_mat.append(bag_of_words_to_vec(vocab_list, doc_list[doc_index])) train_classes.append(class_list[doc_index]) p0_v, p1_v, p_spam = train_nb0(np.array(train_mat), np.array(train_classes)) error_count = 0 for doc_index in test_set: # classify the remaining items word_vector = bag_of_words_to_vec(vocab_list, doc_list[doc_index]) if classify_nb(np.array(word_vector), p0_v, p1_v, p_spam) != class_list[doc_index]: error_count += 1 print("classification error", doc_list[doc_index]) print('the error rate is: ', float(error_count) / len(test_set))
# encoding: utf-8 """ @author:max bay @version: python 3.6 @time: 2018/6/2 13:41 """ import numpy as np import re import operator import feedparser # 词表到向量的转换函数 def load_data_set(): # 词条切分后的文档集合,列表每一行代表一个文档 posting_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'], ['my', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'], ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']] # 1 代表侮辱性文字, 0代表正常言论 class_vec = [0, 1, 0, 1, 0, 1] return posting_list, class_vec # 创建一个包含在所有文档中出现的不重复词的列表 def create_vocab_list(data_set): vocab_set = set([]) # 创建一个空集 for document in data_set: vocab_set = vocab_set | set(document) # 创建两个集合的并集 return list(vocab_set) def set_of_words_to_vec(vocab_list, input_set): """ 该函数的输入参数为词汇表及某个文档,输出的是文档向量,向量的每一元素为1或0, 分别表示词汇表中的单词在输入文档中是否出现。函数首先创建一个和词汇表等长的 向量,并将其元素都设置为0 。接着,遍历文档中的所有单词,如果出现了词汇表中 的单词,则将输出的文档向量中的对应值设为1。一切都顺利的话,就不需要检查某 个词是否还在vocab_list中。 """ return_vec = [0] * len(vocab_list) for word in input_set: if word in vocab_list: return_vec[vocab_list.index(word)] = 1 else: print("the word: %s is not in my Vocabulary!" % word) return return_vec # 朴素贝叶斯分类器训练函数 def train_nb0(train_matrix, train_category): # 获取文档矩阵中文档的数目 num_train_docs = len(train_matrix) # 获取词条向量的长度 num_words = len(train_matrix[0]) p_abusive = sum(train_category) / float(num_train_docs) p0_num = np.ones(num_words) p1_num = np.ones(num_words) # change to ones() p0_denom = 2.0 p1_denom = 2.0 # change to 2.0 for i in range(num_train_docs): if train_category[i] == 1: # 统计所有类别为1的词条向量中各个词条出现的次数 p1_num += train_matrix[i] # 统计类别为1的词条向量中出现的所有词条的总数 # 即统计类1所有文档中出现单词的数目 p1_denom += sum(train_matrix[i]) else: p0_num += train_matrix[i] p0_denom += sum(train_matrix[i]) # 利用NumPy数组计算p(wi|c1), 为避免下溢出问题,改为log() p1_vect = np.log(p1_num / p1_denom) # change to log() p0_vect = np.log(p0_num / p0_denom) # change to log() return p0_vect, p1_vect, p_abusive # 朴素贝叶斯分类函数 def classify_nb(vec_to_classify, p0_vec, p1_vec, p_class1): p1 = sum(vec_to_classify * p1_vec) + np.log(p_class1) # 元素相乘 p0 = sum(vec_to_classify * p0_vec) + np.log(1.0 - p_class1) if p1 > p0: return 1 else: return 0 def bag_of_words_to_vec(vocab_list, input_set): return_vec = [0] * len(vocab_list) for word in input_set: if word in vocab_list: return_vec[vocab_list.index(word)] += 1 return return_vec def testing_nb(): list_posts, list_classes = load_data_set() my_vocab_list = create_vocab_list(list_posts) train_mat = [] for postin_doc in list_posts: train_mat.append(set_of_words_to_vec(my_vocab_list, postin_doc)) p0_v, p1_v, p_ab = train_nb0(np.array(train_mat), np.array(list_classes)) test_entry = ['love', 'my', 'dalmation'] this_doc = np.array(set_of_words_to_vec(my_vocab_list, test_entry)) print(test_entry, 'classified as: ', classify_nb(this_doc, p0_v, p1_v, p_ab)) test_entry = ['stupid', 'garbage'] this_doc = np.array(set_of_words_to_vec(my_vocab_list, test_entry)) print(test_entry, 'classified as: ', classify_nb(this_doc, p0_v, p1_v, p_ab)) # 文件解析及完整的垃圾邮件测试函数 def text_parse(big_string): """ 接受一个大字符串并将其解析为字符串列表。该函数去掉少于两 个字符的字符串,并将所有字符串转换为小写。 """ list_of_tokens = re.split(r'\W*', big_string) return [tok.lower() for tok in list_of_tokens if len(tok) > 2] def spam_test(): """ 对贝叶斯垃圾邮件分类器进行自动化处理。导入文件夹spam与ham下的文本文件,并将它们解析为词列表 。 接下来构建一个测试集与一个训练集,两个集合中的邮件都是随机选出的。本例中共有50封电子邮件,其中 的10封电子邮件被随机选择为测试集。分类器所需要的概率计算只利用训练集中的文档来完成。 Python变 量training_set是一个整数列表,其中的值从0到49。接下来,随机选择其中10个文件 。选择出的数字所 对应的文档被添加到测试集,同时也将其从训练集中剔除。这种随机选择数据的一部分作为训练集,而剩余 部分作为测试集的过程称为留存交叉验证。假定现在只完成了一次迭代,那么为了更精确地估计分类器的错 误率,就应该进行多次迭代后求出平均错误率。接下来的for循环遍历训练集的所有文档,对每封邮件基于 词汇表并使用set_of_words_to_vec()函数来构建词向量。这些词在traind_nb0()函数中用于计算分类 所需的概率。然后遍历测试集,对其中每封电子邮件进行分类 。如果邮件分类错误,则错误数加1,最后 给出总的错误百分比。 """ doc_list = [] class_list = [] full_text = [] for i in range(1, 26): word_list = text_parse(open('email/spam/%d.txt' % i).read()) doc_list.append(word_list) full_text.extend(word_list) class_list.append(1) word_list = text_parse(open('email/ham/%d.txt' % i).read()) doc_list.append(word_list) full_text.extend(word_list) class_list.append(0) vocab_list = create_vocab_list(doc_list) # create vocabulary training_set = list(range(50)) test_set = [] # create test set for i in range(10): rand_index = int(np.random.uniform(0, len(training_set))) test_set.append(training_set[rand_index]) del (training_set[rand_index]) train_mat = [] train_classes = [] for doc_index in training_set: train_mat.append(bag_of_words_to_vec(vocab_list, doc_list[doc_index])) train_classes.append(class_list[doc_index]) p0_v, p1_v, p_spam = train_nb0(np.array(train_mat), np.array(train_classes)) error_count = 0 for doc_index in test_set: # classify the remaining items word_vector = bag_of_words_to_vec(vocab_list, doc_list[doc_index]) if classify_nb(np.array(word_vector), p0_v, p1_v, p_spam) != class_list[doc_index]: error_count += 1 print("classification error", doc_list[doc_index]) print('the error rate is: ', float(error_count) / len(test_set))
[3] 朴素贝叶斯---百度百科
[4] Python3《机器学习实战》学习笔记(五):朴素贝叶斯实战篇之新浪新闻分类
[5] 第4章 基于概率论的分类方法:朴素贝叶斯
[6] Python3《机器学习实战》学习笔记(四):朴素贝叶斯基础篇之言论过滤器