機器學習實戰讀書筆記(四):樸素貝葉斯演算法
阿新 • • 發佈:2018-12-21
樸素貝葉斯 優點: 在資料較少的情況下仍然有效 可以處理多類別問題 缺點:對輸入的資料的準備方式較為敏感 適用資料型別:標稱型資料 p1(x,y)>p2(x,y) 那麼類別是1 p2(x,y)>p1(x,y) 那麼類別是2 貝葉斯決策的核心是選擇具有最高概率的決策 樸素貝葉斯分類器通常有兩種方式 : 伯努利模型 和 多項式模型 這裡採用伯努利實現方式 該實現不考慮詞在文件中出現的次數 只考慮是否出現 某種意義上相當於假設詞是等權重的
from numpy import * # 詞表到向量的轉換函式 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代表正常言論 return postingList,classVec # 建立詞彙表 def createVocabList(dataSet): # 建立詞彙集合 空集 初始為空集 vocabSet = set([]) for document in dataSet: # 操作符| 用於取兩個集合的並集 # vocabSet|set(document) 兩個詞彙集合取並集 # 將每篇文章中的新詞加入到詞彙集 # set集合所有元素不重複 vocabSet = vocabSet|set(document) # 將詞彙集轉換為list return list(vocabSet) # 詞集模型 只考慮單詞是否出現 # vocabList:詞彙表 # inputSet:某個文件向量 def setofWords2Vec(vocabList,inputSet): # 建立一個其中所含元素都為0的向量 returnVec = [0]*len(vocabList) # 輸入集合中的詞彙如果出現在詞彙表中 定影向量位置設定為1 for word in inputSet: if word in vocabList: # vocabList.index(word) 找到word所在的索引值 # 因為vocabList是從set集合轉換過來所以每次只改變一個0 returnVec[vocabList.index(word)] = 1 else: print('The word: %s is not in my Vocabulary!'%word) return returnVec # 樸素貝葉斯詞袋模型 # 每個詞是否出現作為一個特徵 這可以作為詞集模型 伯努利模型 # 如果一個詞不僅出現一次 是這個詞是否出現不能表達的資訊 被稱為 詞袋模型 多項式模型 # 對setofWords2Vec()進行修改 def bagofWords2VecMN(vocabList, inputSet): returnVec = [0]*len(vocabList) for word in inputSet: if word in inputSet: # 該詞彙出現自動加一 returnVec[vocabList.index(word)] += 1 return returnVec # 樸素貝葉斯分類器訓練函式 # trainMatrix 詞向量資料集 # trainCategory 資料集對應的類別標籤 # p0Vect:詞彙表中各個單詞在正常言論中的類條件概率密度 # p1Vect:詞彙表中各個單詞在侮辱性言論中的類條件概率密度 # pAbusive:侮辱性言論在整個資料集中的比例 def trainNB0(trainMatrix,trainCategory): # 訓練集總條數 numTrainDocs = len(trainMatrix) # 訓練集中的所有不重複單詞總數 numWords = len(trainMatrix[0]) # 初始化概率 計算屬於侮辱性文件 class = 1 的概率,p(1) # p(0) = 1-p(1) 二分類 多於兩類需要修改 # 侮辱類佔總數的比例 pAbusive = sum(trainCategory)/float(numTrainDocs) # 正常言論的類條件概率密度p(某單詞|正常言論)=p0Num/p0Denom p0Num = zeros(numWords) # 侮辱性言論的類條件概率密度 p(某單詞|侮辱性言論)=p1Num/p1Denom p1Num = zeros(numWords) # 初始化分母置為0 p0Denom = 0.0 p1Denom = 0.0 for i in range(numTrainDocs): # 以下是向量相加 if trainCategory[i] == 1: # 如果該篇文章是侮辱性的 # 統計侮辱類所有文件中的各個單詞總數 p1Num += trainMatrix[i] # p1Denom侮辱類總單詞數 p1Denom += sum(trainMatrix[i]) else: p0Num += trainMatrix[i] p0Denom += sum(trainMatrix[i]) # 詞彙表中的單詞在侮辱性言論文件中的類條件概率 p1Vect = p1Num/p1Denom # 詞彙表中的單詞在正常性言論文件中的類條件概率 p0Vect = p0Num/p0Denom return p0Vect , p1Vect , pAbusive # 利用貝葉斯分類器對文件進行分類時,要計算多個概率的乘積以獲取某個文件屬於某個類別的概率 # 即計算p(w0|1)*p(w1|1)p(w2|1) # 若其中有一個概率值為0,最後乘積為零 為降低這種影響 # 可以把所有詞的出現初始化為1,並將分母初始化為2 # 大部分相乘因子很小,所以程式會下溢位,四捨五入得到0,所以對乘積取自然對數 # ln(a*b) = ln(a)+ln(b) 避免下溢位或者浮點數舍入導致錯誤 同時採用自然對數不會有任何損失 # 修改trainNB0 def trainNB1(trainMatrix, trainCategory): numTrainDocs = len(trainMatrix) numWords = len(trainMatrix[0]) pAbusive = sum(trainCategory) / float(numTrainDocs) # 修改初試值 p0Num = ones(numWords) p1Num = ones(numWords) p0Denom = 2.0 p1Denom = 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 = log(p1Num / p1Denom) p0Vect = log(p0Num / p0Denom) return p0Vect, p1Vect, pAbusive # 樸素貝葉斯分類函式 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(): # 獲得訓練資料,類別標籤 listOPosts, listClasses = loadDataSet() # 建立詞彙表 myVocabList = createVocabList(listOPosts) # 構建矩陣,存放訓練資料 trainMat = [] # 遍歷原始資料,轉換為詞向量,構成資料訓練矩陣 for postinDoc in listOPosts: # 資料轉換後存入資料訓練矩陣trainMat中 trainMat.append(setofWords2Vec(myVocabList, postinDoc)) # 訓練分類器 p0V, p1V, pAb = trainNB1(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)) listOPosts , listClasses = loadDataSet() myVocabList = createVocabList(listOPosts) # 檔案解析及完整的垃圾郵件測試函式 # 1 # print(myVocabList) # print(setofWords2Vec(myVocabList,listOPosts[0])) # print(setofWords2Vec(myVocabList,listOPosts[3])) # 2 # trainMat = [] # for postinDoc in listOPosts: # trainMat.append(setofWords2Vec(myVocabList, postinDoc)) # p0V, p1V, pAb = trainNBO(trainMat, listClasses) # print(pAb) # print(p0V) # print(p1V) # 3 testingNB() # 4 # 使用樸素貝葉斯對電子郵件進行分類 # 準備資料 切分文字 mySent = 'This book is the best book on python or M.L. I have ever laid eyes upon.' import re regEx = re.compile('\W') listOgTokens = regEx.split(mySent) # 去除空字串 # list1 = [tok for tok in listOgTokens if len(tok) > 0] # 去除空字元串同時全部轉化為小寫 # tok.upper() # tok.lower() # list2 = [tok.lower() for tok in listOgTokens if len(tok) > 0] # print(listOgTokens) # print(list1) # print(list2) # emailText = open('email/ham/6.txt').read() # listOgTokens = regEx.split(emailText) # listText = [tok.lower() for tok in listOgTokens if len(tok) > 2] # print(listText) # 測試演算法 使用樸素貝葉斯進行交叉驗證 # 留存交叉驗證: 隨機選擇資料的一部分作為訓練集,剩餘部分作為測試集的過程 # 檔案解析及完整的垃圾函式測試函式 def textParse(bigString): import re listOgTokens = re.split(r'\W',bigString) return [tok.lower() for tok in listOgTokens if len(tok) >2] def spamTest(): # 詞彙列表 1*n列 1維陣列 docList = [] # 結果列表 1*n列 1維陣列 0:正常 1:垃圾郵件 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) # 為每封郵件編號 trainingSet = list(range(50)) testSet = [] # 50個郵件隨機分成2組,一組10個,一組40個 用於測試和訓練 for i in range(10): # 隨機選取10個下標放入到testSet內 # 從訓練下標集合中刪除 randIndex = int(random.uniform(0,len(trainingSet))) testSet.append(trainingSet[randIndex]) del(trainingSet[randIndex]) # 根據trainingSet組成trainMat矩陣 為2維 trainMat = [] # 訓練郵件結果 trainClasses = [] for docIndex in trainingSet: trainMat.append(setofWords2Vec(vocabList, docList[docIndex])) trainClasses.append(classList[docIndex]) p0V, p1V, pSpam = trainNB0(array(trainMat),array(trainClasses)) errorCount = 0 for docIndex in testSet: wordVector = setofWords2Vec(vocabList,docList[docIndex]) if classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]: errorCount += 1 print('the error rate is: ',float(errorCount)/len(testSet)) spamTest()