首先来简单介绍一下贝叶斯分类吧,贝叶斯分类的基础是概率比较的分类方式,假设给定一个数据集x,x类别为1的概率记做p1(x),x为2的概率记做p2(x),如果p1(x) > p2(x),则预测x的类别为1,否则预测x的类别为2.大概的道理就是这样,计算p1(x)和p2(x)的方式会复杂一些,需要引入贝叶斯概率,这就是朴素贝叶斯分类器的原理。朴素贝叶斯的优点在于其可以在数据较少的情况下已然可以处理多类别分类问题。他的缺点也很明显,就是对输入数据的准备方式较为敏感。
贝叶斯分类的数学原理
贝叶斯概率又叫条件概率,顾名思义,就是在一些先验条件发生的基础上的事件出现概率,本文就不再形象距离来说明条件概率的含义了,直接给出条件概率的公式:$$p(c|x) = \frac{p(x|c)p(c)}{p(x)}$$相信这个公式大家都见过,就是不太容易记住,其实可以这样想,把这个概率公式等号右边的分母乘到等号左边去,就变成了:$$p(c|x)p(x) = p(x|c)p(c)$$这时等号左边和右边都表示$p(cx)$,也就是cx都发生的概率。有了这个原理,就可以把上一节中提到的p1(x) > p2(x)替换成$p(c_{1}|x)>p(c_{2}|x)$,如果成立则x属于$c_{1}$类,否则属于$c_{2}$类。
接下来,就可以把原理付诸实践,使用朴素贝叶斯分类器完成一个文本分类的任务了。
使用Python进行文本分类
要从文本中提取特征,需要先将文本转换成一个one hot向量,对照我们建立的字典,如果改词的位置为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 is abusive, 0 not
return postingList,classVec
def createVocabList(dataSet): #构建字典
vocabSet = set([])
for document in dataSet:
vocabSet = vocabSet | set(document)
return list(vocabSet)
def setOfWord2Vec(vocabList, inputSet): #生成文本向量
returnVec = [0] * len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] = 1
else:
print("word %s is not in list" % word)
return returnVec
可以简单测试一下上面的代码:
if __name__ == "__main__":
listPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listPosts)
print(setOfWord2Vec(myVocabList, listPosts[0]))
如果能够得到下面的结果,就说明代码没问题了:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0]
第二个步骤就是依据文本向量设计训练算法,从词向量计算概率。将之前说到的x替换为w,w表示词组向量,则贝叶斯公式变为:$$p(c|w) = \frac{p(w|c)p(c)}{p(w)}$$计算p(w|c)时,就用到了朴素贝叶斯假设,也就是假设所有的词汇互相独立,那就意味着可以使用p(w0|ci)p(w1|ci)...p(wn|ci)计算,有了这个基础,代码写起来就简单了:
def trainNB0(trainMatrix, trainCategory):
numTrainDocs = len(trainMatrix)
numWords = len(trainMatrix[0])
pAbusive = sum(trainCategory) / float(numTrainDocs)
p1num = ones(numWords)
p0num = ones(numWords)
p0Tot = 2.0
p1Tot = 2.0
for i in range(numTrainDocs):
if trainCategory[i] == 1:
p1num += trainMatrix[i]
p1Tot += sum(trainMatrix[i])
else:
p0num += trainMatrix[i]
p0Tot += sum(trainMatrix[i])
p0Vec = log(p0num / p0Tot)
p1Vec = log(p1num / p1Tot)
return p0Vec, p1Vec, pAbusive
def classifyNB(vec2classify, p0Vec, p1Vec, pClass1):
p0 = sum(vec2classify * p0Vec) + log(pClass1)
p1 = sum(vec2classify * p1Vec) + log(1 - pClass1)
if p1 > p0:
return 1
else:
return 0
测试一下这个代码吧:
if __name__ == "__main__":
listPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listPosts)
print(setOfWord2Vec(myVocabList, listPosts[0]))
trainMat = []
for postingDoc in listPosts:
trainMat.append(setOfWord2Vec(myVocabList, postingDoc))
p0V, p1V, pAb = trainNB0(trainMat, listClasses)
testEntry = ['love', 'my', 'dalmation']
thisDoc = array(setOfWord2Vec(myVocabList, testEntry))
print(testEntry, 'classified as :', classifyNB(thisDoc, p0V, p1V, pAb))
testEntry = ['garbage', 'stupid', 'licks']
thisDoc = array(setOfWord2Vec(myVocabList, testEntry))
print(testEntry, 'classified as :', classifyNB(thisDoc, p0V, p1V, pAb))
得到下面的结果,就算是又成功了一点了:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0]
['love', 'my', 'dalmation'] classified as : 0
['garbage', 'stupid', 'licks'] classified as : 1