vlambda博客
学习文章列表

朴素贝叶斯 - 过滤垃圾邮件

准备数据:切分文本

        

        之前我们介绍了如何创建一个词向量,并且基于词向量进行朴素贝叶斯分类。但是,先前的词向量是预先给出的,我们应该如何从文本文档中构建属于自己的词向量呢?

        对于一个文本字符串,我们科研使用内置函数string.split()进行切分:

mySent = "This book is the best book on Python or M.L. I have ever laid eyes upon."print(mySent.split())
>>>['This', 'book', 'is', 'the', 'best', 'book', 'on', 'Python', 'or', 'M.L.', 'I', 'have', 'ever', 'laid', 'eyes', 'upon.']

        

        我们看到标点符号也被当成了词的一部分(“M.L.”)。

        所以改进切分方法,尝试通过正则表达式来切分,将分隔符定为除字母和数字以外的任意字符。

import reregEx = re.compile("\\W+")mySent = "This book is the best book on Python or M.L. I have ever laid eyes upon."listOfTokens = regEx.split(mySent)listOfTokens = [tok for tok in listOfTokens if len(tok)>0] #筛选出单词长度不为0的listOfTokens = [tok.lower() for tok in listOfTokens] # 将所有单词全部转换成小写print(listOfTokens)
>>>['this', 'book', 'is', 'the', 'best', 'book', 'on', 'python', 'or', 'm', 'l', 'i', 'have', 'ever', 'laid', 'eyes', 'upon']


测试算法:使用朴素贝叶斯进行交叉验证

def textParse(bigString): # 用于接受一个字符串并解析成列表 import re listOfTakens = re.split(r'\W+', bigString) return [tok.lower() for tok in listOfTakens if len(tok) > 2] # 转换成小写并去掉小于两个字符的
def spamTest(): docList = [] ; classList = [] ; fullList = [] for i in range(1, 26): wordList = textParse(open(r'E:\work\ML\机器学习实战\machinelearninginaction\Ch04\email\spam\%d.txt' % i).read()) docList.append(wordList) # 添加的是列表 fullList.extend(wordList) # 将元素添加到列表 classList.append(1) # 对相应的spam贴上标签1 wordList = textParse(open(r'E:\work\ML\机器学习实战\machinelearninginaction\Ch04\email\ham\%d.txt' % i).read()) docList.append(wordList) fullList.extend(wordList) classList.append(0) # 相应的ham贴上标签0 vocabList = createVocabList(docList) # 将docList的所有元素转换成无重复列表 #trainingSet = range(50); testSet = [] trainingSet = list(range(50)); testSet = [] for i in range(10): # 随机选取10封邮件作为测试集 randIndex = int(random.uniform(0, len(trainingSet))) # random.uniform()会随机生成一个在[0, len(trainingSet)]范围内的整数 testSet.append(trainingSet[randIndex]) # 将这个编号添加到测试集 del(trainingSet[randIndex]) # 并从训练集中删除 trainMat = [] ; trainClasses = [] for docIndex in trainingSet: trainMat.append(setOfWords2Vec(vocabList, docList[docIndex])) # 查找预测对象在词汇表中的出现情况 trainClasses.append(classList[docIndex]) # 添加预测对象的标签 p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses)) # 返回预测结果 errorCount = 0 for docIndex in testSet: wordVector = setOfWords2Vec(vocabList, docList[docIndex]) if classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]: errorCount += 1 print("the error rate is: " + str(float(errorCount)/len(testSet)))

                我们可以看到,第一个函数rextParse()将大字符串解析成了字符串列表,并且去掉了长度小于2的元素。第二个函数spamText()导入了两个文件夹下的文本文件,并且贴上标签。最重要的是,该函数随机选出了一部分数据作为训练集,而剩余的部分作为测试集。假设只完成了一次划分,那么可能会存在随机误差,高估或者低估模型的准确性,所以通过多次划分数据,最后求出平均错误率,用平均错误率作为衡量分类准确性的指标。这个过程叫做交叉验证。


bayes.spamTest()
>>>the error rate is: 0.1

        最终结果是10%的错误率。

        我们通过朴素贝叶斯的学习,发现该算法主要是通过计算查询文本在词汇表中出现的单词的频率,没有考虑到单词的位置。而我们的NLL基因家族的分析,有一个关键信息是L出现的位置,所以我认为该算法并不适用于LRR蛋白家族的鉴定。


        由于不可抗因素导致plantFamily公布时期待定,我也趁着这个机会规划了一下自己的硕士生涯。细想之下,三年硕士已经过去了一大半,跑过了不少数据,从基础的RNA-seq、BS-seq到GWAS分析再到Nanopore测序技术,期间接受的各种零零散散探索性分析更是不少,仿佛是个数据产出机器,自己的时间也就像是衔尾蛇般如环无端,大量的时间投入到了克莱因瓶中,却毫无成果,升学更是希望渺茫。所以不如还是花点时间做些让自己真正快乐的事情,希望能够把机器学习学扎实,提前做好面向就业的准备。