日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

朴素贝叶斯算法:实现邮件分类

發布時間:2023/12/20 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 朴素贝叶斯算法:实现邮件分类 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

樸素貝葉斯算法:實現郵件分類

  • 注:代碼和數據已上傳:https://download.csdn.net/download/j__max/10705454

一、實驗準備

1、實驗內容和目的

  • 使用5000封郵件作為訓練集,訓練樸素貝葉斯分類器,然后使用該分類器對1000封郵件進行分類,給出準確率結果

  • 其中訓練集為文件spam_train.txt,測試集為文件spam_test.txt。它們的存儲形式為:每封郵件以一個標簽(0或1)開頭,然后以一個換行符來標示一封郵件的結尾

  • 這里先給出我最后測試的準確率:98.3% ,接下來對我的實現過程進行詳細說明

2、實驗原理

  • 樸素貝葉斯算法是有監督的學習算法,可以用來解決分類問題。樸素貝葉斯基于 貝葉斯定理 和 特征條件獨立假設,它假設每個特征條件之間是獨立的, 比如一個單詞出現的概率和其它相鄰單詞沒有關系,因此我們稱之為“天真”的貝葉斯算法
2.1 基于貝葉斯決策理論的分類方法
  • 樸素貝葉斯是貝葉斯決策理論的一部分,要完全掌握樸素貝葉斯的話有必要了解一下貝葉斯決策理論

    • 假設有一個數據集,由兩類數據組成(體現為圓點和三角形),分布情況如下圖所示:

    • 我們現在用p0(x,y)表示數據點(x,y)屬于類別0(圖中用圓點表示的類別)的概率,用p1(x,y)表示數據點(x,y)屬于類別1(圖中用三角形表示的類別)的概率,那么對于一個新數據點(x,y),可以使用下面的規則來判斷它的類別:

      • 如果p0(x,y) > p1(x,y),那么數據點(x,y)的類別為0

      • 如果p1(x,y) > p0(x,y),那么數據點(x,y)的類別為1

    • 也就是說,我們會高概率對應的類別。這也就是貝葉斯決策理論的核心思想,即選擇具有最高概率的決策

2.2 條件概率
  • 在2.1中提到了概率p0和p1,那么如何計算p0和p1?這就需要理解條件概率的計算。相信只要上過概率論,肯定對符號p(x,y | c)很熟悉,下面通過一個例子來復習一下條件概率

    • 假設現在有一個裝了7塊石頭的罐子,其中3塊灰色的,4塊黑色的(如下圖所示)。如果從罐中隨機取出一塊石頭,那么是灰色石頭的可能性是多少?顯然是3/7。我們使用P(gray)來表示取到灰色石頭的概率,其概率值可以通過灰色石頭數量除以總的石頭數量來得到

    • 那如果這7塊石頭如下圖所示放在兩個桶中,那么上述概率應該怎么計算?

    • 要計算P(gray)或者P(black),事先得知道石頭所在桶的信息會不會改變結果?你有可能已經想到計算從B桶中取到灰色石頭概率的辦法,這就是所謂的條件概率。假定計算的是從B桶中取到灰色石頭的概率,這個概率可以記作P(gray | bucketB),我們稱之為“在已知石頭出自B桶的條件下,取出灰色石頭的概率”。不難算出,P(gray | bucketA)=1/2,P(gray | bucketB)=1/3。條件概率的計算公式如下所示:

      • P(gray | bucketB) = P(gray and bucketB) / P(bucketB)
2.3 使用條件概率來分類
  • 2.1節提到貝葉斯決策理論要求計算兩個概率p0(x,y)和p1(x,y):

    • 如果p0(x,y) > p1(x,y),那么數據點(x,y)的類別為0

    • 如果p1(x,y) > p0(x,y),那么數據點(x,y)的類別為1

  • 但這兩個準則并不是貝葉斯決策理論的所有內容。使用p0()和p1()只是為了盡可能簡化描述,而真正需要計算和比較的是p(c0 | x,y)和p(c1 | x,y)。這些符號所代表的具體意義是:給定某個由x、y表示的數據點,那么該數據點來自類別c0的概率是多少?數據點來自類別c1的概率又是多少?使用這些定義,可以定義貝葉斯準則為:

    • 如果P(c0 | x,y) > P(c1 | x,y),那么屬于類別0

    • 如果P(c1 | x,y) > P(c0 | x,y),那么屬于類別1

  • 基于貝葉斯準則,可以訓練出一個功能強大的貝葉斯分類器,接下來對我構造的分類器進行詳細說明

二、進行實驗

1、算法思路

  • 其實在實驗原理部分,已經是詳細的算法思路了,因為樸素貝葉斯的核心思想,就是實驗原理中說明的三個部分。簡單來說,整體的思路過程就是:對于給定的訓練集,首先基于特征條件獨立假設學習輸入/輸出的聯合概率分布。然后基于此模型,對于給定的輸入x,利用貝葉斯定理求出后驗概率最大的輸出y

2、算法步驟

  • (1) 對訓練數據進行處理,提出每一個訓練數據和其對應的標簽

  • (2) 根據訓練數據,生成一個詞匯表

  • (3) 向量化每個訓練樣本,然后結合生成的詞匯表,訓練樸素貝葉斯分類器

  • (4) 對測試數據進行處理,提出每一個測試數據和其對應的標簽

  • (5) 向量化每個測試數據,使用樸素貝葉斯分類器對其進行分類

  • (6) 即通過比較概率p0和p1的大小,判斷其屬于哪個類別

3、代碼實現

  • 注:代碼中的所有函數功能已注釋在函數頭部,具體的代碼作用也在代碼后面進行了注釋說明

  • (1) 處理數據文件。這里因為訓練數據和測試數據的存放格式一樣的,因此能夠使用同一個功能函數loadFile進行處理。對郵件內容進行切分,取出每封郵件對應的類別標簽,生成每封郵件對應的詞條向量,返回這兩部分

def loadFile(filename):"""函數說明:加載數據文件:param filename:文件名:return:contentList - 切分郵件內容得到的詞條classVec - 類別標簽向量"""file = open(filename)contentList = []classVec = []contents = file.readlines()for line in contents:content = line.strip('\n').split(' ') #以空格為分割符,切分郵件的內容,得到該郵件對應的詞條classVec.append(int(content[0])) #取出郵件的類別標簽del(content[0]) #刪掉詞條中的類別標簽contentList.append(content)return contentList, classVec
  • (2) 根據處理訓練數據得到的詞條,匯總生成一個詞匯表。其中使用set取并集的特性,去除重復的詞匯
def createVocabList(dataSet):"""函數說明:根據訓練數據,生成一個詞匯表:param dataSet:切分所有郵件得到的詞條:return:list(vocabSet) - 使用訓練數據生成的不重復的詞匯表"""vocabList = set([]) #創建一個空集合for content in dataSet:vocabList = vocabList | set(content) #通過取并集的方式去重,擴充詞匯表return list(vocabList) #以list的形式返回詞匯表
  • (3) 根據vocabList詞匯表,將每個wordsSet詞條向量化,向量的每個值為1或0,分別表示該詞有或者沒有在詞匯表中出現
def Words_to_Vec(vocabList, wordsSet):"""函數說明:根據vocabList詞匯表,將每個wordsSet詞條向量化,向量的每個值為1或0,分別表示該詞有或者沒有在詞匯表中出現:param vocabList:詞匯表:param inputSet:切分每封郵件得到的詞條:return:詞條向量"""returnVec = [0] * len(vocabList)for word in wordsSet: #判斷每個詞是否在詞匯表中出現if word in vocabList:returnVec[vocabList.index(word)] = 1 #在詞匯表中出現的話則該詞對應的位置標記為1else:print("The word %s is not in the VocabList!" % word)return returnVec
  • (4) 向量化每個訓練樣本,然后結合生成的詞匯表,訓練樸素貝葉斯分類器。其中使用了拉普拉斯平滑的方法
def trainNB(trainMat, trainLabel):"""函數說明:樸素貝葉斯分類訓練函數:param trainMat:訓練文檔,即Words_to_Vec函數返回的詞向量構成的矩陣:param trainLabel:訓練數據的類別標簽,即loadFile函數返回的classVec:return:p0Vec - 侮辱類的條件概率數組p1Vec - 非侮辱類的條件概率數組pNotAbusive - 文檔屬于侮辱類的概率"""numTraindocs = len(trainMat) #訓練集的數量numWords = len(trainMat[0]) #每個詞條向量的長度pNotAbusive = sum(trainLabel) / float(numTraindocs) #文檔屬于非侮辱類的概率p0Num = np.ones(numWords) #創建numpy.ones數組,詞條出現數初始化為1,拉普拉斯平滑方法p1Num = np.ones(numWords)p0Denom = 2.0 ##分母初始化為2,拉普拉斯平滑方法p1Denom = 2.0for i in range(numTraindocs):if trainLabel[i] == 1:p1Num += trainMat[i] #統計屬于非侮辱類的條件概率所需的數據,即P(w0|1),P(w1|1),P(w2|1)···p1Denom += sum(trainMat[i])else:p0Num += trainMat[i] #統計屬于侮辱類的條件概率所需的數據,即P(w0|0),P(w1|0),P(w2|0)···p0Denom += sum(trainMat[i])p1Vec = np.log(p1Num / p1Denom) #取對數p0Vec = np.log(p0Num / p0Denom)return p0Vec, p1Vec, pNotAbusive
  • (5) 向量化每個測試數據,使用樸素貝葉斯分類器對其進行分類,通過比較概率p0和p1的大小,判斷其屬于哪個類別
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass0):"""函數說明:樸素貝葉斯分類函數:param vec2Classify:待分類的詞條向量:param p0Vec:侮辱類的條件概率數組:param p1Vec:非侮辱類的條件概率數組:param pClass0:文檔屬于侮辱類的概率:return:0 - 文檔屬于侮辱類1 - 文檔屬于分侮辱類"""p1 = sum(vec2Classify * p1Vec) + np.log(pClass0) p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass0)if p1 > p0:return 1else:return 0
  • (6) 主函數,綜合調用上述的功能函數,加載處理數據,訓練分類器,然后對測試集郵件進行分類,最后計算分類的準確率
def main():trainList, trainLabel = loadFile('spam_train.txt') #處理訓練數據VocabList = createVocabList(trainList) #生成詞匯表'''#由于數據集較大,生成的詞匯表內容較多,因此我把生成的詞匯表寫出到txt文件,后續可以直接調用file = open('VocabList.txt', 'w')file.write(str(VocabList))file.close()'''trainMat = []cnt = 0 #用來標記處理到第幾組數據,在處理完每組數據后就會加1輸出for train in trainList: #生成訓練集矩陣trainMat.append(Words_to_Vec(VocabList, train))cnt += 1print("當前處理到第%s組訓練數據." % cnt)'''#由于數據集較大,生成訓練集矩陣較慢,內容也較多,因此我把生成的矩陣寫出到txt文件,后續可以直接調用file = open('trainMat.txt', 'w')file.write(str(trainMat))file.close()'''p0V, p1V, pAb = trainNB(np.array(trainMat), np.array(trainLabel)) #使用訓練集矩陣訓練分類器testList, testLabel = loadFile('spam_test.txt') #處理測試數據resultLabel = []cnt = 0for test in testList: #分類測試數據,將分類的標簽放入resultLabeldoc = np.array(Words_to_Vec(VocabList, test))if classifyNB(doc, p0V, p1V, pAb):resultLabel.append(1)else:resultLabel.append(0)cnt += 1print("當前處理到第%s組測試數據." % cnt)cc = 0 #分類正確的個數for i in range(len(testLabel)): #對比分類標簽和真實標簽if testLabel[i] == resultLabel[i]:cc += 1print('預測準確率:' + str(100 * cc / float(len(testLabel))) + '%') #計算準確率

4、實驗總結

  • 測試最后得到的準確率為98.3%,自認為效果不錯。有一處不足就是生成訓練集矩陣trainMat要花較多時間,有考慮結合多線程或者多進程來加速這一過程

  • 完成這次作業,個人總結了一點樸素貝葉斯算法的優缺點:

    • 優點:

      • 有穩定的分類效率

      • 對小規模的數據表現很好,能個處理多分類任務,適合增量式訓練

      • 對缺失數據不太敏感,算法也比較簡單,常用于文本分類

    • 缺點:

      • 理論上,樸素貝葉斯模型與其他分類方法相比具有最小的誤差率。但是實際上并非總是如此,這是因為樸素貝葉斯模型給定輸出類別的情況下,假設屬性之間相互獨立,這個假設在實際應用中往往是不成立的,在屬性個數比較多或者屬性之間相關性較大時,分類效果不好。而在屬性相關性較小時,樸素貝葉斯性能最為良好

      • 需要知道先驗概率,且先驗概率很多時候取決于假設,假設的模型可以有很多種,因此在某些時候會由于假設的先驗模型的原因導致預測效果不佳

      • 由于我們是通過先驗和數據來決定后驗的概率從而決定分類,所以分類決策存在一定的錯誤率

三、完整代碼

#!/usr/bin/python # -*- coding utf-8 -*- # Project: NB # Author: jiangnan # Mail: jiangnanmax@gmail.com # Date: 2018/9/26import numpy as npdef loadFile(filename):"""函數說明:加載數據文件:param filename:文件名:return:contentList - 切分郵件內容得到的詞條classVec - 類別標簽向量"""file = open(filename)contentList = []classVec = []contents = file.readlines()for line in contents:content = line.strip('\n').split(' ') #以空格為分割符,切分郵件的內容,得到該郵件對應的詞條classVec.append(int(content[0])) #取出郵件的類別標簽del(content[0]) #刪掉詞條中的類別標簽contentList.append(content)return contentList, classVecxdef createVocabList(dataSet):"""函數說明:根據訓練數據,生成一個詞匯表:param dataSet:切分所有郵件得到的詞條:return:list(vocabSet) - 使用訓練數據生成的不重復的詞匯表"""vocabList = set([]) #創建一個空集合for content in dataSet:vocabList = vocabList | set(content) #通過取并集的方式去重,擴充詞匯表return list(vocabList) #以list的形式返回詞匯表def Words_to_Vec(vocabList, wordsSet):"""函數說明:根據vocabList詞匯表,將每個wordsSet詞條向量化,向量的每個值為1或0,分別表示該詞有或者沒有在詞匯表中出現:param vocabList:詞匯表:param inputSet:切分每封郵件得到的詞條:return:詞條向量"""returnVec = [0] * len(vocabList)for word in wordsSet: #判斷每個詞是否在詞匯表中出現if word in vocabList:returnVec[vocabList.index(word)] = 1 #在詞匯表中出現的話則該詞對應的位置標記為1else:print("The word %s is not in the VocabList!" % word)return returnVecdef trainNB(trainMat, trainLabel):"""函數說明:樸素貝葉斯分類訓練函數:param trainMat:訓練文檔,即Words_to_Vec函數返回的詞向量構成的矩陣:param trainLabel:訓練數據的類別標簽,即loadFile函數返回的classVec:return:p0Vec - 侮辱類的條件概率數組p1Vec - 非侮辱類的條件概率數組pNotAbusive - 文檔屬于侮辱類的概率"""numTraindocs = len(trainMat) #訓練集的數量numWords = len(trainMat[0]) #每個詞條向量的長度pNotAbusive = sum(trainLabel) / float(numTraindocs) #文檔屬于非侮辱類的概率p0Num = np.ones(numWords) #創建numpy.ones數組,詞條出現數初始化為1,拉普拉斯平滑方法p1Num = np.ones(numWords)p0Denom = 2.0 ##分母初始化為2,拉普拉斯平滑方法p1Denom = 2.0for i in range(numTraindocs):if trainLabel[i] == 1:p1Num += trainMat[i] #統計屬于非侮辱類的條件概率所需的數據,即P(w0|1),P(w1|1),P(w2|1)···p1Denom += sum(trainMat[i])else:p0Num += trainMat[i] #統計屬于侮辱類的條件概率所需的數據,即P(w0|0),P(w1|0),P(w2|0)···p0Denom += sum(trainMat[i])p1Vec = np.log(p1Num / p1Denom) #取對數p0Vec = np.log(p0Num / p0Denom)return p0Vec, p1Vec, pNotAbusivedef classifyNB(vec2Classify, p0Vec, p1Vec, pClass0):"""函數說明:樸素貝葉斯分類函數:param vec2Classify:待分類的詞條向量:param p0Vec:侮辱類的條件概率數組:param p1Vec:非侮辱類的條件概率數組:param pClass0:文檔屬于侮辱類的概率:return:0 - 文檔屬于侮辱類1 - 文檔屬于分侮辱類"""p1 = sum(vec2Classify * p1Vec) + np.log(pClass0) # 對應元素相乘。logA * B = logA + logB,所以這里加上log(pClass1)p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass0)if p1 > p0:return 1else:return 0def main():trainList, trainLabel = loadFile('spam_train.txt') #處理訓練數據VocabList = createVocabList(trainList) #生成詞匯表'''#由于數據集較大,生成的詞匯表內容較多,因此我把生成的詞匯表寫出到txt文件,后續可以直接調用file = open('VocabList.txt', 'w')file.write(str(VocabList))file.close()'''trainMat = []cnt = 0 #用來標記處理到第幾組數據,在處理完每組數據后就會加1輸出for train in trainList: #生成訓練集矩陣trainMat.append(Words_to_Vec(VocabList, train))cnt += 1print("當前處理到第%s組訓練數據." % cnt)'''#由于數據集較大,生成訓練集矩陣較慢,內容也較多,因此我把生成的矩陣寫出到txt文件,后續可以直接調用file = open('trainMat.txt', 'w')file.write(str(trainMat))file.close()'''p0V, p1V, pAb = trainNB(np.array(trainMat), np.array(trainLabel)) #使用訓練集矩陣訓練分類器testList, testLabel = loadFile('spam_test.txt') #處理測試數據resultLabel = []cnt = 0for test in testList: #分類測試數據,將分類的標簽放入resultLabeldoc = np.array(Words_to_Vec(VocabList, test))if classifyNB(doc, p0V, p1V, pAb):resultLabel.append(1)else:resultLabel.append(0)cnt += 1print("當前處理到第%s組測試數據." % cnt)cc = 0 #分類正確的個數for i in range(len(testLabel)): #對比分類標簽和真實標簽if testLabel[i] == resultLabel[i]:cc += 1print('預測準確率:' + str(100 * cc / float(len(testLabel))) + '%') #計算準確率if __name__ == '__main__':main()

總結

以上是生活随笔為你收集整理的朴素贝叶斯算法:实现邮件分类的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。