1. 程式人生 > >樸素貝葉斯分類文字 python實現

樸素貝葉斯分類文字 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!!’  分類為侮辱性郵件