前言
翻看貝葉斯的案例,我們就會發(fā)現(xiàn)90%以上的案例都是文本分類鲸鹦,如果想以后轉(zhuǎn)做文本數(shù)據(jù)數(shù)據(jù)挖掘慧库,那么貝葉斯應該是必須點亮的技能燈〔鍪龋“真的是任重道遠啊齐板,希望三天時間能skip到下一個算法「鸸剑”
正文
我們將文本數(shù)據(jù)的標簽用c表示甘磨,c包含多個變量,c_i眯停,特征用w表示济舆,w_i,也就是下面說的詞條莺债,同時假設滋觉,特征之間是相互獨立的签夭。
另外在實際做的過程中,也會發(fā)現(xiàn)椎瘟,相比于之前做的貝葉斯分類器覆致,特征是二維變量侄旬,而在文本分類器中肺蔚,實際上特征只是一維變量,這在計算類條件概率上會簡單一些儡羔。當然宣羊,復雜的貝葉斯分類器應該也會涉及到多特征維度的情況。當特征太多的時候汰蜘,也就到了貝葉斯的極限了仇冯,除非我們依然可以保證相互獨立性,負責貝葉斯的預測誤差就會出現(xiàn)族操。#僅為個人理解苛坚,希望指正,以后有新的認識色难,我會回來修正這個理解泼舱。
下面是偽代碼:
#計算每個類別中的文檔數(shù)目 #也就是$P(C_i)$,各類標簽的文檔數(shù)目
#對每篇訓練文檔:
# 對每個類別
# 如果詞條出現(xiàn)在文檔中:增加該詞條計數(shù)值
# 增加該詞條計數(shù)值
# 對每個類別:
# 對每個詞條:
# 將該詞條的數(shù)目除以總詞條數(shù)目得到條件概率 #求$P(w|c_i)$
核心思想:利用文本構(gòu)建詞庫向量
有關詞向量的概念解釋枷莉,參考:https://blog.csdn.net/michael_liuyu09/article/details/78029062
其實這里的只是簡單的向量化娇昙,方便我們統(tǒng)計詞頻,但是深入到NLP的研究中笤妙,詞庫向量就會被利用計算相關性冒掌,這在NLP中應該比較重要,現(xiàn)在沒有涉及蹲盘,以后希望有機會
import numpy as np
#數(shù)據(jù)導入模塊
def loadDataSet():
postingList=[['my','dog','has','flea','problems','help','please'],
['maybe','not','take','him','to','dog','park','stupid'],
['my','dalmation','id','so','cute','I','love','him'],
['stop','posting','stupid','worthless','garbage'],
['mr','licks','ate','my','steak','how','to','stop','him'],
['quit','buying','worthless','dog','food','stupid']]
classVec=[0,1,0,1,0,1]
return(postingList, classVec)
#詞庫構(gòu)建模塊股毫,詞庫中的詞保證是唯一的
def createVocabList(dataSet): #dataSet是指loadDataSet的反饋文本
vocabSet = set([]) #python中的set是一個無序,去重的集合
for document in dataSet:
vocabSet = vocabSet | set(document) #set(document)對每一個句子進行去重唯一召衔,然后與vocabSet進行合并铃诬,擴充vocabSet
return(list(vocabSet))
#構(gòu)建詞庫向量
def setOfWords2Vec(vocabList, inputSet):
returnVec = [0]*len(vocabList) #構(gòu)建0向量,[0,1]分布
for word in inputSet: #對樣本數(shù)據(jù)的進行遍歷薄嫡,出現(xiàn)詞匯表單詞的氧急,則在對應值輸出1
if word in vocabList:
returnVec[vocabList.index(word)]=1
else: print("the word: %s is not in my Vocabulary" %word)
return(returnVec)
#統(tǒng)計頻數(shù),計算后驗概率
def trainNB0(trainMatrix, trainCategory):
numTrainDocs = len(trainMatrix) #計算我們的文本容量毫深,文件數(shù)
numwords = len(trainMatrix[0]) #計算樣本庫詞匯數(shù)
pAbusive = sum(trainCategory)/float(numTrainDocs) #計算$P_c_i$
p0Num=np.zeros(numwords)
p1Num=np.zeros(numwords)
p0Denom = 0.0; p1Denom = 0.0
for i in range(numTrainDocs): #遍歷每一篇文本
if trainCategory[i]==1: #條件概率分類1的情況
p1Num += trainMatrix[i] #累計每個詞匯出現(xiàn)的次數(shù)
p1Denom += sum(trainMatrix[i]) #累計分類1中的所有詞匯的出現(xiàn)次數(shù)
else: #條件概率分類0的情況
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p1Vect=p1Num/p1Denom #計算每個詞匯在分類1中出現(xiàn)的概率 P(w_i|c_1)
p0Vect=p0Num/p0Denom #計算每個詞匯在分類1中出現(xiàn)的概率 P(w_i|c_0)
return(p0Vect, p1Vect, pAbusive)
#main函數(shù)
if __name__ == "__main__":
listOPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listOPosts)
trainMat = []
for postinDoc in listOPosts: #對文本內(nèi)容逐行遍歷秧倾,進行向量化
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0V,p1V,pAb = trainNB0(trainMat,listClasses) #
print(p0V, p1V, pAb)
output1
其實現(xiàn)在,我們正常情況下就可以計算了
套用我們的公式宿礁,P(c_i|w) = \frac{P(w|c_i)P(c_i)}{P(w)}
如果單純考慮后驗概率最大化,我們只需要計算分子部分弧呐,上面的P(w|c_i)我們可以通過p0V,p1V連乘得到嵌纲。然后分開比較大小就可以幫助我們做出判斷俘枫。
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): #vec2Classify是我們將目標文本向量化的產(chǎn)物
p1 = sum(vec2Classify * p1Vec) * pClass1
p0 = sum(vec2Classify * p0Vec) * (1 - pClass1)
if (p1 > p0):
return(1)
if (p1 < p0):
return(0)
testEntry = ['love', 'my', 'dalmation']
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
print(testEntry, 'classified as', classifyNB(thisDoc, p0V, p1V, pAb))
testEntry = ['stupid']
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
print(testEntry, 'classified as', classifyNB(thisDoc, p0V, p1V, pAb))
output2
但是現(xiàn)實情況并非如此完美,如果我們已有的樣本中某分類下并沒有該特征詞逮走,會導致p(w_i|c_i)=0鸠蚪,這會進而導致我們在計算似然函數(shù)時結(jié)果為0的情況,因此為了避免這個問題师溅,我們引入“拉普拉斯修正”茅信。在條件概率式子中分子分母分別加入一個正數(shù),\lambda>0墓臭。當\lambda=0的時候蘸鲸,就是我們平時說的極大似然函數(shù),當\lambda=1的時候窿锉,我們稱為拉普拉斯平滑酌摇。
公式為:p(w|c_i) = \frac{\sum_{i=1}^N(I(x_j|c_i))+\lambda}{\sum_{i=1}^N(I(c_i))+S_j\lambda}, p(ci)=\frac{\sum_{i=1}^NI(c_i)+\lambda}{N+K\lambda} 嗡载,其中窑多,S_j為每一種X的種類數(shù),K為屬性個數(shù)
在這里我們用拉普拉斯平滑鼻疮,令\lambda=1怯伊,因為我們樣本標簽分為2類,一類是好的語言判沟,一類是有侮辱性的語言耿芹,那么,在我們令K=2挪哄,同時我們的樣本每個單詞同屬一類特征吧秕,那么,S=1
在代碼里迹炼,我們初始化p0Num=1, p1Num=1, p0Denom=2.0, p1Denom=2.0
在這里我們會遇到一個新的問題砸彬,那就是數(shù)據(jù)溢出,在計算中斯入,當數(shù)字非常小的時候砂碉,而我們還在做連乘的時候,會出現(xiàn)數(shù)字下溢出的問題刻两,為了避免這個問題實際中增蹭,我們用轉(zhuǎn)換函數(shù),換種方式計算磅摹,避免數(shù)字過小的問題滋迈。我們引入ln(a*b)=ln(a)+ln(b)
我們先比較f(x)和ln(f(x))的區(qū)別
import matplotlib.pyplot as plt
x=np.linspace(0.01,0.9*np.pi,30)
f=np.sin(x) #我們假設原函數(shù)f(x)為sin函數(shù)
g=np.log(f) #我們假設實際函數(shù)為log(f(x))
plt.plot(x,f)
plt.plot(x,g)
plt.legend(['f(x)','log(f(x))'])
plt.show()
output3
在上圖中霎奢,我們發(fā)現(xiàn)雖然兩個函數(shù)不完全相同,但是饼灿,兩個函數(shù)的極值點很接近幕侠,這對于我們貝葉斯在使用極大似然定理里,影響不大碍彭,因此晤硕,我們引入ln函數(shù)替換連乘問題。
接下來硕旗,我們對前面的部分函數(shù)進行優(yōu)化
#統(tǒng)計頻數(shù)窗骑,計算后驗概率
def trainNB0(trainMatrix, trainCategory):
numTrainDocs = len(trainMatrix) #計算我們的文本容量,文件數(shù)
numwords = len(trainMatrix[0]) #計算樣本庫詞匯數(shù)
pAbusive = sum(trainCategory)/float(numTrainDocs) #計算$P_c_i$
p0Num=np.ones(numwords)
p1Num=np.ones(numwords)
p0Denom = 2.0; p1Denom = 2.0
for i in range(numTrainDocs): #遍歷每一篇文本
if trainCategory[i]==1: #條件概率分類1的情況
p1Num += trainMatrix[i] #累計每個詞匯出現(xiàn)的次數(shù)
p1Denom += sum(trainMatrix[i]) #累計分類1中的所有詞匯的出現(xiàn)次數(shù)
else: #條件概率分類0的情況
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p1Vect=np.log(p1Num/p1Denom) #計算每個詞匯在分類1中出現(xiàn)的概率 P(w_i|c_1)
p0Vect=np.log(p0Num/p0Denom) #計算每個詞匯在分類1中出現(xiàn)的概率 P(w_i|c_0)
return(p0Vect, p1Vect, pAbusive)
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): #vec2Classify是我們將目標文本向量化的產(chǎn)物
p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)
p0 = sum(vec2Classify * p0Vec) + np.log(1 - pClass1)
if (p1 > p0):
return(1)
if (p1 < p0):
return(0)
if __name__ == "__main__":
listOPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listOPosts)
trainMat = []
for postinDoc in listOPosts: #對文本內(nèi)容逐行遍歷漆枚,進行向量化
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0V,p1V,pAb = trainNB0(trainMat,listClasses) #
print(p0V, p1V, pAb)
output4
在這里,我們可以發(fā)現(xiàn)抵知,文本向量矩陣的值已經(jīng)變了墙基,但是不影響我們的結(jié)果。
testEntry = ['love', 'my', 'dalmation']
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
print(testEntry, 'classified as', classifyNB(thisDoc, p0V, p1V, pAb))
testEntry = ['stupid','dalmation']
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
print(testEntry, 'classified as', classifyNB(thisDoc, p0V, p1V, pAb))
output5
到這里刷喜,貝葉斯方法的實踐應該是比較深入了残制,算法的使用也好,還是具體一些細節(jié)問題的思考也好掖疮,接下來就是抽時間看下郵件分類有沒有時間做