朴素贝叶斯的中心思想,在于利用各类别在训练样本中的分布以及类别中各特征元素的分布,计算后验概率,使用极大似然法判断测试样本所属。出于该原理,使用该算法实现文本分类的局限性较多,例如训练集中各类样本的比例不能相差过大,比例较大的样本类别会获得更高的划分可能性;其次,该算法假设词与词之间相互独立,共享权重,忽视了词与词之间的关联性,面临共指消解 (同一实体不同表述) 的问题,因此只能用于诸如垃圾邮件识别的简单分类。
以下代码来自郑捷所著《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)