本文主要描述了朴素贝叶斯分类方法,包括模型导出和学习描述。实例部分总结了《machine learning in action》一书中展示的一个该方法用于句子感情色彩分类的程序。1
根据贝叶斯定理,对一个分类问题,给定样本特征x,样本属于类别y的概率是
下面介绍如何从数据中,学习得到朴素贝叶斯分类模型。概述分类方法,并提出一个值得注意的问题。
学习 1.首先,我们来计算公式(2)中的 p(y=ck)
p(y=ck)=∑Ni=1I(yi=ck)N。。。。(3)
其中 I(x) 为指示函数,若括号内成立,则计1,否则为0。
学习 2.接下来计算分子中的条件概率,设 M 维特征的第 j 维有 L 个取值,则某维特征的某个取值 ajl ,在给定某分类 ck 下的条件概率为:
p(xj=ajl|y=ck)=∑Ni=1I(xji=ajl,yi=ck)∑Ni=1I(yi=ck)。。。(4)
经过上述步骤,我们就得到了模型的基本概率,也就完成了学习的任务。
分类 1.计算该实例属于 y=ck 类的概率
p(y=ck|X)=p(y=ck)∏j=1np(X(j)=x(j)|y=ck)。。。(5)
分类 2.确定该实例所属的分类 y
y=argmaxckp(y=ck|X)。。。。(6)
于是我们得到了新实例的分类结果
p(y=ck)=∑Ni=1I(yi=ck)+λN+Kλ。。。。(7)
K 是类的个数
p(xj=ajl|y=ck)=∑Ni=1I(xji=ajl,yi=ck)+λ∑Ni=1I(yi=ck)+Ljλ。。。(8)
Lj 是第 j 维特征的最大取值
可以证明,改进以后的(7)(8)仍然是概率。平滑因子 λ=0 即为(3)(4)实现的最大似然估计,这时会出现在本节开始时提到的0概率问题;而 λ=1 则避免了0概率问题,这种方法被称为拉普拉斯平滑。
根据上面的算法流程,在这里实现一个句子极性划分的例子。所谓句子极性是指,句子所表达的情感色彩,例如积极/消极,这里(书里)使用的是侮辱性/非侮辱性。其实是什么类别不重要,只要给定有标签的训练数据,就可以得到分类模型。
下面简述实现思想和流程,给出代码。
初始化步,构建可以表征句子的特征向量(词汇表)。并根据这个特征向量,把训练集表征出来。从训练集中分离部分数据作为测试集。
学习步,计算 类的先验概率和 特征向量对应每一类的条件概率向量
分类步, 计算测试集中待分类句子在每一类的分类后验概率,取最大值作为其分类,并与给定标签比较,得到误分类率。
初始化:
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)
def bagOfWord2VecMN(vocabList,inputSet):#根据词汇表,讲句子转化为向量
returnVec = [0]*len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] += 1
return returnVec
训练:
def trainNB0(trainMatrix,trainCategory):
numTrainDocs = len(trainMatrix)
numWords = len(trainMatrix[0])
pAbusive = sum(trainCategory)/float(numTrainDocs)
p0Num = ones(numWords);p1Num = ones(numWords)#计算频数初始化为1
p0Denom = 2.0;p1Denom = 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)#注意
p0Vect = log(p0Num/p0Denom)#注意
return p0Vect,p1Vect,pAbusive#返回各类对应特征的条件概率向量
#和各类的先验概率
分类:
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):
p1 = sum(vec2Classify * p1Vec) + log(pClass1)#注意
p0 = sum(vec2Classify * p0Vec) + log(1-pClass1)#注意
if p1 > p0:
return 1
else:
return 0
def testingNB():#流程展示
listOPosts,listClasses = loadDataSet()#加载数据
myVocabList = createVocabList(listOPosts)#建立词汇表
trainMat = []
for postinDoc in listOPosts:
trainMat.append(bagOfWord2VecMN(myVocabList,postinDoc))
p0V,p1V,pAb = trainNB0(trainMat,listClasses)#训练
#测试
testEntry = ['love','my','dalmation']
thisDoc = bagOfWord2VecMN(myVocabList,testEntry)
print testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb)
注意:上述代码中标有注意的地方,是公式中概率连乘变成了对数概率相加。此举可以在数学上证明不会影响分类结果,且在实际计算中,避免了因概率因子远小于1而连乘造成的下溢出。