樸素貝葉斯的中心思想,在於利用各類別在訓練樣本中的分佈以及類別中各特徵元素的分佈,計算後驗概率,使用極大似然法判斷測試樣本所屬。出於該原理,使用該算法實現文本分類的侷限性較多,例如訓練集中各類樣本的比例不能相差過大,比例較大的樣本類別會獲得更高的劃分可能性;其次,該算法假設詞與詞之間相互獨立,共享權重,忽視了詞與詞之間的關聯性,面臨共指消解 (同一實體不同表述) 的問題,因此只能用於諸如垃圾郵件識別的簡單分類。
以下代碼來自鄭捷所著《NLP漢語自然語言處理,原理與實踐》:
import numpy as np
class NaiveBayes(object):
'''
算法原理:
- 訓練
i.借用TF-IDF中TF的概念生成詞頻矩陣TF
ii.計算各個分類的邊緣概率P(yi)
iii.計算各個分類的後驗詞頻分佈P(x|yi)
- 預測
i.計算測試詞頻TF
ii.對每個分類計算TF*P(x|yi)*P(yi),以最大項作爲預測類別
'''
def __init__(self):
self.train = [] #訓練集
self.label = [] #訓練集標籤
self.vocab = [] #詞集
self.tf = None #TF矩陣(row:訓練集數量 col:詞集詞頻)
self.idf = None #IDF向量(詞集長度)
self.prob = {} #P(yi)
self.con_prob = None #P(x|yi) (row:類別 col:詞集詞頻)
def fit(self,train,label):
'''導入訓練集,生成算法參數'''
assert isinstance(train,list)
assert isinstance(label,list)
assert len(train) == len(label)
self.train += train
self.label += label
vocab = set()
[vocab.add(word) for text in train for word in text]; #將詞語導入詞集
self.vocab = list(vocab)
self.cal_prob() #計算分類的邊緣概率P(yi)
self.cal_tfidf() #計算TF-IDF
self.cal_con_prob() #計算後驗概率分佈:P(x|yi)
def cal_prob(self):
'''針對每個分類計算P(yi)'''
for y in set(self.label):
self.prob[y] = float(self.label.count(y)/len(self.label)) #計算每個分類在數據集中的概率:P(yi)
def cal_tfidf(self):
'''生成TF矩陣和IDF向量'''
self.tf = np.zeros((len(self.train),len(self.vocab)))
self.idf = np.zeros(len(self.vocab))
for idx in range(len(self.train)):
for word in self.train[idx]:
self.tf[idx,self.vocab.index(word)] += 1
self.tf[idx] = self.tf[idx]/len(train[idx]) #消除不同句長導致的偏差
for word in set(self.train[idx]):
self.idf[self.vocab.index(word)] += 1
self.idf = np.log(len(self.train)/self.idf)
def cal_con_prob(self):
'''計算後驗概率分佈:P(x|yi)'''
self.con_prob = np.zeros((len(self.prob),len(self.vocab)))
con_sum = np.zeros((len(self.prob),1)) #統計每個分類的總值
for idx in range(len(self.train)):
self.con_prob[self.label[idx]] += self.tf[idx] #將同一類別的TF矩陣按列加總
for y in self.prob.keys():
con_sum[y] = np.sum(self.con_prob[y]) #統計每個分類的概率分佈總值
self.con_prob = self.con_prob/con_sum #歸一化生成P(x|yi)
def predict(self,test):
'''針對每個分類計算TF*P(x|yi)*P(yi),輸出最大值所在分類作爲預測分類'''
assert isinstance(test,list)
assert isinstance(test[0],str)
if len(set(test)-set(self.vocab)) > 0: #如果測試集包含詞集裏沒有的詞,則退出程序
raise ValueError('測試集中包含訓練集中未出現過的詞,請重新輸入')
test_tf = np.zeros([1,len(self.vocab)])
for word in test:
test_tf[0,self.vocab.index(word)] += 1 #生成測試集TF矩陣
test_tf /= len(self.vocab)
pred_prob = []
for y in self.prob.keys():
pred_prob += [np.sum(test_tf * self.con_prob[y] * self.prob[y])] #計算計算TF*P(x|yi)*P(yi)
return np.argmax(pred_prob)
if __name__ == '__main__':
train = [['我們','常常','仰望','和','羨慕','別人','的','幸福'],
['一','回頭','卻','發現','自己','也','正','被','仰望','和','羨慕','着'],
['其實','每個','人','都','是','幸福','的'],
['只是','你','的','幸福','常常','在','別人','眼裏']]
label = [0,1,2,1]
test = train[0]
model = NaiveBayes()
model.fit(train,label) #模型訓練
pred = model.predict(test) #模型預測
print('Prediction result: %s'%pred)