本文系做者原創,轉載請註明出處:http://www.javashuo.com/article/p-quecffxb-g.html html
樸素貝葉斯是經典的機器學習算法之一,也是爲數很少的基於機率論的分類算法。樸素貝葉斯原理簡單,也很容易實現,多用於文本分類,好比垃圾郵件過濾。python
該算法的優勢在於簡單易懂、學習效率高、在某些領域的分類問題中可以與決策樹、神經網絡相媲美。算法
但因爲該算法以自變量之間的獨立(條件特徵獨立)性和連續變量的正態性假設爲前提,就會致使算法精度在某種程度上受影響。python3.x
marco 博客https://www.cnblogs.com/marc01in/p/4775440.html的簡單例子可以很形象說明其核心思想:數組
如今出現一個新的點new_point (x,y),其分類未知。咱們能夠用p1(x,y)表示數據點(x,y)屬於紅色一類的機率,同時也能夠用p2(x,y)表示數據點(x,y)屬於藍色一類的機率。那要把new_point歸在紅、藍哪一類呢?網絡
咱們提出這樣的規則:app
若是p1(x,y) > p2(x,y),則(x,y)爲紅色一類。less
若是p1(x,y) <p2(x,y), 則(x,y)爲藍色一類。dom
換人類的語言來描述這一規則:選擇機率高的一類做爲新點的分類。這就是貝葉斯決策理論的核心思想,即選擇具備最高几率的決策。機器學習
用條件機率的方式定義這一貝葉斯分類準則:
若是p(red|x,y) > p(blue|x,y), 則(x,y)屬於紅色一類。
若是p(red|x,y) < p(blue|x,y), 則(x,y)屬於藍色一類。
也就是說,在出現一個須要分類的新點時,咱們只須要計算這個點的
max(p(c1 | x,y),p(c2 | x,y),p(c3 | x,y)...p(cn| x,y))。其對於的最大機率標籤,就是這個新點的分類啦。
那麼問題來了,對於分類i 如何求解p(ci| x,y)?
沒錯,就是貝葉斯公式:
若是要肯定某個樣本歸屬於哪一類,則須要計算出歸屬不一樣類的機率,再從中挑選出最大的機率。
咱們把上面的貝葉斯公式寫出這樣,也許你能更好的理解:
而這個公式告訴咱們,須要計算最大的後驗機率,只須要計算出分子的最大值便可,而不一樣水平的機率P(C)很是容易得到,故難點就在於P(X|C)的機率計算。而問題的解決,正是聰明之處,即貝葉斯假設變量X間是條件獨立的,故而P(X|C)的機率就能夠計算爲:
1. 收集數據:提供數據源(通常訓練數據與測試數據比例爲7:3);
2. 準備數據:將數據源解析成詞條向量;
3. 分析數據:檢查詞條確保解析的正確性;
4. 訓練算法:用訓練數據生成的分類器;
5. 測試算法:用訓練生成的分類器預測測試數據的結果,對比真實結果,計算錯誤率,衡量分類器的準確度。
6. 使用算法:經過錯誤率來評估分類器;
myBayes.py:代碼實現文件,代碼已作了詳細註釋,包含3個示例:
1. 過濾侮辱文檔
2. 過濾垃圾郵件
3. 尋找在線RSS源排名靠前的單詞
1 # -*- coding: utf-8 -*- 2 """ 3 Created on Mon Nov 5 14:02:18 2018 4 5 @author: weixw 6 """ 7 8 import numpy as np 9 10 def loadDataSet(): 11 postingList=[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'], 12 ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'], 13 ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'], 14 ['stop', 'posting', 'stupid', 'worthless', 'garbage'], 15 ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'], 16 ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']] 17 classVec = [0,1,0,1,0,1] #1 侮辱性, 0 not 18 return postingList,classVec 19 20 #輸入:原始數據集 21 #功能:建立一個包含全部原始數據集詞語集合,這個列表中詞語不重複。 22 #輸出:不重複詞列表 23 def createVocabList(dataSet): 24 #建立一個空集合,這個集合中內容惟一,而且可動態擴展。 25 vocabSet = set([]) #create empty set 26 #獲取每一行數據list 27 for document in dataSet: 28 #將每一行數據取出,合併到vocabSet集合中,vocabSet會自動過濾重複詞 29 vocabSet = vocabSet | set(document) #union of the two sets 30 #將集合轉化爲列表list 31 return list(vocabSet) 32 33 #輸入:列表集合(惟一),輸入集合(一行文檔內容) 34 ''' 35 功能:檢查輸入集合單詞是否在列表集合中,在則在列表集合對應位置設置爲1,不然爲0 36 將每一個詞在文檔中出現與否做爲一個特徵,稱爲詞集模型(set-of-words model) 37 ''' 38 #輸出:向量列表(判斷輸入文檔中每一個單詞是否在詞彙樣本中) 39 def setOfWords2Vec(vocabList, inputSet): 40 #初始化爲0,長度爲vocabList長度的集合 41 returnVec = [0]*len(vocabList) 42 #檢查inputSet中每一個單詞是否在vocabList集合中,在則在vocabList對應位置置1,不然不做處理 43 for word in inputSet: 44 if word in vocabList: 45 returnVec[vocabList.index(word)] = 1 46 else: print ("the word: %s is not in my Vocabulary!" % word) 47 return returnVec 48 49 #輸入:trainMatrix(文檔數組(處理爲0,1向量)),trainCategory(列標籤數組) 50 ''' 51 功能:生成分類器(獲取最佳訓練參數權值) 52 訓練樣本以及測試樣本大小要相同,經過標籤指定行詞語類別來計算訓練樣本對應位置權重參數值。 53 這裏有個假定前提,每一行詞語是相互獨立的,也就有 54 p(w/c1) = p(w0,w1,w2...wn/c1) = p(w0/c1)*p(w1/c1)*p(w2/c1)***p(wn/c1) 55 ''' 56 #輸出:p0Vect(文檔中非侮辱性詞語機率),p1Vect(侮辱性詞語機率),pAbusive(標籤類中侮辱性標籤機率) 57 def trainNB0(trainMatrix,trainCategory): 58 #數據集行大小 59 numTrainDocs = len(trainMatrix) 60 #數據集列大小 61 numWords = len(trainMatrix[0]) 62 #標籤類中侮辱性標籤機率(0:非侮辱,1:侮辱) 63 #引入float強制轉換是使結果爲小數 64 pAbusive = sum(trainCategory)/float(numTrainDocs) 65 #初始化爲數據集列大小的單位矩陣 66 #p0Num:非侮辱性矩陣,p1Num:侮辱性矩陣 67 p0Num = np.ones(numWords); p1Num = np.ones(numWords) #change to ones() 68 #使np.log > 1,防止np.log分母爲0而沒法計算 69 70 #防止分母爲0。由於計算每一個子項機率採用的對數log(防止下溢出),是以2爲底的,若是pADDenom = 2.0,則避免了分母爲0的可能。 71 p0Denom = 2.0; p1Denom = 2.0 #change to 2.0 72 #遍歷數據集每一行 73 for i in range(numTrainDocs): 74 #若是標籤類中該行定義爲侮辱性標籤 75 if trainCategory[i] == 1: 76 #將數據集中指定侮辱性行對應數據迭代求和,結果仍是矩陣 77 #統計數據集中指定侮辱性行存在的詞語(爲1),並求和,結果是數字 78 p1Num += trainMatrix[i] 79 p1Denom += sum(trainMatrix[i]) 80 #若是標籤類中該行定義爲非侮辱性標籤 81 else: 82 #將數據集中指定非侮辱性行對應數據迭代求和,結果仍是矩陣 83 p0Num += trainMatrix[i] 84 #統計數據集中指定非侮辱性行存在的詞語(爲1),並求和,結果是數字 85 p0Denom += sum(trainMatrix[i]) 86 #這裏有個假定前提,每一行詞語是相互獨立的,也就有 87 #p(w/c1) = p(w0,w1,w2...wn/c1) = p(w0/c1)*p(w1/c1)*p(w2/c1)***p(wn/c1) 88 #計算數據集中侮辱性詞語機率p(w/c1) 89 #取對數是防止下溢出,因爲機率因子都很是小,當計算乘積p(W0/Ci)*p(W1/Ci)*p(W2/Ci)...*p(Wn/Ci)時,獲得結果會更小, 90 #四捨五入會爲0,因此採用log(a*b) = log(a) + log(b) 91 p1Vect = np.log(p1Num/p1Denom) #change to log() 92 #計算數據集中非侮辱性詞語機率p(w/c0) 93 p0Vect = np.log(p0Num/p0Denom) #change to log() 94 95 return p0Vect,p1Vect,pAbusive 96 ''' 97 輸入:vec2Classify(測試文檔(0.1向量)),p0Vec(非侮辱性機率訓練參數), 98 p1Vec(侮辱性機率訓練參數),pClass1(標籤類中侮辱性標籤機率) 99 功能:比較侮辱性和非侮辱性後驗機率,判斷測試文檔所屬類別 100 樸素貝葉斯公式:p(Ci/W) = p(W/Ci)p(Ci)/p(W) 101 輸出:1:侮辱性文檔 0:非侮辱性文檔 102 ''' 103 def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): 104 #計算文檔屬於侮辱性標籤機率p(c1/w) 105 #np.log是以2爲底,求和實際是相乘 106 p1 = sum(vec2Classify * p1Vec) + np.log(pClass1) #element-wise mult 107 #計算文檔屬於非侮辱性標籤機率p(c0/w) 108 p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1) 109 if p1 > p0: 110 #u"侮辱文檔" 111 return 1 112 else: 113 #u"非侮辱文檔" 114 return 0 115 116 #輸入:列表集合(惟一),輸入集合(文檔或詞彙表) 117 ''' 118 功能:檢查輸入集合單詞是否在列表集合中,在則在列表集合對應位置加1 119 若是一個詞在文檔中出現不止一次,這可能意味着包含該詞是否出如今文檔中所不能表達的某種信息, 120 稱爲詞袋模型(bags-of-words model)。在詞袋中,每一個單子能夠出現屢次,而在詞集中,每一個詞只能出現一次。 121 ''' 122 #輸出:向量列表(統計輸入文檔中每一個單詞在詞彙樣本中個數) 123 def bagOfWords2VecMN(vocabList, inputSet): 124 returnVec = [0]*len(vocabList) 125 for word in inputSet: 126 if word in vocabList: 127 returnVec[vocabList.index(word)] += 1 128 return returnVec 129 130 #測試(過濾網站惡意留言) 131 def testingNB(): 132 #獲取數據集(數組),標籤列表 133 listOPosts,listClasses = loadDataSet() 134 #將數據集數組轉化爲內容惟一的數據集list 135 #建立一個包含全部原始數據集詞語集合,這個列表中詞語不重複。 136 myVocabList = createVocabList(listOPosts) 137 trainMat=[] 138 #遍歷數據集中每一行數組,將詞語轉化爲向量0,1(1:該行數據中詞語在myVocabList存在) 139 #詞語文檔 => 向量文檔 140 for postinDoc in listOPosts: 141 trainMat.append(bagOfWords2VecMN(myVocabList, postinDoc)) 142 #計算分類器訓練參數 143 p0V,p1V,pAb = trainNB0(np.array(trainMat),np.array(listClasses)) 144 #測試1 145 testEntry = ['love', 'my', 'dalmation'] 146 #詞語文檔 => 向量文檔 147 thisDoc = np.array(bagOfWords2VecMN(myVocabList, testEntry)) 148 #結果分類 149 print (testEntry,'classified as: ', u"0 (非侮辱文檔)" if(classifyNB(thisDoc,p0V,p1V,pAb) == 0) else u"1(侮辱文檔)") 150 #測試2 151 testEntry = ['stupid', 'garbage'] 152 #詞語文檔 => 向量文檔 153 thisDoc = np.array(bagOfWords2VecMN(myVocabList, testEntry)) 154 #結果分類 155 print (testEntry,'classified as: ', u"0 (非侮辱文檔)" if(classifyNB(thisDoc,p0V,p1V,pAb) == 0) else u"1(侮辱文檔)") 156 157 158 #輸入:文本字符串 159 ''' 160 功能:文本處理,過濾掉一些不須要的字符(?,&,=...)以及URL中en和py這樣的單詞, 161 並所有轉換爲小寫 162 ''' 163 #輸出:處理後的字符串集合 164 def textParse(bigString): #input is big string, #output is word list 165 import re 166 #分隔符是除單詞、數字外的任意字符都是分隔符 167 #string.split()只是以" "分隔 168 #正則匹配 169 listOfTokens = re.split(r'\W*', bigString) 170 #過濾掉少於2個字符的字符串,並所有轉換爲小寫 171 return [tok.lower() for tok in listOfTokens if len(tok) > 2] 172 173 #測試(過濾垃圾郵件) 174 #說明:50封郵件中隨機選取10封做爲測試樣本,剩下40封做爲訓練樣本 175 def spamTest(): 176 docList=[]; classList = []; fullText =[] 177 #email文件下26封正常郵件,26封垃圾郵件 178 for i in range(1,26): 179 #準備數據,文本預處理 180 wordList = textParse(open('email/spam/%d.txt' % i).read()) 181 #保存爲list(list遞增 二維) 182 docList.append(wordList) 183 #保存爲array(擴展 一維) 184 fullText.extend(wordList) 185 #標記郵件類別,垃圾郵件 186 classList.append(1) 187 #準備數據,文本預處理 188 wordList = textParse(open('email/ham/%d.txt' % i).read()) 189 #保存爲list(list遞增 二維) 190 docList.append(wordList) 191 #保存爲array(擴展 一維) 192 fullText.extend(wordList) 193 #標記郵件類別,正常郵件 194 classList.append(0) 195 #將數據集數組轉化爲內容惟一的數據集list 196 #建立一個包含全部原始數據集詞語集合,這個列表中詞語不重複。 197 vocabList = createVocabList(docList)#create vocabulary 198 #python3.x range返回的是range對象,不返回數組對象 199 trainingSet = list(range(50)); testSet=[] #create test set 200 #隨機選取10封郵件做爲測試數據 201 for i in range(10): 202 #在50封郵件中隨機抽選 203 randIndex = int(np.random.uniform(0,len(trainingSet))) 204 #保存list中 205 testSet.append(trainingSet[randIndex]) 206 #選取並保存後,去除該郵件對應的索引 207 del(trainingSet[randIndex]) 208 trainMat=[]; trainClasses = [] 209 #剩餘40封郵件做爲訓練樣本,訓練分類器參數 210 for docIndex in trainingSet:#train the classifier (get probs) trainNB0 211 #將處理後的文本轉化爲詞向量(詞袋模型),並保存 212 trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex])) 213 #保存對應標籤 214 trainClasses.append(classList[docIndex]) 215 #訓練,獲取最終訓練參數 216 p0V,p1V,pSpam = trainNB0(np.array(trainMat),np.array(trainClasses)) 217 218 errRate = 0.0;iterNumber = 20 219 #迭代20次 220 for i in range(iterNumber): 221 errorCount = 0 222 #遍歷測試數據集 223 for docIndex in testSet: #classify the remaining items 224 #將處理後的測試數據轉化爲詞向量(詞袋模型),並保存 225 wordVector = bagOfWords2VecMN(vocabList, docList[docIndex]) 226 #將訓練後的參數導入分類器,輸入測試的詞向量,與測試數據對應標籤比較,統計錯誤率 227 result = classifyNB(np.array(wordVector),p0V,p1V,pSpam) 228 if result != classList[docIndex]: 229 errorCount += 1 230 # print ("docIndex:%d,classification error:%s"%(docIndex, docList[docIndex])) 231 print ("docIndex:%d,classification error, predict result: %d mark class result %d:"%(docIndex, result, classList[docIndex])) 232 #每次錯誤率 233 print ('the error rate is: ',float(errorCount)/len(testSet)) 234 errRate += float(errorCount)/len(testSet) 235 #平均錯誤率 236 print ("the mean error rate is:", float(errRate)/iterNumber) 237 238 #return vocabList,fullText 239 240 241 #輸入:vocabList(去重詞彙表),fullText(未去重全部單詞) 242 ''' 243 功能:計算高頻詞彙。遍歷詞彙表中每一個詞並統計它在文本中出現的次數,而後根據出現次數從高到低對詞典進行排序, 244 最後返回排序最高的30個單詞。 245 ''' 246 #輸出:按降序排列的前30個單詞以及單詞對應出現次數 247 def calcMostFreq(vocabList,fullText): 248 import operator 249 freqDict = {} 250 #遍歷詞彙表中每一個單詞,統計每一個單詞出現的次數,而後以鍵值對保存 251 for token in vocabList: 252 freqDict[token]=fullText.count(token) 253 #以單詞出現次數倒敘排列 254 sortedFreq = sorted(freqDict.items(), key=operator.itemgetter(1), reverse=True) 255 #返回前30個鍵值對對象 256 return sortedFreq[:30] 257 258 #輸入:feed1(RSS1源),feed0(RSS0源) 259 ''' 260 功能: 261 1 根據樸素貝葉斯公式生成分類器; 262 2 判斷隨機抽選測試數據屬於源RSS0或者RSS1; 263 3 計算分類錯誤率; 264 ''' 265 #輸出:vocabList(詞彙表),p0V(源RSS0機率),p1V(源RSS1機率) 266 def localWords(feed1,feed0): 267 # import feedparser 268 docList=[]; classList = []; fullText =[] 269 #獲取源RSS0,RSS1最小行大小 270 minLen = min(len(feed1['entries']),len(feed0['entries'])) 271 for i in range(minLen): 272 #文本處理 273 wordList = textParse(feed1['entries'][i]['summary']) 274 #保存list(二維) 275 docList.append(wordList) 276 #保存array(一維) 277 fullText.extend(wordList) 278 #添加標籤 279 classList.append(1) #NY is class 1 280 #文本處理 281 wordList = textParse(feed0['entries'][i]['summary']) 282 #保存list(二維) 283 docList.append(wordList) 284 #保存array(一維) 285 fullText.extend(wordList) 286 #添加標籤 287 classList.append(0) 288 #生成詞彙表 289 vocabList = createVocabList(docList)#create vocabulary 290 #獲取詞彙表中單詞在文本中出現次數,並截取排名前30鍵值對 291 top30Words = calcMostFreq(vocabList,fullText) #remove top 30 words 292 #去除詞彙表中排名前30單詞 293 for pairW in top30Words: 294 if pairW[0] in vocabList: vocabList.remove(pairW[0]) 295 #RSS0+RSS1 296 trainingSet = list(range(2*minLen)); testSet=[] #create test set 297 #隨機選取20做爲測試數據,保存,而後在訓練集中去除 298 for i in range(20): 299 randIndex = int(np.random.uniform(0,len(trainingSet))) 300 testSet.append(trainingSet[randIndex]) 301 del(trainingSet[randIndex]) 302 trainMat=[]; trainClasses = [] 303 #除去20個測試數據,剩下做爲訓練數據 304 for docIndex in trainingSet:#train the classifier (get probs) trainNB0 305 trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex])) 306 trainClasses.append(classList[docIndex]) 307 #生成分類器,獲取訓練參數結果 308 p0V,p1V,pSpam = trainNB0(np.array(trainMat),np.array(trainClasses)) 309 errorCount = 0 310 #計算測試分類錯誤率 311 for docIndex in testSet: #classify the remaining items 312 wordVector = bagOfWords2VecMN(vocabList, docList[docIndex]) 313 if classifyNB(np.array(wordVector),p0V,p1V,pSpam) != classList[docIndex]: 314 errorCount += 1 315 print ('the error rate is: ',float(errorCount)/len(testSet)) 316 return vocabList,p0V,p1V 317 318 #輸入:ny(源RSS0),sf(源RSS1) 319 #功能:根據貝葉斯公式,經過訓練分類器得到的分類機率,找到排名靠前的單詞。 320 #輸出:RSS0和RSS1出現頻率排名靠前的單詞 321 def getTopWords(ny,sf): 322 # import operator 323 #獲取訓練樣本詞彙表,以及RSS0,RSS1的機率 324 vocabList,p0V,p1V=localWords(ny,sf) 325 topNY=[]; topSF=[] 326 #設置機率閾值,保存知足條件的鍵值對 327 for i in range(len(p0V)): 328 if p0V[i] > -6.0 : topSF.append((vocabList[i],p0V[i])) 329 if p1V[i] > -6.0 : topNY.append((vocabList[i],p1V[i])) 330 #RSS0最終結果排序 331 sortedSF = sorted(topSF, key=lambda pair: pair[1], reverse=True) 332 print (u"RSS0最終結果排序") 333 #打印RSS0頻率最高詞彙 334 for item in sortedSF: 335 print (item[0]) 336 #RSS1最終結果排序 337 sortedNY = sorted(topNY, key=lambda pair: pair[1], reverse=True) 338 print (u"RSS1最終結果排序") 339 #打印RSS1頻率最高詞彙 340 for item in sortedNY: 341 print (item[0])
測試文件:testMyBayes.py
1 # -*- coding: utf-8 -*- 2 """ 3 Created on Mon Nov 5 14:08:32 2018 4 5 @author: weixw 6 """ 7 8 import myBayes as mb; 9 import feedparser as fp 10 11 #過濾網站惡意留言 12 mb.testingNB(); 13 14 #過濾垃圾郵件 15 mb.spamTest() 16 17 18 ny = fp.parse('http://www.people.com.cn/rss/politics.xml') 19 length = len(ny['entries']) 20 21 sf = fp.parse('http://www.people.com.cn/rss/world.xml') 22 length = len(ny['entries']) 23 24 #vocabList,pSF,pNY = bs.localWords(ny,sf) 25 26 #找到排名靠前的單詞 27 mb.getTopWords(ny,sf);
1. 過濾侮辱文檔
由給定的標籤類能夠看出,預測分類結果是正確的。
2. 過濾垃圾郵件(40封做爲訓練樣本,10封做爲測試樣本),迭代次數:20
結果一:對垃圾郵件的過濾準確度平均只有80%,並且還會把正確郵件錯認爲垃圾郵件。
結果二:對垃圾郵件的過濾準確度100%。
出現兩種不一樣結果的緣由是:訓練樣本太少,致使準確度不穩定,而且產生了將正確郵件錯認爲垃圾郵件。
結果二
3. 尋找在線RSS源排名靠前的單詞
經過RSS源http://www.people.com.cn/rss/politics.xml,http://www.people.com.cn/rss/world.xml
來校驗。
《機器學習實戰》
博客:http://www.cnblogs.com/marc01in/p/4775440.html
不要讓懶惰佔據你的大腦,不要讓妥協拖垮了你的人生。青春就是一張票,能不能遇上時代的快車,你的步伐就掌握在你的腳下。