《机器学习实战》chapter04 使用Python进行文本分类
一、使用樸素貝葉斯過濾垃圾郵件
使用樸素貝葉斯計算每一封郵件是垃圾郵件的概率p1和非垃圾郵件的概率p0,如果p1 > p0,則是垃圾郵件,否則不是。
首先,我們先介紹一個例子:
對于任意一條評論是否帶有侮辱性質?我們通常看這個評論中是否包含侮辱性詞匯,對于人來說,侮辱性詞匯我們一眼就能夠看出來,可是計算機并不理解什么是侮辱性,而我們又不能直接告訴計算機哪些詞是侮辱性的,因為我們也列舉不全,我們應該讓程序本身去判斷一個詞是侮辱性的概率,所以我們需要通過給定數據集訓練算法。
表示句子中包含w(單詞組合)時是侮辱性或非侮辱性的概率
所以:表示這個句子是侮辱性(非侮辱性)的概率,等于訓練集中侮辱性的句子總數/總句子數(非侮辱性的句子總數/總句子數)
表示這個句子是侮辱性(非侮辱性)條件下句子里每一個單詞是侮辱性(非侮辱性)的概率
表示這個句子中每一個單詞在總的訓練集下出現的概率
當需要測試一個句子是否有侮辱性時,我們只需要判斷這個句子是侮辱性的概率是否大于非侮辱性的概率?(p1>p0?)
轉化成代碼上的一些實現問題:
表達式,其中w是一個向量,表示在條件下這個句子中每一個單詞出現的概率,為了使這個公式的可編程性更好我們把他轉換成兩個向量的乘積:
Vec2Classify*p1Vec
這兩個向量的長度都是訓練集中所有詞匯的集合(無重復)長度
Vec2Classify中把在當前句子中出現了的單詞索引位置置為1,未出現的置為0。
p1Vec訓練集中所有出現過的單詞是侮辱性的概率(侮辱性的概率用每個單詞在侮辱性句子中出現的頻數/侮辱性句子中總的單詞數表示,非侮辱性保存在p0Vec中)
用每個單詞在訓練集所有句子中出現的頻數/所有句子總的單詞數。又因為我們最終要判斷的是p1>p0?而在p1和p0的計算中都會除以這個值,且這個值是一樣的,所以我們可以不用計算,直接省去。
過濾郵件與這個其實就是一樣的。1.1、準備數據:將文本文件解析成詞條向量
- 從文本中構建詞向量
- 參數:a、詞匯表;b、輸入的文檔
- 對于文檔進行切分:a、以字母數字之外的字符作為分隔符切分;b、去掉切分后生成的空白串;c、統一詞格式(都變小寫);
textParse()函數接受一個大字符串并將其解析為字符串列表
# 創建實驗樣本 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代標非侮辱性言論, 1代標侮辱性言論classVec = [0, 1, 0, 1, 0, 1]return postingList, classVec# 創建一個包含在所有文檔中出現的不重復的詞表 def createVocabList(dataSet):# 創建一個空集vocabSet = set([])# 將每篇文檔返回的新詞集合添加到該集合中for document in dataSet:# 創建兩個集合的并集vocabSet = vocabSet | set(document)return list(vocabSet)# vocabList 詞匯表, inputList 文檔, 返回值 文檔向量 def setOfWords2Vec(vocabList, inputSet):# 創建一個其中所含元素都為0的向量,表示詞匯表中的單詞是否在文檔中出現returnVec = [0] * len(vocabList)# 遍歷文檔中的所有單詞,檢查是否出現在詞匯表中for word in inputSet:if word in vocabList:# 在詞匯表中出現,標記為1returnVec[vocabList.index(word)] = 1else:print("the word: %s is not in my vocabulary!" % word)# 返回文檔向量,return returnVecloadDataSet()函數是在之前沒用文本里的數據時建的一個測試數據集,加載了一個實驗樣本
createVocabList()會創建一個包含在所有文檔中出現的不重復詞列表
獲得詞匯表后,使用setOfWords2Vec()函數,輸入參數為詞匯表以及某個切分后的文檔,輸出是文檔向量,向量的每一元素為1或0,分別表示詞匯表中的單詞在文檔中是否出現,出現為1。
1.2 訓練算法:從詞向量計算概率
- 偽代碼:
計算每個類別中文檔的數目
對每篇訓練文檔:
? ? 對每個類別:
????? ? 如果詞條出現在文檔中-->增加該詞條的計數值
????? ? 增加所有詞條的計數值
對每個類別:
????對每個詞條:
????? ? 將該詞條的數目除以總詞條數目得到條件概率
返回每個類別的條件概率
# 訓練樸素貝葉斯算法,trainMatrix 文檔矩陣;trainCategory 文檔類別標簽所構成的向量 def trainNB0(trainMatrix, trainCategory):# 計算文檔數目和第一個文檔中詞條數numTrainDocs = len(trainMatrix)numWords = len(trainMatrix[0])# 求在訓練集中任取一個文檔是侮辱性(trainCategory=1)的概率pAbusive = sum(trainCategory) / float(numTrainDocs)# 初始化,沒有用p0Num = zeros(numWords),是為了避免某一個概率值為0,# 使得最后的乘積也是0,即使變成log(),log(0)也是不對的p0Num = ones(numWords)p1Num = ones(numWords)# 相應地 p0Denom = 0.0 修改為 p0Denom = 2.0p0Denom = 2.0p1Denom = 2.0for i in range(numTrainDocs):# 如果是侮辱性言論,if trainCategory[i] == 1:# 把當前文檔的詞條向量加到p1Num上,p1Num:侮辱性言論中每個單詞出現次數p1Num += trainMatrix[i]# p1Denom:侮辱性言論中總單詞數p1Denom += sum(trainMatrix[i])# 非侮辱性else:# 把當前文檔的詞條向量加到p0Num上,p0Num:非侮辱性言論中每個單詞出現次數p0Num += trainMatrix[i]# p0Denom:非侮辱性言論中總單詞數p0Denom += sum(trainMatrix[i])# 對每個元素做除法(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:是侮辱性文檔的概率,對每一個單詞累加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 測試算法:使用樸素貝葉斯進行交叉驗證
# 字符串拆分、小寫、去除長度小于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)# 根據輸入的文檔生成包含文檔中所有單詞的詞匯表vocabList = bayes.createVocabList(docList)# 生成長度為50的列表,元素值為0-49,用作docList列表的索引trainingSet = list(range(50))# 聲明testSet列表testSet = []# 在trainingSet中任取10個不重復數據的索引加入到測試及for i in range(10):randIndex = int(random.uniform(0, len(trainingSet)))# 把索引對應的trainingSet中的值加入到testSet中testSet.append(trainingSet[randIndex])# 刪除加入到testSet中的索引del trainingSet[randIndex]# 聲明trainMat(訓練數據集)、trainClasses(訓練數據集的分類列表)trainMat = []trainClasses = []# 給訓練數據集、trainClasses添加數據for docIndex in trainingSet:trainMat.append(bayes.setOfWords2Vec(vocabList, docList[docIndex]))trainClasses.append(classList[docIndex])# 調用訓練算法進行訓練p0V, p1V, pSpam = bayes.trainNB0(array(trainMat), array(trainClasses))# 使用測試數據集測試訓練后的算法的錯誤率errorCount = 0for docIndex in testSet:# 對于測試數據,求每一個文檔的詞條向量wordVector = bayes.setOfWords2Vec(vocabList, docList[docIndex])# 對每一個詞條向量分類并與真實分類進行比較計算錯誤率if bayes.classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:errorCount += 1print('the error rate is:', float(errorCount) / len(testSet))第一個函數textParse()接受一個大字符串并將其解析為字符串列表
第二個函數spamTest()對貝葉斯垃圾郵件分類器進行自動化處理
2 示例:使用樸素貝葉斯分類器從個人廣告中獲取區域傾向
我們將分別從美國的兩個城市中選取一些人, 通過分析這些人發布的征婚廣告信息,來比較兩個城市的人們在廣告用詞上是否不同。
2.1 收集數據:導入RSS源
需要feedparse包來作為RSS閱讀器
2.2 測試算法
# 返回出現頻率最高的30個詞 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]# 加載數據,計算貝葉斯的錯誤率 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)# 獲取出現頻率最高的30個詞top30Words = calcMostFreq(vocabList, fullText)# 去掉出現次數最高的30個詞,語言中大部分都是冗余和結構輔助性內容,# 即出現次數多的中有大量的停用詞for pairW in top30Words:if pairW[0] in vocabList:vocabList.remove(pairW[0])trainingSet = list(range(2 * minLen))testSet = []# 任取20條數據的索引加入到測試數據集中for i in range(20):randIndex = int(random.uniform(0, len(trainingSet)))testSet.append(trainingSet[randIndex])del trainingSet[randIndex]# 構造訓練數據集trainMat = []trainClasses = []for docIndex in trainingSet:# 詞袋模型trainMat.append(bagOfWord2VecMN(vocabList, docList[docIndex]))trainClasses.append(classList[docIndex])# 訓練算法p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses))# 初始化錯誤率,并計算算法錯誤率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# 測試 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()使用兩個RSS源作為參數。RSS源要在函數外導入,這樣做的原因是RSS源會隨時間而改變。
總結
以上是生活随笔為你收集整理的《机器学习实战》chapter04 使用Python进行文本分类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《机器学习实战》chapter03 决策
- 下一篇: Python中的向量、矩阵(numpy)