本系列代码托管于:https://github.com/chintsan-code/machine-learning-tutorials
本篇使用的项目为:bayes_email
在上一篇中,我们使用了改进后的贝叶斯分类器对于文本进行分类,本篇将继续使用这一分类器,并对垃圾邮件进行过滤,同时介绍留存交叉验证。
首先来看下我们的样本:
./email/spam路径下存放的是25份垃圾邮件
./email/ham路径下存放的是25份正常邮件
它们的内容都类似于:
基本上可以使用空格对这些文本进行切片,但是如果这样子,会引入标点符号等,因此我们本次使用正则模块来对文章进行切片,同时对切完之后的单词做一个长度的过滤,滤除过短的无用词。具体代码如下:
def textParse(bigString): # input is big string, #output is word list
import re
listOfTokens = re.split(r'\W+', bigString) # 有些版本需要将W+改为W*
return [tok.lower() for tok in listOfTokens if len(tok) > 2]
像上图所示的邮件,切片后的结果为:[‘codeine’, ’15mg’, ‘for’, ‘203’, ‘visa’, ‘only’, ‘codeine’, ‘methylmorphine’, ‘narcotic’, ‘opioid’, ‘pain’, ‘reliever’, ‘have’, ’15mg’, ’30mg’, ‘pills’, ’15mg’, ‘for’, ‘203’, ’15mg’, ‘for’, ‘385’, ’15mg’, ‘for’, ‘562’, ‘visa’, ‘only’]
数据集准备完成之后,我们就可以使用之间构建的分类器进行分类了。代码如下:
def spamTest():
docList = []
classList = []
fullText = []
for i in range(1, 26):
wordList = textParse(open('email/spam/%d.txt' % i, encoding="unicode_escape").read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(1)
wordList = textParse(open('email/ham/%d.txt' % i, encoding="unicode_escape").read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
vocabList = createVocabList(docList) # create vocabulary
# 从50封邮件中随机抽取10份做测试集,其余40份做训练集
trainingSet = list(range(50))
testSet = [] # create test set
for i in range(10):
randIndex = int(random.uniform(0, len(trainingSet)))
testSet.append(trainingSet[randIndex])
del (trainingSet[randIndex])
trainMat = []
trainClasses = []
for docIndex in trainingSet: # train the classifier (get probs) trainNB0
trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))
trainClasses.append(classList[docIndex])
p0V, p1V, pSpam = trainNB0mprovement(array(trainMat), array(trainClasses))
errorCount = 0
for docIndex in testSet: # classify the remaining items
wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])
if classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
errorCount += 1
print("classification error", docList[docIndex])
print('the error rate is: ', float(errorCount) / len(testSet))
# return vocabList,fullText
在这份代码中,我们在进行训练和测试时,我们再总共的50份样本(25份正样本,25份负样本)中,我们随机选取了10份样本作为测试集,剩余的40份样本作为训练集。这种随机选择数据的一部分作为训练集, 而剩余部分作为测试集的过程称为留存交叉验证(hold-out cross validation)。
我们之前将每个词的出现与否作为一个特征,这可以被描述为词集模型(set-of-words model)。如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现在文档中所不能表 达的某种信息,这种方法被称为词袋模型(bag-of-words model)。在词袋中,每个单词可以出现 多次,而在词集中,每个词只能出现一次。为适应词袋模型,需要对函数setOfWords2Vec() 稍加修改,修改后的函数称为bagOfWords2Vec() 。
词集模型对应我们之前提到的贝努利模型, 词袋模型对应多项式模型。在代码实现上,他们仅仅存在一处不同:
def setOfWords2Vec(vocabList, inputSet):
returnVec = [0] * len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] = 1
else:
print("the word: %s is not in my Vocabulary!" % word)
return returnVec
def bagOfWords2VecMN(vocabList, inputSet):
returnVec = [0] * len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] += 1 # 仅此处不同
return returnVec
函数spamTest()会输出在10封随机选择的电子邮件上的分类错误率。既然这些电子邮件是随机选择的,所以每次的输出结果可能有些差别。如果发现错误的话,函数会输出错分文档的词表,这样就可以了解到底是哪篇文档发生了错误。如果想要更好地估计错误率,那么就应该将上述过程重复多次,比如说10次,然后求平均值。 例如我某一次输出结果是这样的:
classification error ['home', 'based', 'business', 'opportunity', 'knocking', 'your', 'door', 'don', 'rude', 'and', 'let', 'this', 'chance', 'you', 'can', 'earn', 'great', 'income', 'and', 'find', 'your', 'financial', 'life', 'transformed', 'learn', 'more', 'here', 'your', 'success', 'work', 'from', 'home', 'finder', 'experts']
the error rate is: 0.1
即10个测试集出现了1次错误。
评论 (0)