1.基于贝叶斯决策理论的分类方法
朴素贝叶斯是贝叶斯决策理论的一部分,所以讲述朴素贝叶斯之前有必要快速了解一下贝叶斯决策理论。
假设现在我们有一个数据集,它由两类数据组成,数据分布如图:
我们现在用p1(x,y)表示数据点(x,y)属于类别1(图中圆点表示的类别)的概率,用p2(x,y)表示数据点(x,y)属于类别2(图中三角形表示的类别)的概率,那么对于一个新数据点(x,y),可以用以下规则判断它的类别:
- 如果p1(x,y) > p2(x,y),那么类别为1。
- 如果p1(x,y) < p2(x,y),那么类别为2。
也就是说,我们会选择高概率对应的类别。这就是贝叶斯决策理论的核心思想,即选择具有最高概率的决策。
接下来,我们要详述p1及p2概率计算方法,下面将介绍条件概率。
2.条件概率
3.使用条件概率来分类
使用贝叶斯准则,可以通过已知的三个概率值来计算未知的概率值。后面就会给出利用贝叶斯准则来计算概率并对数据进行分类的代码。下一节将介绍一个简单但功能强大的贝叶斯分类器的应用案例。
4.使用朴素贝叶斯进行文档分类
机器学习的一个重要应用就是文档的自动分类。
在文档分类中,整个文档(如一封电子邮件)是实例,而电子邮件中的某些元素则构成特征。我们可以观察文档中出现的词,并把每个词的出现或者不出现作为一个特征,这样得到的特征数目就会跟词汇表中的词目一样多。朴素贝叶斯是贝叶斯分类器的一个拓展,是用于文档分类的常用算法。
假设词汇表中由1000个单词。要得到好的概率分布,就需要足够的数据样本,假定样本数为N。由统计学,如果每个特征需要N个样本,那么对于10个特征将需要N10个样本,对于包含1000个特征的词汇表将需要N^1000个样本(这个地方有点不太明白)。
如果特征之间相互独立,那么样本数就可以从N^1000减少到1000*N。所谓独立指的是统计意义上的独立,即一个特征或者单词出现的可能性与它和其他单词相邻没有关系。
5.使用python进行文本分类
要从文本中获取特征,需要先拆分文本。
具体如何做呢?这里的特征是来自文本的词条,一个词条是字符的任意组合。可以把词条想象为单词,也可以使用非单词词条,如URL、IP地址或者其他任意字符串。然后将每一个文本片段表示为一个词条向量,其中值为1表示词条出现在文档中,0表示词条未出现。
接下来首先给出将文本转换为数字向量的过程,然后介绍如何基于这些向量来计算条件概率,并在此基础上构建分类器,最后还要介绍一些利用python实现朴素贝叶斯过程中需要考虑的问题。
(1).准备数据:从文本中构建词向量
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]
return postingList,classVec
def createVocabList(dataSet):
vocabSet=set([])
for document in dataSet:
vocabSet=vocabSet | set(document)
return list(vocabSet)
def set0fWords2Vec(vocabList,inputSet):
returnVec=[0]*len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)]=1
else:
print("the word"+word+"is not in my Vocabulary!")
return returnVec
listOPosts,listClasses=loadDataSet()
myVocabList=createVocabList(listOPosts)
myVocabList
Out[7]:
['please',
'cute',
'love',
'quit',
'my',
'ate',
'park',
'to',
'food',
'so',
'not',
'help',
'mr',
'licks',
'problems',
'stop',
'steak',
'dog',
'how',
'dalmation',
'has',
'him',
'maybe',
'is',
'posting',
'worthless',
'garbage',
'flea',
'I',
'buying',
'stupid',
'take']
set0fWords2Vec(myVocabList,listOPosts[0])
Out[8]:
[1,
0,
0,
0,
1,
0,
0,
0,
0,
0,
0,
1,
0,
0,
1,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0]
(2).训练算法:从词向量计算概率
from numpy import *
def trainNB0(trainMatrix,trainCategory):
"""
分类器训练函数
- - - -
trainMatrix - 训练文档矩阵
trainCategory - 训练类别标签向量,即classVec
"""
#训练的文档数目
numTrainDocs =len(trainMatrix)
#词条数
numWords = len(trainMatrix[0])
#文档属于侮辱类的概率
pAbusive = sum(trainCategory)/float(numTrainDocs)
#初始化概率
p0Num = np.zeros(numWords)
p1Num = np.zeros(numWords)
p0Denom = 0.0
p1Denom = 0.0
for i in range(numTrainDocs):
#统计属于侮辱类的条件概率所需的数据,即P(wi|1)
if trainCategory[i] == 1:
p1Num += trainMatrix[i]
p1Denom += sum(trainMatrix[i])
#统计属于非侮辱类的条件概率所需的数据,即P(wi|0)
else:
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
#对每个元素做除法
p1Vect = p1Num/p1Denom
p0Vect = p0Num/p0Denom
return p0Vect,p1Vect,pAbusive
(3).测试算法:根据实际情况修改分类器
利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,如果其中一个概率值为0,那么最后的乘积也为0。
为降低这种影响,可以将所有词的出现数初始化为1,并将分母初始化为2
将trainNB0()对应部分修改为:
p0Num = np.ones(numWords)
p1Num = np.ones(numWords)
p0Denom = 2.0
p1Denom = 2.0
另一个遇到的问题是下溢出,这是由于太多很小的数相乘造成的。
一种解决办法是对乘积取自然对数。在代数中有ln(a*b) = ln(a)+ln(b),于是通过求对数可以避免下溢出或者浮点数舍入导致的错误。
将trainNB0()对应部分修改为:
p1Vect = np.log(p1Num/p1Denom)
p0Vect = np.log(p0Num/p0Denom)
最后分类和测试函数如下:
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
"""
朴素贝叶斯分类器分类函数
- - - -
vec2Classify - 待分类的词条数组
p0Vec - 侮辱类的条件概率数组
p1Vec -非侮辱类的条件概率数组
pClass1 - 文档属于侮辱类的概率
"""
p1 = np.sum(vec2Classify * p1Vec) + np.log(pClass1)
p0 = np.sum(vec2Classify * p0Vec) + np.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(np.array(trainMat),np.array(listClasses))
#测试样本1
testEntry = ['love', 'my', 'dalmation']
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
print (testEntry,'classified as:' ,classifyNB(thisDoc,p0V,p1V,pAb))
#测试样本2
testEntry = ['stupid', 'garbage']
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
print (testEntry,'classified as:' ,classifyNB(thisDoc,p0V,p1V,pAb))
#['love', 'my', 'dalmation'] classified as: 0
#['stupid', 'garbage'] classified as: 1
(5).准备数据:文档词袋模型
目前为止,我们将每个词的出现与否作为一个特征,这可以被描述为词集模型(set-of-wordsmodel)。
如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现在文档中所不能表达的某种信息,这种方法被称为词袋模型(bag-of-words model)。
下面给出基于词袋模型的朴素贝叶斯代码。它与函数setOfWords2Vec()几乎完全相同,唯一不同的是每当遇到一个单词时,它会增加词向量中的对应值,而不只是将对应的数值设为1。
def bagOfWords2VecMN(vocabList, inputSet):
"""
根据词汇表,将inputSet向量化(词袋化)
- - - -
vocabList - 词汇表
inputSet - 切分的词条列表
"""
returnVec = [0]*len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] += 1
return returnVec
小结
还有过滤垃圾邮件实例没有看,未完待续