上面运行的程序中listOPosts中有六句话,把每句话的单词拆分开来。listClasses表示着这六句话是侮辱类还是非侮辱类,比如第一句话:my dog has flea problems help please,是非侮辱类,所以标为0。myVocabList是listOPosts六句话的所有单词的集合,setOfWords2Vec函数使listOPosts中的句子由myVocabList来表示,比如listOPosts[0]中有help这个单词,那么myVocabList[2]就是1。而trianMat就是listOPosts中的六句话全部用myVocabList来表示。
def trainNB0(trainMatrix,trainCategory): numTrainDocs = len(trainMatrix) numWords = len(trainMatrix[0]) pAbusive = sum(trainCategory)/float(numTrainDocs) p0Num = zeros(numWords); p1Num = zeros(numWords) #change to ones() p0Denom = 0; p1Denom = 0 #change to 2.0 for i in range(numTrainDocs): if trainCategory[i] == 1: p1Num += trainMatrix[i] p1Denom += sum(trainMatrix[i]) else: p0Num += trainMatrix[i] p0Denom += sum(trainMatrix[i]) p1Vect = p1Num/p1Denom #change to log() p0Vect = p0Num/p0Denom #change to log() return p0Vect,p1Vect,pAbusive 该函数的输入参数是: >>> p0V,p1V,pAb=bayes.trainNB0(trainMat,listClasses) sum(trainCategory)代表的是侮辱性的句子的条数,float(numTrainDocs)是总的句子的条数,pAbusive就是输入的样本中侮辱性句子所占的比例,这里为0.5。numWords是所有单词集合的个数,接下来的for语句中一个句子一个句子进行分析,p1Num是一个具有numWords个元素的列表,如果一个句子是侮辱性的,那么trainMatrix中跟句子相关的列表会累加起来。p0Num是一个具有numWords个元素的列表,如果一个句子是非侮辱性的,那么trainMatrix中跟句子相关的列表会累加起来。从p1V和p0V可以看出相应单词在侮辱性或非侮辱性句子中出现的比例。如p1V[26]是stupid,表示在所有侮辱性句子的单词中,这个词所占的比例是0.15789474。
>>> p0V array([ 0.04166667, 0.04166667, 0.04166667, 0. , 0. , 0.04166667, 0.04166667, 0.04166667, 0. , 0.04166667, 0.04166667, 0.04166667, 0.04166667, 0. , 0. , 0.08333333, 0. , 0. , 0.04166667, 0. , 0.04166667, 0.04166667, 0. , 0.04166667, 0.04166667, 0.04166667, 0. , 0.04166667, 0. , 0.04166667, 0.04166667, 0.125 ]) >>> sum(p0V) 0.99999999999999967 >>> p1V array([ 0. , 0. , 0. , 0.05263158, 0.05263158, 0. , 0. , 0. , 0.05263158, 0.05263158, 0. , 0. , 0. , 0.05263158, 0.05263158, 0.05263158, 0.05263158, 0.05263158, 0. , 0.10526316, 0. , 0.05263158, 0.05263158, 0. , 0.10526316, 0. , 0.15789474, 0. , 0.05263158, 0. , 0. , 0. ]) >>> sum(p1V) 0.99999999999999978 def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): p1 = sum(vec2Classify * p1Vec) + log(pClass1) #element-wise mult p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1) if p1 > p0: return 1 else: return 0 def testingNB(): listOPosts,listClasses = loadDataSet() myVocabList = createVocabList(listOPosts) trainMat=[] for postinDoc in listOPosts: trainMat.append(setOfWords2Vec(myVocabList, postinDoc)) p0V,p1V,pAb = trainNB0(array(trainMat),array(listClasses)) testEntry = ['love', 'my', 'dalmation'] thisDoc = array(setOfWords2Vec(myVocabList, testEntry)) print testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb) testEntry = ['stupid', 'garbage'] thisDoc = array(setOfWords2Vec(myVocabList, testEntry)) print testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb) 上面testingNB()函数中将love my dalmation转化成由myVocabList表示的数组,然后分别与p0V和p1V相乘,看哪个值大,就判定这句话是什么类型的。 bagOfWords2VecMN函数和setOfWords2Vec函数的区别在于用 returnVec[vocabList.index(word)] += 1 替换 returnVec[vocabList.index(word)] = 1 因为输入的句子中有可能有相同的单词。 def textParse(bigString): #input is big string, #output is word list import re listOfTokens = re.split(r'\W*', bigString) return [tok.lower() for tok in listOfTokens if len(tok) > 2] def spamTest(): docList=[]; classList = []; fullText =[] for i in range(1,26): wordList = textParse(open('email/spam/%d.txt' % i).read()) docList.append(wordList) fullText.extend(wordList) classList.append(1) wordList = textParse(open('email/ham/%d.txt' % i).read()) docList.append(wordList) fullText.extend(wordList) classList.append(0) vocabList = createVocabList(docList)#create vocabulary trainingSet = 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 = trainNB0(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 上面的第一个函数textParse将邮件的句子拆分成单词。spamTest函数中的docList相当于前面的 listOPosts,classList相当于前面的listClasses,spam和ham文件夹中各有25封邮件,将这50封邮件拆成单词,并存放于docList中。spam中的邮件标为1,ham中邮件标为0,记录在classList中。vocabList包含了50封邮件中的所有拆分的单词。然后50封邮件中随机抽取10封为测试邮件testSet,其余40封为trainingSet。由trainingSet中的邮件做成trainMat和trainingClasses,然后得到p0V,p1V和pSpam。接下来用p0V,p1V和pSpam三个参数对testSet中的10封邮件进行测试,根据测出的结果(0或1)与已知的结果比较,得出错误的概率。 >>> ny=feedparser.parse('http://newyork.craigslist.org/search/stp/index.rss') >>> sf=feedparser.parse('http://sfbay.craigslist.org/search/stp/index.rss') >>> sf['entries'] [] >>> ny['entries'] [] >>> import bayes >>> vocaList,pSF,pNY=bayes.localWords(ny,sf) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "bayes.py", line 142, in localWords testSet.append(trainingSet[randIndex]) IndexError: list index out of range
上面的程序运行起来和书中的不一样,可能是RSS修改了书中ny['entries']的长度为100,但是这里该长度为0。所以程序中运行的参数trainingSet变成了0,所以报错了。