根据贝叶斯定理,对一个分类问题,给定样本特征x,样本属于类别y的概率是
下面介绍如何从数据中,学习得到朴素贝叶斯分类模型。概述分类方法,并提出一个值得注意的问题。
学习 1.首先,我们来计算公式(2)中的 p(y=c k )
p(y=c k )=∑ N i=1 I(y i =c k )N 。。。。(3)
其中 I(x) 为指示函数,若括号内成立,则计1,否则为0。
学习 2.接下来计算分子中的条件概率,设 M 维特征的第 j 维有 L 个取值,则某维特征的某个取值 a jl ,在给定某分类 c k 下的条件概率为:
p(x j =a jl |y=c k )=∑ N i=1 I(x j i =a jl ,y i =c k )∑ N i=1 I(y i =c k ) 。。。(4)
经过上述步骤,我们就得到了模型的基本概率,也就完成了学习的任务。
分类 1.计算该实例属于 y=c k 类的概率
p(y=c k |X)=p(y=c k )∏ j=1 n p(X (j) =x (j) |y=c k )。。。(5)
分类 2.确定该实例所属的分类 y
y=argmax c k p(y=c k |X)。。。。(6)
于是我们得到了新实例的分类结果
p(y=c k )=∑ N i=1 I(y i =c k )+λN+Kλ 。。。。(7)
K 是类的个数
p(x j =a jl |y=c k )=∑ N i=1 I(x j i =a jl ,y i =c k )+λ∑ N i=1 I(y i =c k )+L j λ 。。。(8)
L j 是第 j 维特征的最大取值
可以证明,改进以后的(7)(8)仍然是概率。平滑因子 λ=0 即为(3)(4)实现的最大似然估计,这时会出现在本节开始时提到的0概率问题;而 λ=1 则避免了0概率问题,这种方法被称为拉普拉斯平滑。
根据上面的算法流程,在这里实现一个句子极性划分的例子。所谓句子极性是指,句子所表达的情感色彩,例如积极/消极,这里(书里)使用的是侮辱性/非侮辱性。其实是什么类别不重要,只要给定有标签的训练数据,就可以得到分类模型。
下面简述实现思想和流程,给出代码。
初始化步,构建可以表征句子的特征向量(词汇表)。并根据这个特征向量,把训练集表征出来。从训练集中分离部分数据作为测试集。
学习步,计算 类的先验概率和 特征向量对应每一类的条件概率向量
分类步, 计算测试集中待分类句子在每一类的分类后验概率,取最大值作为其分类,并与给定标签比较,得到误分类率。
Python实现:
from numpy import * ''' 词汇表到向量的转换函数 ''' def loadDataSet(): postingList=[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'], ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'], ['my', 'dalmation', 'is', '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] # 1代表侮辱性文字,0代表正常言论 return postingList,classVec ''' 创建一个空集 ''' def createVocabList(dataSet): vocabSet = set([]) for document in dataSet: vocabSet = vocabSet | set(document) #创建两个集合的并集,创建不重复的单词向量 return list(vocabSet) ''' 创建一个其中所含元素都为0的向量 ''' def setOfWords2Vec(vocabList,inputSet): returnVec = [0] * len(vocabList) for word in inputSet: if word in vocabList: returnVec[vocabList.index(word)] = 1 else: print"the word: %s is not in my Vocabulary!" %word return returnVec ''' trainMatrix=[0,1,0,0,0,....,0,1,0,1],trainCategory=[0,1,0,1,0,1] ''' ''' 朴素贝叶斯分类器训练函数 ''' def trainNB0(trainMatrix,trainCategory): numTrainDocs = len(trainMatrix) numWords = len(trainMatrix[0]) pAbusive = sum(trainCategory)/float(numTrainDocs) p0Num = ones(numWords); p1Num = ones(numWords) #change to ones() p0Denom = 2.0; p1Denom = 2.0 #change to 2.0 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 = log(p1Num/p1Denom) #change to log() p0Vect = log(p0Num/p0Denom) #change to log() return p0Vect,p1Vect,pAbusive ''' 朴素贝叶斯分类函数 ''' def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1): p1 = sum(vec2Classify * p1Vec) + log(pClass1) p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1) if p1 > p0: return 1 else: return 0 def testingNB(): listOPosts,listClasses = loadDataSet() myVocabList = createVocabList(listOPosts) trainMat = [] for postinDoc in listOPosts: trainMat.append(setOfWords2Vec(myVocabList,postinDoc)) p0V,p1V,pAb = trainNB0(array(trainMat),array(listClasses)) testEntry = ['love','my','dalmation'] thisDoc = array(setOfWords2Vec(myVocabList,testEntry)) print testEntry,'classified as:',classifyNB(thisDoc,p0V,p1V,pAb) testEntry = ['stupid','garbage'] thisDoc = array(setOfWords2Vec(myVocabList,testEntry)) print testEntry,'classified as:',classifyNB(thisDoc,p0V,p1V,pAb) def bagOfWords2Vec(vocabList,inputSet): returnVec = [0] * len(vocabList) for word in inputSet: if word in vocabList: returnVec[vocabList.index(word)] += 1 return returnVec ''' 文件解析及完整的垃圾邮件测试函数 ''' def textParse(bigString): import re listOfTokens = re.split(r'\W*',bigString) return [tok.lower() for tok in listOfTokens if len(tok) > 2] def spamTest(): docList = []; classList = []; fullText = [] for i in range(1,26): wordList = textParse(open('email/spam/%d.txt' % i).read()) docList.append(wordList) fullText.extend(wordList) classList.append(1) wordList = textParse(open('email/ham/%d.txt' % i).read()) docList.append(wordList) fullText.extend(wordList) classList.append(0) ''' 创建不重复词的列表 ''' vocabList = createVocabList(docList) ''' 定义训练样本和测试样本,以便进行交叉验证 ''' trainingSet = range(50); testSet = [] ''' 随机选取10个电子邮件作为测试集 ''' for i in range(10): ''' 在0~50之间随机选取一个数 ''' randIndex = int(random.uniform(0,len(trainingSet))) ''' 将选取的值放在测试集testSet中 ''' testSet.append(trainingSet[randIndex]) ''' 在训练集合中删除作为测试集的部分 ''' del(trainingSet[randIndex]) trainMat = []; trainClasses = [] for docIndex in trainingSet: ''' 输入参数为词汇表及其某个文档,输出的是文档向量,向量的每一个元素是1或0, 分别表示词汇表中的单词在输入文档中是否出现''' trainMat.append(setOfWords2Vec(vocabList, docList[docIndex])) trainClasses.append(classList[docIndex]) ''' trainMat是文档矩阵,trainClasses是每篇文章类别标签所构成的向量 ''' p0V,p1V,pSpam = trainNB0(array(trainMat),array(trainClasses)) errorCount = 0 for docIndex in testSet: wordVector = setOfWords2Vec(vocabList, docList[docIndex]) if classifyNB(array(wordVector),p0V,p1V,pSpam) != classList[docIndex]: errorCount += 1 print 'the error rate is: ',float(errorCount)/len(testSet)