樸素貝葉斯分類文字 python實現
樸素貝葉斯(naive bayes)模型主要用於文字分類,比如要將郵件分類為正常郵件和帶侮辱性詞彙郵件
對於一封郵件來說其特徵可以表示為該郵件中單詞出現的情況。
比如我們有一個5000個詞的詞典表,那麼郵件的特徵可表示成一個特徵向量,特徵向量的維數等於詞典表的單詞個數,特徵向量每一維的取值空間為0或1(即這個單詞是否出現)
對於p(x|y),在某一組樣本中:
p(x1x2...x5000|y)=p(x1|y)p(x2|y,x1).....p(x5000|y,x1,x2...x4999)
這個問題是很複雜的,因此我們需要做樸素貝葉斯假設(這就是為什麼這個演算法樸素的原因):
我們假設樣本中的特徵(即每個詞出現的情況)相互獨立
該假設不符合常理,因為很多詞的出現概率之間是有聯絡的。例如,郵件中如果出現了‘Obama’,那麼郵件中出現‘USA’的概率將會大大提高。
但是我們做了這個假設後,能夠簡化我們的問題,且分類器的效果還不錯
因此上式可改為:
p(x1x2...x5000|y)=p(x1|y)p(x2|y,x1).....p(x5000|y,x1,x2...x4999)
=p(x1|y)p(x2|y)..p(x5000|y)
之後求解極大似然估計啥的就不寫了,最終通過訓練樣本我們需要得到的是:y=0時第j個詞出現的概率,y=1時第j個詞出現的概率,以及樣本中y=1的概率
因此,對於一組輸入,我們要確定它屬於0類還是1類,我們只需要比較:
y=1時輸入中所有出現的詞在樣本中出現的概率之積*樣本中y=1的概率 與
y=0時輸入中所有出現的詞在樣本中出現的概率之積*樣本中y=0的概率 哪個大
說的不是很清楚,因為東西太多了,表達能力有限,公式也沒寫全。具體的細節大家可以看看吳恩達的機器學習課程來學習。
下面上python程式碼,主要參考了《機器學習實戰》。裡邊包括了一些細節,比如log優化,拉普拉斯平滑,大家就看看程式碼的註釋吧,註釋寫的清楚。
naiveBayes.py
# coding=UTF-8 from numpy import * import matplotlib.pyplot as plt import time import math import re def loadTrainDataSet(): #讀取訓練集 fileIn = open('testSet.txt') postingList=[] #郵件表,二維陣列 classVec=[] i=0 for line in fileIn.readlines(): lineArr = line.strip().split() temp=[] for i in range(len(lineArr)): if i==0: classVec.append(int(lineArr[i])) else: temp.append(lineArr[i]) postingList.append(temp) i=i+1 return postingList,classVec def createVocabList(dataSet): #建立詞典 vocabSet = set([]) #定義list型的集合 for document in dataSet: vocabSet = vocabSet | set(document) return list(vocabSet) def setOfWords2Vec(vocabList,inputSet): #對於每一個訓練樣本,得到其特徵向量 returnVec= [0]*len(vocabList) for word in inputSet: if word in vocabList: returnVec[vocabList.index(word)] = 1 else: pass #print("\'%s\' 不存在於詞典中"%word) return returnVec def createTrainMatrix(vocabList,postingList): #生成訓練矩陣,即每個樣本的特徵向量 trainMatrix=[] #訓練矩陣 for i in range(len(postingList)): curVec=setOfWords2Vec(vocabList,postingList[i]) trainMatrix.append(curVec) return trainMatrix def trainNB0(trainMatrix,trainCategory): numTrainDocs = len(trainMatrix) #樣本數量 numWords = len(trainMatrix[0]) #樣本特徵數 pAbusive = sum(trainCategory)/float(numTrainDocs) #p(y=1) #分子賦值為1,分母賦值為2(拉普拉斯平滑) p0Num=ones(numWords); #初始化向量,代表所有0類樣本中詞j出現次數 p1Num=ones(numWords); #初始化向量,代表所有1類樣本中詞j出現次數 p0Denom=p1Denom=2.0 #代表0類1類樣本的總詞數 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 = p1Num/p1Denom #概率向量(p(x0=1|y=1),p(x1=1|y=1),...p(xn=1|y=1)) p0Vect = p0Num/p0Denom #概率向量(p(x0=1|y=0),p(x1=1|y=0),...p(xn=1|y=0)) #取對數,之後的乘法就可以改為加法,防止數值下溢損失精度 p1Vect=log(p1Vect) p0Vect=log(p0Vect) return p0Vect,p1Vect,pAbusive def classifyNB(vocabList,testEntry,p0Vec,p1Vec,pClass1): #樸素貝葉斯分類 #先將輸入文字處理成特徵向量 regEx = re.compile('\\W*') #正則匹配分割,以字母數字的任何字元為分隔符 testArr=regEx.split(testEntry) testVec=array(setOfWords2Vec(vocabList,testArr)) #此處的乘法並非矩陣乘法,而是矩陣相同位置的2個數分別相乘 #矩陣乘法應當 dot(A,B) 或者 A.dot(B) #下式子是原式子取對數,因此原本的連乘變為連加 p1=sum(testVec*p1Vec)+log(pClass1) p0=sum(testVec*p0Vec)+log(1.0-pClass1) #比較大小即可 if p1>p0: return 1 else: return 0 #測試方法 def testingNB(): postingList,classVec=loadTrainDataSet() vocabList=createVocabList(postingList) trainMatrix=createTrainMatrix(vocabList,postingList) p0V,p1V,pAb=trainNB0(trainMatrix,classVec) #輸入測試文字,單詞必須用空格分開 testEntry='welcome to my blog!' #testEntry='fuck you bitch!!!' print('測試文字為: '+testEntry) if classifyNB(vocabList,testEntry,p0V,p1V,pAb): print("--------侮辱性郵件--------") else: print("--------正常郵件--------") testingNB()
testSet.txt //訓練集,開頭的01代表郵件是否是侮辱性郵件
0 i want you
1 fuck you
0 i want to go shopping
1 you are sillyb
0 i eat a lot of food
0 my university is UPC and ECNU
0 my professor is gaoming
0 i am learning data mining and machine learning
0 i think i need to buy some pen
1 you are shit fucking
0 haha haha haha
0 will you merry me:
1 fuck u dog sillyb
1 you are stupid
1 fuck stupid dog
1 fuck mother
1 fuck father dick
1 shit fuck bitch penis dick
分類結果:
輸入為 'welcome to my blog!' 分類為普通郵件
輸入為‘fuck you bitch!!’ 分類為侮辱性郵件