《机器学习实战》chapter04 使用Python进行文本分类
一、使用樸素貝葉斯過(guò)濾垃圾郵件
使用樸素貝葉斯計(jì)算每一封郵件是垃圾郵件的概率p1和非垃圾郵件的概率p0,如果p1 > p0,則是垃圾郵件,否則不是。
首先,我們先介紹一個(gè)例子:
對(duì)于任意一條評(píng)論是否帶有侮辱性質(zhì)?我們通常看這個(gè)評(píng)論中是否包含侮辱性詞匯,對(duì)于人來(lái)說(shuō),侮辱性詞匯我們一眼就能夠看出來(lái),可是計(jì)算機(jī)并不理解什么是侮辱性,而我們又不能直接告訴計(jì)算機(jī)哪些詞是侮辱性的,因?yàn)槲覀円擦信e不全,我們應(yīng)該讓程序本身去判斷一個(gè)詞是侮辱性的概率,所以我們需要通過(guò)給定數(shù)據(jù)集訓(xùn)練算法。
表示句子中包含w(單詞組合)時(shí)是侮辱性或非侮辱性的概率
所以:表示這個(gè)句子是侮辱性(非侮辱性)的概率,等于訓(xùn)練集中侮辱性的句子總數(shù)/總句子數(shù)(非侮辱性的句子總數(shù)/總句子數(shù))
表示這個(gè)句子是侮辱性(非侮辱性)條件下句子里每一個(gè)單詞是侮辱性(非侮辱性)的概率
表示這個(gè)句子中每一個(gè)單詞在總的訓(xùn)練集下出現(xiàn)的概率
當(dāng)需要測(cè)試一個(gè)句子是否有侮辱性時(shí),我們只需要判斷這個(gè)句子是侮辱性的概率是否大于非侮辱性的概率?(p1>p0?)
轉(zhuǎn)化成代碼上的一些實(shí)現(xiàn)問(wèn)題:
表達(dá)式,其中w是一個(gè)向量,表示在條件下這個(gè)句子中每一個(gè)單詞出現(xiàn)的概率,為了使這個(gè)公式的可編程性更好我們把他轉(zhuǎn)換成兩個(gè)向量的乘積:
Vec2Classify*p1Vec
這兩個(gè)向量的長(zhǎng)度都是訓(xùn)練集中所有詞匯的集合(無(wú)重復(fù))長(zhǎng)度
Vec2Classify中把在當(dāng)前句子中出現(xiàn)了的單詞索引位置置為1,未出現(xiàn)的置為0。
p1Vec訓(xùn)練集中所有出現(xiàn)過(guò)的單詞是侮辱性的概率(侮辱性的概率用每個(gè)單詞在侮辱性句子中出現(xiàn)的頻數(shù)/侮辱性句子中總的單詞數(shù)表示,非侮辱性保存在p0Vec中)
用每個(gè)單詞在訓(xùn)練集所有句子中出現(xiàn)的頻數(shù)/所有句子總的單詞數(shù)。又因?yàn)槲覀冏罱K要判斷的是p1>p0?而在p1和p0的計(jì)算中都會(huì)除以這個(gè)值,且這個(gè)值是一樣的,所以我們可以不用計(jì)算,直接省去。
過(guò)濾郵件與這個(gè)其實(shí)就是一樣的。1.1、準(zhǔn)備數(shù)據(jù):將文本文件解析成詞條向量
- 從文本中構(gòu)建詞向量
- 參數(shù):a、詞匯表;b、輸入的文檔
- 對(duì)于文檔進(jìn)行切分:a、以字母數(shù)字之外的字符作為分隔符切分;b、去掉切分后生成的空白串;c、統(tǒng)一詞格式(都變小寫(xiě));
textParse()函數(shù)接受一個(gè)大字符串并將其解析為字符串列表
# 創(chuàng)建實(shí)驗(yàn)樣本 def loadDataSet():postingList = [['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'],['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]# 0代標(biāo)非侮辱性言論, 1代標(biāo)侮辱性言論classVec = [0, 1, 0, 1, 0, 1]return postingList, classVec# 創(chuàng)建一個(gè)包含在所有文檔中出現(xiàn)的不重復(fù)的詞表 def createVocabList(dataSet):# 創(chuàng)建一個(gè)空集vocabSet = set([])# 將每篇文檔返回的新詞集合添加到該集合中for document in dataSet:# 創(chuàng)建兩個(gè)集合的并集vocabSet = vocabSet | set(document)return list(vocabSet)# vocabList 詞匯表, inputList 文檔, 返回值 文檔向量 def setOfWords2Vec(vocabList, inputSet):# 創(chuàng)建一個(gè)其中所含元素都為0的向量,表示詞匯表中的單詞是否在文檔中出現(xiàn)returnVec = [0] * len(vocabList)# 遍歷文檔中的所有單詞,檢查是否出現(xiàn)在詞匯表中for word in inputSet:if word in vocabList:# 在詞匯表中出現(xiàn),標(biāo)記為1returnVec[vocabList.index(word)] = 1else:print("the word: %s is not in my vocabulary!" % word)# 返回文檔向量,return returnVecloadDataSet()函數(shù)是在之前沒(méi)用文本里的數(shù)據(jù)時(shí)建的一個(gè)測(cè)試數(shù)據(jù)集,加載了一個(gè)實(shí)驗(yàn)樣本
createVocabList()會(huì)創(chuàng)建一個(gè)包含在所有文檔中出現(xiàn)的不重復(fù)詞列表
獲得詞匯表后,使用setOfWords2Vec()函數(shù),輸入?yún)?shù)為詞匯表以及某個(gè)切分后的文檔,輸出是文檔向量,向量的每一元素為1或0,分別表示詞匯表中的單詞在文檔中是否出現(xiàn),出現(xiàn)為1。
1.2 訓(xùn)練算法:從詞向量計(jì)算概率
- 偽代碼:
計(jì)算每個(gè)類別中文檔的數(shù)目
對(duì)每篇訓(xùn)練文檔:
? ? 對(duì)每個(gè)類別:
????? ? 如果詞條出現(xiàn)在文檔中-->增加該詞條的計(jì)數(shù)值
????? ? 增加所有詞條的計(jì)數(shù)值
對(duì)每個(gè)類別:
????對(duì)每個(gè)詞條:
????? ? 將該詞條的數(shù)目除以總詞條數(shù)目得到條件概率
返回每個(gè)類別的條件概率
# 訓(xùn)練樸素貝葉斯算法,trainMatrix 文檔矩陣;trainCategory 文檔類別標(biāo)簽所構(gòu)成的向量 def trainNB0(trainMatrix, trainCategory):# 計(jì)算文檔數(shù)目和第一個(gè)文檔中詞條數(shù)numTrainDocs = len(trainMatrix)numWords = len(trainMatrix[0])# 求在訓(xùn)練集中任取一個(gè)文檔是侮辱性(trainCategory=1)的概率pAbusive = sum(trainCategory) / float(numTrainDocs)# 初始化,沒(méi)有用p0Num = zeros(numWords),是為了避免某一個(gè)概率值為0,# 使得最后的乘積也是0,即使變成log(),log(0)也是不對(duì)的p0Num = ones(numWords)p1Num = ones(numWords)# 相應(yīng)地 p0Denom = 0.0 修改為 p0Denom = 2.0p0Denom = 2.0p1Denom = 2.0for i in range(numTrainDocs):# 如果是侮辱性言論,if trainCategory[i] == 1:# 把當(dāng)前文檔的詞條向量加到p1Num上,p1Num:侮辱性言論中每個(gè)單詞出現(xiàn)次數(shù)p1Num += trainMatrix[i]# p1Denom:侮辱性言論中總單詞數(shù)p1Denom += sum(trainMatrix[i])# 非侮辱性else:# 把當(dāng)前文檔的詞條向量加到p0Num上,p0Num:非侮辱性言論中每個(gè)單詞出現(xiàn)次數(shù)p0Num += trainMatrix[i]# p0Denom:非侮辱性言論中總單詞數(shù)p0Denom += sum(trainMatrix[i])# 對(duì)每個(gè)元素做除法(log把乘變成加避免下溢出)p1Vect = log(p1Num / p1Denom)# print(p1Vect)p0Vect = log(p0Num / p0Denom)# print(p0Vect)# lineplot(p0Num, p0Vect)return p0Vect, p1Vect, pAbusivedef classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):# p1:是侮辱性文檔的概率,對(duì)每一個(gè)單詞累加log()概率p1 = sum(vec2Classify * p1Vec) + log(pClass1)# p0:是非侮辱性文檔的概率p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)# 如果p1 > p0,侮辱性文檔,反之,非if p1 > p0:return 1else:return 01.3 測(cè)試算法:使用樸素貝葉斯進(jìn)行交叉驗(yàn)證
# 字符串拆分、小寫(xiě)、去除長(zhǎng)度小于3的 def textParse(bigString):listOfTokens = re.split('\w+', bigString)return [tok.lower() for tok in listOfTokens if len(tok) > 2]# spamTest(): def spamTest():docList = []classList = []fullList = []for i in range(1, 26):# 把spam(垃圾郵件)文件夾下的文本加入到docList、fullList中wordList = textParse(open('email/spam/%d.txt' % i).read())docList.append(wordList)fullList.extend(wordList)# 垃圾郵件類別為1,calssList加入1classList.append(1)# 把ham(非垃圾郵件)文件夾下的文本加入到docList、fullList中wordList = textParse(open('email/ham/%d.txt' % i, encoding='gb18030', errors='ignore').read())docList.append(wordList)fullList.extend(wordList)# 垃圾郵件類別為0,calssList加入0classList.append(0)# 根據(jù)輸入的文檔生成包含文檔中所有單詞的詞匯表vocabList = bayes.createVocabList(docList)# 生成長(zhǎng)度為50的列表,元素值為0-49,用作docList列表的索引trainingSet = list(range(50))# 聲明testSet列表testSet = []# 在trainingSet中任取10個(gè)不重復(fù)數(shù)據(jù)的索引加入到測(cè)試及for i in range(10):randIndex = int(random.uniform(0, len(trainingSet)))# 把索引對(duì)應(yīng)的trainingSet中的值加入到testSet中testSet.append(trainingSet[randIndex])# 刪除加入到testSet中的索引del trainingSet[randIndex]# 聲明trainMat(訓(xùn)練數(shù)據(jù)集)、trainClasses(訓(xùn)練數(shù)據(jù)集的分類列表)trainMat = []trainClasses = []# 給訓(xùn)練數(shù)據(jù)集、trainClasses添加數(shù)據(jù)for docIndex in trainingSet:trainMat.append(bayes.setOfWords2Vec(vocabList, docList[docIndex]))trainClasses.append(classList[docIndex])# 調(diào)用訓(xùn)練算法進(jìn)行訓(xùn)練p0V, p1V, pSpam = bayes.trainNB0(array(trainMat), array(trainClasses))# 使用測(cè)試數(shù)據(jù)集測(cè)試訓(xùn)練后的算法的錯(cuò)誤率errorCount = 0for docIndex in testSet:# 對(duì)于測(cè)試數(shù)據(jù),求每一個(gè)文檔的詞條向量wordVector = bayes.setOfWords2Vec(vocabList, docList[docIndex])# 對(duì)每一個(gè)詞條向量分類并與真實(shí)分類進(jìn)行比較計(jì)算錯(cuò)誤率if bayes.classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:errorCount += 1print('the error rate is:', float(errorCount) / len(testSet))第一個(gè)函數(shù)textParse()接受一個(gè)大字符串并將其解析為字符串列表
第二個(gè)函數(shù)spamTest()對(duì)貝葉斯垃圾郵件分類器進(jìn)行自動(dòng)化處理
2 示例:使用樸素貝葉斯分類器從個(gè)人廣告中獲取區(qū)域傾向
我們將分別從美國(guó)的兩個(gè)城市中選取一些人, 通過(guò)分析這些人發(fā)布的征婚廣告信息,來(lái)比較兩個(gè)城市的人們?cè)趶V告用詞上是否不同。
2.1 收集數(shù)據(jù):導(dǎo)入RSS源
需要feedparse包來(lái)作為RSS閱讀器
2.2 測(cè)試算法
# 返回出現(xiàn)頻率最高的30個(gè)詞 def calcMostFreq(vocabList, fullText):freqDict = {}for token in vocabList:freqDict[token] = fullText.count(token)sortedFreq = sorted(freqDict.items(), key=operator.itemgetter(1), reverse=True)return sortedFreq[:30]# 加載數(shù)據(jù),計(jì)算貝葉斯的錯(cuò)誤率 def localWords(feed1, feed0):docList = []classList = []fullText = []minLen = min(len(feed1['entries']), len(feed0['entries']))for i in range(minLen):wordList = textParse(feed1['entries'][i]['summary'])docList.append(wordList)fullText.extend(wordList)classList.append(1)wordList = textParse(feed0['entries'][i]['summary'])docList.append(wordList)fullText.extend(wordList)classList.append(0)vocabList = createVocabList(docList)# 獲取出現(xiàn)頻率最高的30個(gè)詞top30Words = calcMostFreq(vocabList, fullText)# 去掉出現(xiàn)次數(shù)最高的30個(gè)詞,語(yǔ)言中大部分都是冗余和結(jié)構(gòu)輔助性內(nèi)容,# 即出現(xiàn)次數(shù)多的中有大量的停用詞for pairW in top30Words:if pairW[0] in vocabList:vocabList.remove(pairW[0])trainingSet = list(range(2 * minLen))testSet = []# 任取20條數(shù)據(jù)的索引加入到測(cè)試數(shù)據(jù)集中for i in range(20):randIndex = int(random.uniform(0, len(trainingSet)))testSet.append(trainingSet[randIndex])del trainingSet[randIndex]# 構(gòu)造訓(xùn)練數(shù)據(jù)集trainMat = []trainClasses = []for docIndex in trainingSet:# 詞袋模型trainMat.append(bagOfWord2VecMN(vocabList, docList[docIndex]))trainClasses.append(classList[docIndex])# 訓(xùn)練算法p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses))# 初始化錯(cuò)誤率,并計(jì)算算法錯(cuò)誤率errorCount = 0for docIndex in testSet:wordVector = bagOfWord2VecMN(vocabList, docList[docIndex])if classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:errorCount += 1print('the error rate is : ', float(errorCount) / len(testSet))return vocabList, p0V, p1V# 測(cè)試 ny = feedparser.parse('http://newyork.craigslist.org/stp/index.rss') sf = feedparser.parse('http://sfbay.craigslist.org/stp/index.rss') vocablist, psf, pny = localWords(ny, sf)loacalWords()使用兩個(gè)RSS源作為參數(shù)。RSS源要在函數(shù)外導(dǎo)入,這樣做的原因是RSS源會(huì)隨時(shí)間而改變。
總結(jié)
以上是生活随笔為你收集整理的《机器学习实战》chapter04 使用Python进行文本分类的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 《机器学习实战》chapter03 决策
- 下一篇: websocket python爬虫_p