摘要:本篇內(nèi)容為機器學(xué)習(xí)實戰(zhàn)第章基于概率論的分類方法樸素貝葉斯程序清單。樸素貝葉斯優(yōu)點在數(shù)據(jù)較少的情況下仍然有效,可以處理多類別問題。參考鏈接機器學(xué)習(xí)實戰(zhàn)筆記之四基于概率論的分類方法樸素貝葉斯不足之處,歡迎指正。
本篇內(nèi)容為《機器學(xué)習(xí)實戰(zhàn)》第 4 章 基于概率論的分類方法:樸素貝葉斯程序清單。所用代碼為 python3。
樸素貝葉斯優(yōu)點:在數(shù)據(jù)較少的情況下仍然有效,可以處理多類別問題。
缺點:對于輸入數(shù)據(jù)的準備方式較為敏感。
適用數(shù)據(jù)類型:標稱型數(shù)據(jù)。
簡單描述這個過程為:從文本中獲取特征,構(gòu)建分類器,進行分類輸出結(jié)果。這里的特征是來自文本的詞條 (token),需要將每一個文本片段表示為一個詞條向量,其中值為 1 表示詞條出現(xiàn)在文檔中,0 表示詞條未出現(xiàn)。
接下來給出將文本轉(zhuǎn)換為數(shù)字向量的過程,然后基于這些向量來計算條件概率,并在此基礎(chǔ)上構(gòu)建分類器。
下面我們以在線社區(qū)的留言板為例,給出一個用來過濾的例子。準備數(shù)據(jù):從文本中構(gòu)建詞向量 程序清單 4-1 詞表到向量的轉(zhuǎn)換函數(shù)
為了不影響社區(qū)的發(fā)展,我們需要屏蔽侮辱性的言論,所以要構(gòu)建一個快速過濾器,如果某條留言使用來負面或者侮辱性的語言,就將該留言標識為內(nèi)容不當(dāng)。對此問題建立兩個類別:侮辱類和非侮辱類,分別使用 1 和 0 來表示。
""" Created on Sep 10, 2018 @author: yufei """ # coding=utf-8 from numpy import * # 創(chuàng)建一些實例樣本 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"]] classVec = [0,1,0,1,0,1] # 1 代表侮辱性文字,0 代表正常言論 """ 變量 postingList 返回的是進行詞條切分后的文檔集合。 留言文本被切分成一些列詞條集合,標點符號從文本中去掉 變量 classVec 返回一個類別標簽的集合。 這些文本的類別由人工標注,標注信息用于訓(xùn)練程序以便自動檢測侮辱性留言。 """ return postingList, classVec """ 創(chuàng)建一個包含在所有文檔中出現(xiàn)的不重復(fù)詞的列表 是用python的 Set 數(shù)據(jù)類型 將詞條列表輸給 Set 構(gòu)造函數(shù),set 就會返回一個不重復(fù)詞表 """ def createVocabList(dataSet): # 創(chuàng)建一個空集合 vocabSet = set([]) # 將每篇文檔返回的新詞集合添加進去,即創(chuàng)建兩個集合的并集 for document in dataSet: vocabSet = vocabSet | set(document) # 獲得詞匯表 return list(vocabSet) # 參數(shù):詞匯表,某個文檔 def setOfWords2Vec(vocabList, inputSet): # 創(chuàng)建一個和詞匯表等長的向量,將其元素都設(shè)置為 0 returnVec = [0] * len(vocabList) # 遍歷文檔中所有單詞 for word in inputSet: # 如果出現(xiàn)詞匯表中的單詞,將輸出的文檔向量中的對應(yīng)值設(shè)為 1 if word in vocabList: returnVec[vocabList.index(word)] = 1 else: print("the word: %s is not in my Vocabulary!" % word) # 輸出文檔向量,向量元素為 1 或 0 return returnVec
在 python 提示符下,執(zhí)行代碼并得到結(jié)果:
>>> import bayes >>> list0Posts, listClasses = bayes.loadDataSet() >>> myVocabList = bayes.createVocabList(list0Posts) >>> myVocabList ["problems", "mr", "ate", "buying", "not", "garbage", "how", "maybe", "stupid", "cute", "stop", "help", "dalmation", "take", "is", "worthless", "him", "flea", "park", "my", "I", "to", "licks", "steak", "dog", "love", "quit", "so", "please", "posting", "has", "food"]
即可得到的一個不會出現(xiàn)重復(fù)單詞的詞表myVocabList,目前該詞表還沒有排序。
繼續(xù)執(zhí)行代碼:
>>> bayes.setOfWords2Vec(myVocabList, list0Posts[3]) [0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] >>> bayes.setOfWords2Vec(myVocabList, list0Posts[0]) [0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0]
函數(shù)setOfWords2Vec使用詞匯表或者說想要檢查的所有單詞作為輸入,然后為其中每一個單詞構(gòu)建一個特征。一旦給定一篇文章(本例中指一條留言),該文檔就會被轉(zhuǎn)換為詞向量。
訓(xùn)練算法:從詞向量計算概率函數(shù)偽代碼如下:
··· 計算每個類別中的文檔數(shù)目 ··· 對每篇訓(xùn)練文檔: ······ 對每個類別: ········· 如果詞條出現(xiàn)在文檔中—>增加該詞條的計數(shù)值 ········· 增加所有詞條的計數(shù)值 ······ 對每個類別: ········· 對每個詞條: ············ 將該詞條對數(shù)目除以總詞條數(shù)目得到條件概率 ······ 返回每個類別對條件概率程序清單 4-2 樸素貝葉斯分類器訓(xùn)練函數(shù)
""" Created on Sep 11, 2018 @author: yufei """ # 參數(shù):文檔矩陣 trainMatrix,每篇文檔的類別標簽所構(gòu)成的向量 trainCategory def trainNB0(trainMatrix, trainCategory): numTrainDocs = len(trainMatrix) #文檔的個數(shù) numWords = len(trainMatrix[0]) #獲取第一篇文檔的單詞長度 """ 計算文檔屬于侮辱性文檔的概率 用類別為1的個數(shù)除以總篇數(shù) sum([0,1,0,1,0,1])=3,也即是 trainCategory 里面 1 的個數(shù) """ pAbusive = sum(trainCategory) / float(numTrainDocs) """ 初始化概率 當(dāng)利用貝葉斯分類器對文檔分類時,計算多個概率的乘積以獲得屬于某個類別的概率 把所有詞出現(xiàn)次數(shù)初始化為1,分母初始化為2,用log避免數(shù)太小被約掉 """ p0Num = ones(numWords) p1Num = ones(numWords) p0Denom = 2.0 p1Denom = 2.0 # 遍歷訓(xùn)練集 trainMatrix 中的所有文檔 for i in range(numTrainDocs): # 侮辱性詞語在某個文檔中出現(xiàn) if trainCategory[i] == 1: # 該詞對應(yīng)個數(shù)加一,即分子把所有的文檔向量按位置累加 # trainMatrix[2] = [1,0,1,1,0,0,0];trainMatrix[3] = [1,1,0,0,0,1,1] p1Num += trainMatrix[i] # 文檔總詞數(shù)加一,即對于分母 # 把trainMatrix[2]中的值先加起來為3,再把所有這個類別的向量都這樣累加起來,這個是計算單詞總數(shù)目 p1Denom += sum(trainMatrix[i]) # 正常詞語在某個文檔中出現(xiàn),同上 else: p0Num += trainMatrix[i] p0Denom +=sum(trainMatrix[i]) """ 對每個元素除以該類別的總詞數(shù),得條件概率 防止太多的很小的數(shù)相乘造成下溢。對乘積取對數(shù) # p1Vect = log(p1Num / p1Denom) # p0Vect = log(p0Num / p0Denom) """ p1Vect = p1Num / p1Denom p0Vect = p0Num / p0Denom """ 函數(shù)返回兩個向量和一個概率 返回每個類別的條件概率,是一個向量 在向量里面和詞匯表向量長度相同 每個位置代表這個單詞在這個類別中的概率 """ return p0Vect, p1Vect, pAbusive
在 python 提示符下,執(zhí)行代碼并得到結(jié)果:
>>> from numpy import * >>> importlib.reload(bayes)>>> list0Posts, listClasses = bayes.loadDataSet() >>> myVocabList = bayes.createVocabList(list0Posts)
以上,調(diào)入數(shù)據(jù)后構(gòu)建了一個包含所有詞的列表myVocabList
>>> trainMat = [] >>> for postinDoc in list0Posts: ... trainMat.append(bayes.setOfWords2Vec(myVocabList, postinDoc))
這個for循環(huán)使用詞向量來填充trainMat列表。
繼續(xù)給出屬于侮辱性文檔的概率以及兩個類別的概率向量。
>>> p0V, p1V, pAb = bayes.trainNB0(trainMat, listClasses)
查看變量的內(nèi)部值
>>> pAb 0.5 >>> p0V array([0.03846154, 0.07692308, 0.03846154, 0.07692308, 0.07692308, 0.07692308, 0.07692308, 0.03846154, 0.03846154, 0.03846154, 0.07692308, 0.07692308, 0.15384615, 0.07692308, 0.07692308, 0.07692308, 0.03846154, 0.07692308, 0.07692308, 0.07692308, 0.07692308, 0.07692308, 0.03846154, 0.07692308, 0.11538462, 0.07692308, 0.07692308, 0.03846154, 0.03846154, 0.03846154, 0.07692308, 0.03846154]) >>> p1V array([0.0952381 , 0.04761905, 0.0952381 , 0.0952381 , 0.14285714, 0.04761905, 0.04761905, 0.0952381 , 0.0952381 , 0.14285714, 0.04761905, 0.04761905, 0.04761905, 0.04761905, 0.04761905, 0.04761905, 0.0952381 , 0.04761905, 0.04761905, 0.04761905, 0.0952381 , 0.04761905, 0.0952381 , 0.04761905, 0.0952381 , 0.04761905, 0.04761905, 0.19047619, 0.0952381 , 0.0952381 , 0.04761905, 0.0952381 ])
我們發(fā)現(xiàn)文檔屬于侮辱類的概率pAb為 0.5,查看pV1的最大值 0.19047619,它出現(xiàn)在第 27 個下標位置,查看myVocabList的第 27 個下標位置該詞為 stupid,說明這是最能表征類別 1 的單詞。
測試算法:根據(jù)現(xiàn)實情況修改分類器 程序清單 4-3 樸素貝葉斯分類函數(shù)""" Created on Sep 11, 2018 @author: yufei """ # vec2Classify: 要分類的向量 def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): p1 = sum(vec2Classify * p1Vec) + log(pClass1) p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1) if p1 > p0: return 1 else: return 0 def testingNB(): list0Posts, listClasses = loadDataSet() myVocabList = createVocabList(list0Posts) trainMat = [] for posinDoc in list0Posts: trainMat.append(setOfWords2Vec(myVocabList, posinDoc)) 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))
在 python 提示符下,執(zhí)行代碼并得到結(jié)果:
>>> importlib.reload(bayes)>>> bayes.testingNB() ["love", "my", "dalmation"] classified as: 0 ["stupid", "garbage"] classified as: 1
分類器輸出結(jié)果,分類正確。
準備數(shù)據(jù):文檔詞袋模型詞集模型:將每個詞的出現(xiàn)與否作為一個特征。即我們上面所用到的。
詞袋模型:將每個詞出現(xiàn)次數(shù)作為一個特征。每遇到一個單詞,其詞向量對應(yīng)值 +1,而不是全設(shè)置為 1。
對函數(shù)setOfWords2Vec()進行修改,修改后的函數(shù)為bagOfWords2VecMN。
程序清單 4-4 樸素貝葉斯詞袋模型def bagOfWords2VecMN(vocabList, inputSet): returnVec = [0] * len(vocabList) for word in inputSet: if word in inputSet: returnVec[vocabList.index(word)] += 1 return returnVec
修改的地方為:每當(dāng)遇到一個單詞時,它會增加詞向量中的對應(yīng)值,而不只是將對應(yīng)的數(shù)值設(shè)為 1。
下面我們將利用該分類器來過濾垃圾郵件。
""" Created on Sep 11, 2018 @author: yufei """ """ 接受一個大字符串并將其解析為字符串列表 """ def textParse(bigString): #input is big string, #output is word list import re listOfTokens = re.split(r"W*", bigString) # 去掉小于兩個字符的字符串,并將所有字符串轉(zhuǎn)換為小寫 return [tok.lower() for tok in listOfTokens if len(tok) > 2] """ 對貝葉斯垃圾郵件分類器進行自動化處理 """ def spamTest(): docList=[]; classList = []; fullText =[] #導(dǎo)入并解析文本文件為詞列表 for i in range(1,26): wordList = textParse(open("email/spam/%d.txt" % i, encoding="ISO-8859-1").read()) docList.append(wordList) fullText.extend(wordList) classList.append(1) wordList = textParse(open("email/ham/%d.txt" % i, encoding="ISO-8859-1").read()) docList.append(wordList) fullText.extend(wordList) classList.append(0) vocabList = createVocabList(docList)#create vocabulary 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 = [] # 遍歷訓(xùn)練集的所有文檔,對每封郵件基于詞匯表并使用 bagOfWords2VecMN 來構(gòu)建詞向量 for docIndex in trainingSet:#train the classifier (get probs) trainNB0 trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex])) trainClasses.append(classList[docIndex]) # 用上面得到的詞在 trainNB0 函數(shù)中計算分類所需的概率 p0V,p1V,pSpam = trainNB0(array(trainMat),array(trainClasses)) errorCount = 0 # 對測試集分類 for docIndex in testSet: #classify the remaining items wordVector = bagOfWords2VecMN(vocabList, docList[docIndex]) # 如果郵件分類錯誤,錯誤數(shù)加 1 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
在 python 提示符下,執(zhí)行代碼并得到結(jié)果:
>>> importlib.reload(bayes)>>> bayes.spamTest() 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
函數(shù)spamTest()會輸出在 10 封隨機選擇的電子郵件上的分類錯誤率。由于是隨機選擇的,所以每次的輸出結(jié)果可能有些差別。如果想要更好地估計錯誤率,那么就應(yīng)該將上述過程重復(fù)多次求平均值。
這里的代碼需要注意的兩個地方是:1、直接使用語句 wordList = textParse(open("email/spam/%d.txt" % i).read()) 報錯 UnicodeDecodeError: "utf-8" codec can"t decode byte 0x92 in position 884: invalid start byte。這是因為在文件里可能存在不是以 utf-8 格式保存的字符,需改為wordList = textParse(open("email/spam/%d.txt" % i, encoding="ISO-8859-1").read())。
2、將隨機選出的文檔添加到測試集后,要同時將其從訓(xùn)練集中刪除,使用語句 del(trainingSet[randIndex]),此時會報錯 TypeError: "range" object doesn"t support item deletion,這是由于 python2 和 python3 的不同而導(dǎo)致的。在 python2 中可以直接執(zhí)行,而在 python3 中需將 trainingSet 設(shè)為 trainingSet = list(range(50)),而不是 trainingSet = range(50),即必須讓它是一個 list 再進行刪除操作。
以上,我們就用樸素貝葉斯對文檔進行了分類。
參考鏈接:
《機器學(xué)習(xí)實戰(zhàn)》筆記之四——基于概率論的分類方法:樸素貝葉斯
UnicodeDecodeError: "utf-8" codec can"t decode byte 0x92 in position 884: invalid start byte
不足之處,歡迎指正。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/42429.html
摘要:本篇內(nèi)容為機器學(xué)習(xí)實戰(zhàn)第章支持向量機部分程序清單。支持向量機優(yōu)點泛化錯誤率低,計算開銷不大,結(jié)果易解釋。注以上給出的僅是簡化版算法的實現(xiàn),關(guān)于完整的算法加速優(yōu)化并應(yīng)用核函數(shù),請參照機器學(xué)習(xí)實戰(zhàn)第頁。 本篇內(nèi)容為《機器學(xué)習(xí)實戰(zhàn)》第 6 章 支持向量機部分程序清單。所用代碼為 python3。 支持向量機優(yōu)點:泛化錯誤率低,計算開銷不大,結(jié)果易解釋。 缺點:對參數(shù)調(diào)節(jié)和核函數(shù)的選擇敏感,...
閱讀 3978·2021-11-16 11:44
閱讀 5231·2021-10-09 09:54
閱讀 2039·2019-08-30 15:44
閱讀 1691·2019-08-29 17:22
閱讀 2763·2019-08-29 14:11
閱讀 3401·2019-08-26 13:25
閱讀 2332·2019-08-26 11:55
閱讀 1602·2019-08-26 10:37