关于本站
1、基于Django+Bootstrap开发
2、主要发表本人的技术原创博客
3、本站于 2015-12-01 开始建站
看懂该篇需要先看如下博文,本文大部分知识和代码会使用到前面如下博文:
在机器学习07:朴素贝叶斯应用(词集模式)中第2部分处理句子,将句子向量化的时候采用词集模式。
该模式只是体现处理的句子中的单词是否出现,没有提醒单词出现次数。同个单词出现次数越多说明权重越大。
若需要体现该权重,可以修改上篇博文的bayes.py文件中的Python代码。(复制一份新的,并重命名为bayes_email.py)
添加如下方法(见《机器学习实战》第64页):
def set_of_words_to_bag(vocab_list, input_set): '''对比句子向量和词汇表,找到对应出现在词汇表的位置以及个数''' return map(lambda x:input_set.count(x), vocab_list)
书中的代码过于啰嗦,其实一句话可以实现词袋模式。
再将使用set_of_words_to_vect方法的地方都改成使用set_of_words_to_bag方法。
bayes_email.py文件中核心的朴素贝叶斯算法的代码无需改变,思路一致。
我们准备使用词袋模式的朴素贝叶斯算法判断是否是垃圾邮件。
《机器学习实战》已经给我们提供相关数据,打开如下链接,下载email.zip文件。
https://github.com/pbharrin/machinelearninginaction/tree/master/Ch04
该压缩包中有两个文件夹:ham(正常邮件)和spam(垃圾邮件)。解压放到bayes_email.py一起。
我们需要使用ham和spam这两个文件夹中的邮件内容作为训练集。
那么还需要修改bayes_email.py的创建训练集方法。
ham和spam文件夹中各有25个txt文件。每个文件都代表一封邮件的内容。邮件内容是正常的句子,所以我们不能只是使用空格拆分单词,还要考虑标点符号等等。
假设我已经获取了一个txt文件内容,拆分单词代码如下:
#coding:utf-8 import os import re #拆分邮件 def text_parse(text): #使用非字母和非数字的字符拆分 words = re.split(r'\W*', text) #保留长度大于2的单词 return [x.lower() for x in words if len(x)>2]
使用字母和数字之外的字符拆分邮件内容,最后会残留一些无意义的缩写或介词之类的。通常这些单词长度是1~2个。过滤掉这些即可,保留有实际意义的单词。
单个邮件内容处理方法添加之后,再调整创建训练集的代码。如下:
def create_dataset(): #遍历ham和spam文件夹,获取邮件内容列表 folders = [u'ham', u'spam'] posting_list = [] class_vect = [] for i, folder in enumerate(folders): for f in os.listdir(folder): #判断文件是否为txt文件 if os.path.splitext(f)[-1] != '.txt': continue #合并路径,读取文件内容 f_path = os.path.join(folder, f) with open(f_path, 'r') as txt: #新增文本拆分结果 posting_list.append(text_parse(txt.read())) #新增该文本所属类别。ham为0,spam为1 class_vect.append(i) return posting_list, class_vect
可以测试一下是否顺利创建训练集。如下图:
到这里,词袋模式的代码修改完毕。
(不要问朴素贝叶斯是什么,朴素贝叶斯其他代码在哪里。看前面两篇博文)
相关内容太多,我不想再说第2次。
写好分类函数,测试一下。我们可以拿训练集的数据测试该算法的错误率。如下代码:
#测试方法 def testing_nb(): #创建训练集 posting_list, class_vect = create_dataset() #获取词汇表 vocab_list = create_vocab_list(posting_list) #句子单词数字化 posting_vects = [] for posting in posting_list: posting_vect = set_of_words_to_bag(vocab_list, posting) posting_vects.append(posting_vect) #获得相关概率 p_vect, p_class = train_nb0(np.array(posting_vects), np.array(class_vect)) #以上是通用代码,下面是分类器测试 #测试分类器 #遍历ham和spam文件夹,获取邮件内容列表 folders = [u'ham', u'spam'] err_num = 0 all_num = 0 for i, folder in enumerate(folders): for f in os.listdir(folder): #判断文件是否为txt文件 if os.path.splitext(f)[-1] != '.txt': continue #合并路径,读取文件内容 f_path = os.path.join(folder, f) with open(f_path, 'r') as txt: #文本拆分 words = text_parse(txt.read()) vect_test = set_of_words_to_bag(vocab_list, words) #用朴素贝叶斯判断该邮件内容类别 class_type, _ = classify_nb(vect_test, p_vect, p_class) #判断结果是否正确 if class_type != i: err_num += 1 all_num += 1 #返回错误率 return err_num / float(all_num)
执行该方法,测试错误率为0.0。
(错误率竟然比《机器学习实战》书中的错误率还要低)
完整代码如下:
#coding:utf-8 #词袋模式,以句子为单位,体现单词出现次数 import numpy as np import os import re #拆分邮件 def text_parse(text): #使用非字母和非数字的字符拆分 words = re.split(r'\W*', text) #保留长度大于2的单词 return [x.lower() for x in words if len(x)>2] def create_dataset(): #遍历ham和spam文件夹,获取邮件内容列表 folders = [u'ham', u'spam'] posting_list = [] class_vect = [] for i, folder in enumerate(folders): for f in os.listdir(folder): #判断文件是否为txt文件 if os.path.splitext(f)[-1] != '.txt': continue #合并路径,读取文件内容 f_path = os.path.join(folder, f) with open(f_path, 'r') as txt: #新增文本拆分结果 posting_list.append(text_parse(txt.read())) #新增该文本所属类别。ham为0,spam为1 class_vect.append(i) return posting_list, class_vect def create_vocab_list(dataset): '''对所有句子取并集,获取词汇表''' return list(reduce(lambda x,y:x|set(y), [set([])] + dataset)) def set_of_words_to_bag(vocab_list, input_set): '''对比句子向量和词汇表,找到对应出现在词汇表的位置以及个数''' return map(lambda x:input_set.count(x), vocab_list) #训练函数(句子数字化之后的矩阵,句子的性质列表) def train_nb0(posting_vects, train_classes): num_train_docs = len(posting_vects) #总句子数 num_words = len(posting_vects[0]) #词汇表单词数 #遍历分类标签,统计对应类别的数量 p_class_num = {} #各类别句子数 p_vect_num = {} #各类别各单词数 for i, class_type in enumerate(train_classes): #累计每个类别的单词数(初始化每个类别单词数为1个) p_vect_num[class_type] = p_vect_num.get(class_type, np.ones(num_words)) + posting_vects[i] #累计每个类别的句子数 p_class_num[class_type] = p_class_num.get(class_type, 0) + 1 #计算每个类别的条件概率,对应单词数除以该类别的单词总数 p_class = {} #各类别的概率 p_vect = {} #各类别的各单词条件概率 for class_type in train_classes: p_vect[class_type] = p_vect_num[class_type]/np.sum(p_vect_num[class_type]) p_class[class_type] = p_class_num[class_type]/float(num_train_docs) return p_vect, p_class #朴素贝叶斯分类函数 def classify_nb(vect_classify, p_vect, p_class): #计算句子属于各类别的概率 p = {} for class_type, vect in p_vect.items(): p[class_type] = np.sum(vect_classify * np.log(vect)) + np.log(p_class[class_type]) #获取最大概率的类别 return max(p.items(), key=lambda x:x[1]) #测试方法 def testing_nb(): #创建训练集 posting_list, class_vect = create_dataset() #获取词汇表 vocab_list = create_vocab_list(posting_list) #句子单词数字化 posting_vects = [] for posting in posting_list: posting_vect = set_of_words_to_bag(vocab_list, posting) posting_vects.append(posting_vect) #获得相关概率 p_vect, p_class = train_nb0(np.array(posting_vects), np.array(class_vect)) #以上是通用代码,下面是分类器测试 #测试分类器 #遍历ham和spam文件夹,获取邮件内容列表 folders = [u'ham', u'spam'] err_num = 0 all_num = 0 for i, folder in enumerate(folders): for f in os.listdir(folder): #判断文件是否为txt文件 if os.path.splitext(f)[-1] != '.txt': continue #合并路径,读取文件内容 f_path = os.path.join(folder, f) with open(f_path, 'r') as txt: #文本拆分 words = text_parse(txt.read()) vect_test = set_of_words_to_bag(vocab_list, words) #用朴素贝叶斯判断该邮件内容类别 class_type, _ = classify_nb(vect_test, p_vect, p_class) #判断结果是否正确 if class_type != i: err_num += 1 all_num += 1 #返回错误率 return err_num / float(all_num) if __name__ == '__main__': print(testing_nb())
点击查看相关目录。
相关专题: 机器学习实战