摘要:本篇內(nèi)容為機(jī)器學(xué)習(xí)實(shí)戰(zhàn)第章基于概率論的分類方法樸素貝葉斯程序清單。樸素貝葉斯優(yōu)點(diǎn)在數(shù)據(jù)較少的情況下仍然有效,可以處理多類別問題。參考鏈接機(jī)器學(xué)習(xí)實(shí)戰(zhàn)筆記之四基于概率論的分類方法樸素貝葉斯不足之處,歡迎指正。
本篇內(nèi)容為《機(jī)器學(xué)習(xí)實(shí)戰(zhàn)》第 4 章 基于概率論的分類方法:樸素貝葉斯程序清單。所用代碼為 python3。
樸素貝葉斯優(yōu)點(diǎn):在數(shù)據(jù)較少的情況下仍然有效,可以處理多類別問題。
缺點(diǎn):對于輸入數(shù)據(jù)的準(zhǔn)備方式較為敏感。
適用數(shù)據(jù)類型:標(biāo)稱型數(shù)據(jù)。
簡單描述這個(gè)過程為:從文本中獲取特征,構(gòu)建分類器,進(jìn)行分類輸出結(jié)果。這里的特征是來自文本的詞條 (token),需要將每一個(gè)文本片段表示為一個(gè)詞條向量,其中值為 1 表示詞條出現(xiàn)在文檔中,0 表示詞條未出現(xiàn)。
接下來給出將文本轉(zhuǎn)換為數(shù)字向量的過程,然后基于這些向量來計(jì)算條件概率,并在此基礎(chǔ)上構(gòu)建分類器。
下面我們以在線社區(qū)的留言板為例,給出一個(gè)用來過濾的例子。準(zhǔn)備數(shù)據(jù):從文本中構(gòu)建詞向量 程序清單 4-1 詞表到向量的轉(zhuǎn)換函數(shù)
為了不影響社區(qū)的發(fā)展,我們需要屏蔽侮辱性的言論,所以要構(gòu)建一個(gè)快速過濾器,如果某條留言使用來負(fù)面或者侮辱性的語言,就將該留言標(biāo)識為內(nèi)容不當(dāng)。對此問題建立兩個(gè)類別:侮辱類和非侮辱類,分別使用 1 和 0 來表示。
""" Created on Sep 10, 2018 @author: yufei """ # coding=utf-8 from numpy import * # 創(chuàng)建一些實(shí)例樣本 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 返回的是進(jìn)行詞條切分后的文檔集合。 留言文本被切分成一些列詞條集合,標(biāo)點(diǎn)符號從文本中去掉 變量 classVec 返回一個(gè)類別標(biāo)簽的集合。 這些文本的類別由人工標(biāo)注,標(biāo)注信息用于訓(xùn)練程序以便自動(dòng)檢測侮辱性留言。 """ return postingList, classVec """ 創(chuàng)建一個(gè)包含在所有文檔中出現(xiàn)的不重復(fù)詞的列表 是用python的 Set 數(shù)據(jù)類型 將詞條列表輸給 Set 構(gòu)造函數(shù),set 就會返回一個(gè)不重復(fù)詞表 """ def createVocabList(dataSet): # 創(chuàng)建一個(gè)空集合 vocabSet = set([]) # 將每篇文檔返回的新詞集合添加進(jìn)去,即創(chuàng)建兩個(gè)集合的并集 for document in dataSet: vocabSet = vocabSet | set(document) # 獲得詞匯表 return list(vocabSet) # 參數(shù):詞匯表,某個(gè)文檔 def setOfWords2Vec(vocabList, inputSet): # 創(chuàng)建一個(gè)和詞匯表等長的向量,將其元素都設(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"]
即可得到的一個(gè)不會出現(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è)單詞構(gòu)建一個(gè)特征。一旦給定一篇文章(本例中指一條留言),該文檔就會被轉(zhuǎn)換為詞向量。
訓(xùn)練算法:從詞向量計(jì)算概率函數(shù)偽代碼如下:
··· 計(jì)算每個(gè)類別中的文檔數(shù)目 ··· 對每篇訓(xùn)練文檔: ······ 對每個(gè)類別: ········· 如果詞條出現(xiàn)在文檔中—>增加該詞條的計(jì)數(shù)值 ········· 增加所有詞條的計(jì)數(shù)值 ······ 對每個(gè)類別: ········· 對每個(gè)詞條: ············ 將該詞條對數(shù)目除以總詞條數(shù)目得到條件概率 ······ 返回每個(gè)類別對條件概率程序清單 4-2 樸素貝葉斯分類器訓(xùn)練函數(shù)
""" Created on Sep 11, 2018 @author: yufei """ # 參數(shù):文檔矩陣 trainMatrix,每篇文檔的類別標(biāo)簽所構(gòu)成的向量 trainCategory def trainNB0(trainMatrix, trainCategory): numTrainDocs = len(trainMatrix) #文檔的個(gè)數(shù) numWords = len(trainMatrix[0]) #獲取第一篇文檔的單詞長度 """ 計(jì)算文檔屬于侮辱性文檔的概率 用類別為1的個(gè)數(shù)除以總篇數(shù) sum([0,1,0,1,0,1])=3,也即是 trainCategory 里面 1 的個(gè)數(shù) """ pAbusive = sum(trainCategory) / float(numTrainDocs) """ 初始化概率 當(dāng)利用貝葉斯分類器對文檔分類時(shí),計(jì)算多個(gè)概率的乘積以獲得屬于某個(gè)類別的概率 把所有詞出現(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): # 侮辱性詞語在某個(gè)文檔中出現(xiàn) if trainCategory[i] == 1: # 該詞對應(yīng)個(gè)數(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,再把所有這個(gè)類別的向量都這樣累加起來,這個(gè)是計(jì)算單詞總數(shù)目 p1Denom += sum(trainMatrix[i]) # 正常詞語在某個(gè)文檔中出現(xiàn),同上 else: p0Num += trainMatrix[i] p0Denom +=sum(trainMatrix[i]) """ 對每個(gè)元素除以該類別的總詞數(shù),得條件概率 防止太多的很小的數(shù)相乘造成下溢。對乘積取對數(shù) # p1Vect = log(p1Num / p1Denom) # p0Vect = log(p0Num / p0Denom) """ p1Vect = p1Num / p1Denom p0Vect = p0Num / p0Denom """ 函數(shù)返回兩個(gè)向量和一個(gè)概率 返回每個(gè)類別的條件概率,是一個(gè)向量 在向量里面和詞匯表向量長度相同 每個(gè)位置代表這個(gè)單詞在這個(gè)類別中的概率 """ 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)建了一個(gè)包含所有詞的列表myVocabList
>>> trainMat = [] >>> for postinDoc in list0Posts: ... trainMat.append(bayes.setOfWords2Vec(myVocabList, postinDoc))
這個(gè)for循環(huán)使用詞向量來填充trainMat列表。
繼續(xù)給出屬于侮辱性文檔的概率以及兩個(gè)類別的概率向量。
>>> 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 個(gè)下標(biāo)位置,查看myVocabList的第 27 個(gè)下標(biāo)位置該詞為 stupid,說明這是最能表征類別 1 的單詞。
測試算法:根據(jù)現(xiàn)實(shí)情況修改分類器 程序清單 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é)果,分類正確。
準(zhǔn)備數(shù)據(jù):文檔詞袋模型詞集模型:將每個(gè)詞的出現(xiàn)與否作為一個(gè)特征。即我們上面所用到的。
詞袋模型:將每個(gè)詞出現(xiàn)次數(shù)作為一個(gè)特征。每遇到一個(gè)單詞,其詞向量對應(yīng)值 +1,而不是全設(shè)置為 1。
對函數(shù)setOfWords2Vec()進(jìn)行修改,修改后的函數(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)遇到一個(gè)單詞時(shí),它會增加詞向量中的對應(yīng)值,而不只是將對應(yīng)的數(shù)值設(shè)為 1。
下面我們將利用該分類器來過濾垃圾郵件。
""" Created on Sep 11, 2018 @author: yufei """ """ 接受一個(gè)大字符串并將其解析為字符串列表 """ def textParse(bigString): #input is big string, #output is word list import re listOfTokens = re.split(r"W*", bigString) # 去掉小于兩個(gè)字符的字符串,并將所有字符串轉(zhuǎn)換為小寫 return [tok.lower() for tok in listOfTokens if len(tok) > 2] """ 對貝葉斯垃圾郵件分類器進(jìn)行自動(dòng)化處理 """ 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ù)中計(jì)算分類所需的概率 p0V,p1V,pSpam = trainNB0(array(trainMat),array(trainClasses)) errorCount = 0 # 對測試集分類 for docIndex in testSet: #classify the remaining items wordVector = bagOfWords2VecMN(vocabList, docList[docIndex]) # 如果郵件分類錯(cuò)誤,錯(cuò)誤數(shù)加 1 if classifyNB(array(wordVector),p0V,p1V,pSpam) != classList[docIndex]: errorCount += 1 print ("classification error",docList[docIndex]) # 給出總的錯(cuò)誤百分比 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 封隨機(jī)選擇的電子郵件上的分類錯(cuò)誤率。由于是隨機(jī)選擇的,所以每次的輸出結(jié)果可能有些差別。如果想要更好地估計(jì)錯(cuò)誤率,那么就應(yīng)該將上述過程重復(fù)多次求平均值。
這里的代碼需要注意的兩個(gè)地方是:1、直接使用語句 wordList = textParse(open("email/spam/%d.txt" % i).read()) 報(bào)錯(cuò) UnicodeDecodeError: "utf-8" codec can"t decode byte 0x92 in position 884: invalid start byte。這是因?yàn)樵谖募锟赡艽嬖诓皇且?utf-8 格式保存的字符,需改為wordList = textParse(open("email/spam/%d.txt" % i, encoding="ISO-8859-1").read())。
2、將隨機(jī)選出的文檔添加到測試集后,要同時(shí)將其從訓(xùn)練集中刪除,使用語句 del(trainingSet[randIndex]),此時(shí)會報(bào)錯(cuò) TypeError: "range" object doesn"t support item deletion,這是由于 python2 和 python3 的不同而導(dǎo)致的。在 python2 中可以直接執(zhí)行,而在 python3 中需將 trainingSet 設(shè)為 trainingSet = list(range(50)),而不是 trainingSet = range(50),即必須讓它是一個(gè) list 再進(jìn)行刪除操作。
以上,我們就用樸素貝葉斯對文檔進(jìn)行了分類。
參考鏈接:
《機(jī)器學(xué)習(xí)實(shí)戰(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://specialneedsforspecialkids.com/yun/42429.html
摘要:本篇內(nèi)容為機(jī)器學(xué)習(xí)實(shí)戰(zhàn)第章支持向量機(jī)部分程序清單。支持向量機(jī)優(yōu)點(diǎn)泛化錯(cuò)誤率低,計(jì)算開銷不大,結(jié)果易解釋。注以上給出的僅是簡化版算法的實(shí)現(xiàn),關(guān)于完整的算法加速優(yōu)化并應(yīng)用核函數(shù),請參照機(jī)器學(xué)習(xí)實(shí)戰(zhàn)第頁。 本篇內(nèi)容為《機(jī)器學(xué)習(xí)實(shí)戰(zhàn)》第 6 章 支持向量機(jī)部分程序清單。所用代碼為 python3。 支持向量機(jī)優(yōu)點(diǎn):泛化錯(cuò)誤率低,計(jì)算開銷不大,結(jié)果易解釋。 缺點(diǎn):對參數(shù)調(diào)節(jié)和核函數(shù)的選擇敏感,...
閱讀 3973·2021-11-16 11:44
閱讀 5209·2021-10-09 09:54
閱讀 2034·2019-08-30 15:44
閱讀 1683·2019-08-29 17:22
閱讀 2756·2019-08-29 14:11
閱讀 3393·2019-08-26 13:25
閱讀 2328·2019-08-26 11:55
閱讀 1600·2019-08-26 10:37