(上接第二章)
2.3 分类算法:朴素贝叶斯
2.3.1 贝叶斯公式推导(略)
分类的流程:
第一阶段:训练数据生成训练样本集:TF-IDF
第二阶段:对每个类别计算p(yi)。
第三个阶段:对每个特征属性计算所有划分的条件概率
第四个阶段:对每个类别计算P(x|yi)P(yi)。
第五个阶段:以P(x|yi)P(yi)的最大项作为x的所属类别。
2.3.2 朴素贝叶斯算法实现
创建Nbayes_lib.py的文件,这个文件用来导入数据和朴素贝叶斯类的代码。
使用简单的英文语料作为数据集:
def loadDataSet(): postingList =[['my','dog','has','flea','problem','help','please'], ['maybe','not','take','him','to','dog','park','stupid'], ['my','dalmation','is','so','cute','I','love','him','my'], ['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] #1 is abusive,0 not return postingList,classVec
posstingList 是训练集文本,classVec是每个文本对应的分类:
(1)编写一个贝叶斯算法类,并创建默认的构造方法:
class NBayes(object): def __init__(self): self.vocabulary = [] #词典 self.idf = 0 #词典的IDF权值向量 self.tf = 0 #训练值的权值矩阵 self.tdm = 0 #P(x|yi) self.Pcates = {} #P(yi)是一个类别字典 self.labels = [] #对应每个文本的分类,是一个外部导入的列表 self.doclength = 0 #训练集文本数 self.vocablen = 0 #词典词长 self.testset = 0 #测试集
(2)导入和训练数据集,生成算法必需的参数和数据结构
def train_set(self,trainset,classVec): self.cate_prob(classVec) #计算每个分类在数据集中的概率P(yi) self.doclength = len(trainset) tempset = set() [tempset.add(word) for doc in trainset for word in doc] #生成词典 self.vocabulary = list(tempset) self.vocablen = len(self.vocabulary)
self.calc_wordfreq(trainset)
self.build_tdm() #按分类累计向量空间的每维值P(x|yi)
(3)cate_prob函数:计算在数据集中每个分类的概率P(yi)
def cate_prob(self,classVec): self.labels = classVec labeltemps = set(self.labels) #获取全部分类 for labeltemp in labeltemps: #统计列表中重复的分类: self.labels.count(labeltemp) self.Pcates[labeltemp] = float(self.labels.count(labeltemp))/float(len(self.labels))
(4)calc_wordfreq函数:生成普通的词频向量
def calc_wordfreq(self,trainset): self.idf = np.zeros([1,self.vocablen]) #1*词典数 self.tf = np.zeros([self.doclength,self.vocablen]) #训练集文件数*词典数 for indx in xrange(self.doclength): #遍历所有的文本 for word in trainset[indx]: #遍历文本中的每个词 #找到文本的词在字典中的位置+1 self.tf[indx,self.vocabulary.index(word)] += 1 for signleword in set(trainset[indx]): #idf词数 self.idf[0,self.vocabulary.index(signleword)] += 1
(5)Build_tdm函数:按分类累计计算向量空间的每维值P(x|yi)
def build_tdm(self): self.tdm = np.zeros([len(self.Pcates),self.vocablen]) #类别行*词典列 sumlist = np.zeros([len(self.Pcates),1]) #统计每个分类的总值 for indx in xrange(self.doclength): #将同一类别的词向量空间值加总 self.tdm[self.labels[indx]] += self.tf[indx] #统计每个分类的总值--是一个标量 sumlist[self.labels[indx]] = np.sum(self.tdm[self.labels[indx]]) self.tdm = self.tdm/sumlist #生成P(x|yi)
(6)map2vocab函数:将测试集映射到当前词典
# (6)map2vocab函数:将测试集映射到当前词典 def map2vocab(self,testdata): self.testset = np.zeros([1,self.vocablen]) for word in testdata: self.testset[0,self.vocabulary.index(word)] += 1
(7)predict函数:预测分类结果,输出预测的分类类别
def predict(self,testset): if np.shape(testset)[1] != self.vocablen: #如果测试集长度与词典不相等,则退出程序 print "输入错误" exit(0) predvalue = 0 #初始化类别概率 predclass = "" #初始化类别名称 for tdm_vect,keyclass in zip(self.tdm,self.Pcates): #P(x|yi)*P(yi) #变量tdm,计算最大分类值 temp = np.sum(testset*tdm_vect*self.Pcates[keyclass]) if temp > predvalue: predvalue = temp predclass = keyclass return predclass
2.3.3 算法的改进
此算法的改进是为普通的词频向量使用TF-IDF策略,使之有能力修正多种偏差
calc_tfidf函数:以TF-IDF方式生成向量空间。
#生成tf-idf def calc_tfidf(self,trainset): self.idf = no.zeros([1,self.vocablen]) self.tf = np.zeros([self.doclength,self.vocablen]) for indx in xrange(self.doclength): for word in trainset[indx]: self.tf[indx,self.vocabulary.index(word)] += 1 #消除不同句长导致的偏差 self.tf[indx] = self.tf[indx]/float(len(trainset[indx])) for signleword in set(trainset[indx]): self.idf[0,self.vocabulary.index(signleword)] += 1 self.idf = np.log(float(self.doclength)/self.idf) self.tf = np.multiply(self.tf,self.idf) #矩阵与向量的点乘 TF*IDF
2.3.4 评估分类结果
#coding:utf-8 import sys import os from numpy import * import numpy as np from Nbayes_lib import * #评估分类结果 dataSet,listClasses = loadDataSet() #导入外部数据集 #dataSet:句子的词向量 #listClass是句子所属的类别[0,1,0,1,0,1] nb = NBayes() #实例化 nb.train_set(dataSet,listClasses) #训练数据集 nb.map2vocab(dataSet[0]) #随机选择一个测试语句 print nb.predict(nb.testset)
分类结果如下:
1
参考资料及版权所有:郑捷《机器学习算法原理与编程实践》