一、模型方法
本工程采用的模型方法為樸素貝葉斯分類算法,它的核心算法思想基于概率論。我們稱之為“樸素”,是因為整個形式化過程只做最原始、最簡單的假設。樸素貝葉斯是貝葉斯決策理論的一部分,所以講述樸素貝葉斯之前有必要快速了解一下貝葉斯決策理論。假設現在我們有一個數據集,它由兩類數據組成,數據分布如下圖所示。
我們現在用p1(x,y)表示數據點(x,y)屬于類別1(圖中用圓點表示的類別)的概率,用p2(x,y)表示數據點(x,y)屬于類別2(圖中用三角形表示的類別)的概率,那么對于一個新數據點(x,y),可以用下面的規則來判斷它的類別:
如果 p1(x,y) > p2(x,y),那么類別為1。
如果 p2(x,y) > p1(x,y),那么類別為2。
也就是說,我們會選擇高概率對應的類別。這就是貝葉斯決策理論的核心思想,即選擇具有最高概率的決策。
在本工程中我們可以使用條件概率來進行分類。其條件概率公式如下:
其中粗體w表示這是一個向量,它是有多個值組成。對于類別i表示分類的個數,在本工程中i=0時,c0表示非垃圾郵件。i=1時,c1表示垃圾郵件。w展開為一個個獨立特征,那么就可以將上述概率寫作p(w0,w1,w2..wN|ci)。這里假設所有詞都互相獨立,該假設也稱作條件獨立性假設,它意味著可以使用p(w0|ci)p(w1|ci)p(w2|ci)...p(wN|ci)來計算上述概率,這就極大地簡化了計算的過程,這也是被稱為樸素貝葉斯的原因。在本工程中wj代表第i個單詞特征,而p(wj|ci)則代表了在垃圾郵件(或非垃圾郵件)中,第j個單詞出現的概率;而p(w|ci)則表示在垃圾郵件(或非垃圾郵件)中的全體向量特征(單詞向量特征)出現的概率;而p(ci| w)則表示在全體向量特征(單詞向量特征)下是垃圾郵件(或非垃圾郵件)的概率。本工程項目主要是計算p(ci|w);p(ci)則表示是垃圾郵件(或非垃圾郵件)的概率。
二、系統設計
數據的收集及保存
郵件的收集來源于網上,保存在email文件夾中。其中email分兩個子文件,一個為ham文件夾(保存非垃圾郵件),另一個為spam文件夾(保存垃圾郵件)。ham與spam中各保存25各郵件,保存格式為x.txt(x為1到25)。
訓練集和測試集的選取
由于收集的郵件個數有限,故選取80%的郵件作為訓練集,其方式為隨機選取。剩余20%郵件作為測試集。
特征向量構建
特征向量的構建分為兩種,一個為對訓練集的特征向量構建。一個為測試集的特征向量構建。對于訓練集特征向量只需要分為兩類,因為郵件只分為垃圾郵件和非垃圾郵件。特征向量分為對訓練集中所有垃圾郵件中構成的特征向量(記做w)和訓練集中所有非垃圾郵件構成特征向量(記做w')。對于w的計算實際就是統計所有訓練集中垃圾郵件中的每個單詞的出現情況,出現則次數加1。其計數初值為1,按照正常情況應為0,因為用的樸素貝葉斯算法,假設所有詞都互相獨立 ,就有p(w|ci) = p(w0|ci)p(w1|ci)p(w2|ci)...p(wN|ci)。所以當第i個單詞wi在其特征向量中沒有出現,則有p(wi|ci) =0,這就導致了p(w|ci)導致結果的不正確性。所以我們索性將所有單詞默認出現1遍,所以從1開始計數。對于w'的計算和w的計算方法相同,這里就不在贅述。
對于測試集的特征向量構建就是對每個郵件中單詞出現的次數進行統計,其單詞表可以來源于50個郵件中的所有單詞。對于每一個郵件中單詞如果出現就加1,其計數初值為0。每個測試集的郵件都需構建特征向量。其特征向量在python中可用列表表示。
構建貝葉斯分類器
對于分類器的訓練其目的訓練三個參數為p1Vect(w中每個單詞出現的概率構成的特征向量)、p0Vect(w'中每個單詞出現的概率構成的特征向量)和pAbusive(訓練集中垃圾郵件的概率)。對于p1Vect、p0Vect計算可能會造成下溢出,這 是 由 于 太 多 很 小 的 數 相 乘 造 成 的 。 當 計 算 乘 積p(w0|ci)p(w1|ci)p(w2|ci)...p(wN|ci)時,由于大部分因子都非常小,所以程序會下溢出或者得到不正確的答案。一種解決辦法是對乘積取自然對數。在代數中有ln(a*b) = ln(a)+ln(b),于是通過求對數可以避免下溢出或者浮點數舍入導致的錯誤。同時,采用自然對數進行處理不會有任何損失。圖1給出函數f(x)與ln(f(x))的曲線。檢查這兩條曲線,就會發現它們在相同區域內同時增加或者減少,并且在相同點上取到極值。它們的取值雖然不同,但不影響最終結果。
所以p1Vect = log(w/p1Denom),p0Vect = log(w'/p0Denom),其中p1Denom、p0Denom分別為垃圾郵件中單詞的總數和非垃圾郵件中單詞的總數。而pAbusive 就等于訓練集中垃圾郵件總數與訓練集中郵件總數之比。
測試集驗證與評估
對于判斷是否為垃圾郵件,只需對每個郵件判斷p(c0|w)(不是垃圾郵件的概率)與p(c1|w)(是垃圾郵件的概率)。
q如果p(c0|w) > p(c1|w),那么該郵件為非垃圾郵件。
q如果 p(c0|w) < p(c1|w),那么該郵件為垃圾郵件。
然而p(ci|w)(i=0或1)的計算則依賴于p(w|ci)與p(ci)的計算,p(w)無需計算。所以最終結果依賴于pi = p(w|ci)·p(ci)。由于p(w|ci)很小,可能向下溢出。所以我們取以10為底的對數得log(pi) = log(p(w|ci))+log(p(ci)),所以可得以下結論:
q如果log(p0) > log(p1),那么該郵件為非垃圾郵件。
q如果log(p0) < log(p1),那么該郵件為垃圾郵件。
其中p(w|ci)為在垃圾郵件(或非垃圾郵件)中的全體向量特征(單詞向量特征)出現的概率,p(ci)為訓練集中垃圾郵件(或非垃圾郵件)的概率。
三、系統演示與實驗結果分析對比
由訓練集(40個)和測試集(個)的樣本數目比較小,所以測試的分類結果正確性為90%-100%之間,如下圖所示:
本工程只是對郵件進行二分類,貝葉斯算法也可以處理多分類問題,如新聞的分類,如分成軍事、體育、科技等等。當然本工程只是對英文的垃圾郵件分類,但也可以對中文的垃圾郵件分類(可用python中的jieba的庫模塊進行對中文分詞)。
四、代碼實現
- #coding=UTF-8
- import random
- from numpy import *
- #解析英文文本,并返回列表
- def textParse(bigString):
- #將單詞以空格劃分
- listOfTokens = bigString.split()
- #去除單詞長度小于2的無用單詞
- return [tok.lower() for tok in listOfTokens if len(tok)>2]
- #去列表中重復元素,并以列表形式返回
- def createVocaList(dataSet):
- vocabSet = set({})
- #去重復元素,取并集
- for document in dataSet:
- vocabSet = vocabSet | set(document)
- return list(vocabSet)
- #統計每一文檔(或郵件)在單詞表中出現的次數,并以列表形式返回
- def setOfWordsToVec(vocabList,inputSet):
- #創建0向量,其長度為單詞量的總數
- returnVec = [0]*len(vocabList)
- #統計相應的詞匯出現的數量
- for word in inputSet:
- if word in vocabList:
- returnVec[vocabList.index(word)] += 1
- return returnVec
- #樸素貝葉斯分類器訓練函數
- def trainNB0(trainMatrix,trainCategory):
- #獲取訓練文檔數
- numTrainDocs = len(trainMatrix)
- #獲取每一行詞匯的數量
- numWords = len(trainMatrix[0])
- #侮辱性概率(計算p(Ci)),計算垃圾郵件的比率
- 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
- #test
- def spamtest():
- #導入并解析文本文件
- docList =[];classList=[];fullText = []
- for i in range(1,26):
- #讀取第i篇垃圾文件,并以列表形式返回
- wordList = textParse(open('email/spam/{0}.txt'.format(i)).read())
- #轉化成二維列表
- docList.append(wordList)
- #一維列表進行追加
- fullText.extend(wordList)
- #標記文檔為垃圾文檔
- classList.append(1)
- #讀取第i篇非垃圾文件,并以列表形式返回
- wordList = textParse(open('email/ham/{0}.txt'.format(i)).read())
- #轉化成二維列表
- docList.append(wordList)
- #一維列表進行追加
- fullText.extend(wordList)
- #標記文檔為非垃圾文檔
- classList.append(0)
- #去除重復的單詞元素
- vocabList = createVocaList(docList)
- #訓練集,選40篇doc
- trainingSet = [x for x in range(50)]
- #測試集,選10篇doc
- testSet = []
- #選出10篇doc作測試集
- for i in range(10):
- randIndex = int(random.uniform(0,len(trainingSet)))
- testSet.append(trainingSet[randIndex])
- del trainingSet[randIndex]
- trainMat = [];trainClasses=[]
- #選出40篇doc作訓練集
- for docIndex in trainingSet:
- trainMat.append(setOfWordsToVec(vocabList, docList[docIndex]))
- trainClasses.append(classList[docIndex])
- p0V,p1V,pSpam = trainNB0(array(trainMat), array(trainClasses))
- #對測試集分類
- errorCount = 0
- for docIndex in testSet:
- wordVector = setOfWordsToVec(vocabList,docList[docIndex])
- if classifyNB(array(wordVector), p0V, p1V, pSpam)!=classList[docIndex]:
- errorCount+=1
- print("錯誤率為:{0}".format(float(errorCount)/len(testSet)))
- spamtest()
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://blog.csdn.net/qq_39559641/article/details/89606727