1贝叶斯算法简介
贝叶斯分类算法是统计学的一种分类方法,它是一类利用概率统计知识进行分类的算法。在许多场合,朴素贝叶斯(Naïve Bayes,NB)分类算法可以与决策树和神经网络分类算法相媲美,该算法能运用到大型数据库中,而且方法简单、分类准确率高、速度快。
1.1贝叶斯算法优点优点:在数据较少时仍有效,可处理多类别问题
1.2贝叶斯算法缺点缺点:对输入数据准备方式敏感,如果输入的数据的各个特征之间是具有关联的,那么分类的效果可能不佳,反之,如果各个特征之间的关联度不大,则分类效果才可能不错
1.3贝叶斯算法思想假设现在我们有一个数据集,它由两类数据组成,数据分布如上图所示:
我们现在用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
也就是说,我们会选择高概率对应的类别。这就是贝叶斯决策理论的核心思想,即选择具有最高概率的决策。
适用决策树不会非常成功,和简单的概率计算相比,KNN计算量太大,因此对于上述问题,最佳选择是概率比较方法。
接下来,就是学习如何计算p1和p2概率。
2.概率公式 2.1 条件概率条件概率(Condittional probability),就是指在事件B发生的情况下,事件A发生的概率,用P(A|B)来表示。
2.2 全概率 3 朴素贝叶斯定理 3.1贝叶斯推导对条件概率进行变形:
我们把P(A)称为”先验概率”(Prior probability),即在B事件发生之前,我们对A事件概率的一个判断。
P(A|B)称为”后验概率”(Posterior probability),即在B事件发生之后,我们对A事件概率的重新评估。
P(B|A)/P(B)称为”可能性函数”(Likelyhood),这是一个调整因子,使得预估概率更接近真实概率。
所以,条件概率可以理解成下面的式子:
后验概率=先验概率∗调整因子后验概率=先验概率∗调整因子
这就是贝叶斯推断的含义:我们先预估一个”先验概率”,然后加入实验结果,看这个实验到底是增强还是削弱了”先验概率”,由此得到更接近事实的”后验概率”。
在这里,如果”可能性函数”P(B|A)/P(B)>1,意味着”先验概率”被增强,事件A的发生的可能性变大;如果”可能性函数”=1,意味着B事件无助于判断事件A的可能性;如果”可能性函数”<1,意味着”先验概率”被削弱,事件A的可能性变小。
3.2朴素贝叶斯概念
“朴素”的解释:假设各个特征之间相互独立(在贝叶斯分类器上做了简化)
朴素贝叶斯的基础假设:
①每个特征相互独立;
②每个特征的权重(或重要性)都相等,即对结果的影响程度都相同。
4.1词表到向量转换函数
def loadDataSet():
"""
Function: 创建实验样本
Args: 无
Returns: postingList:词条切分后的文档集合
classVec:类别标签的集合
"""
#词条切分后的文档集合
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):
"""
Function: 创建一个包含所有文档中出现的不重复词的列表
Args: dataSet:数据集
Returns: list(vocabSet):返回一个包含所有文档中出现的不重复词的列表
"""
#创建一个空集
vocabSet = set([])
#将新词集合添加到创建的集合中
for document in dataSet:
#操作符 | 用于求两个集合的并集
vocabSet = vocabSet | set(document)
#返回一个包含所有文档中出现的不重复词的列表
return list(vocabSet)
def setOfWords2Vec(vocabList, inputSet):
"""
Function: 词表到向量的转换
Args: vocabList:词汇表
inputSet:某个文档
Returns: returnVec:文档向量
"""
#创建一个所含元素都为0的向量
returnVec = [0]*len(vocabList)
#遍历文档中词汇
for word in inputSet:
#如果文档中的单词在词汇表中,则相应向量位置置1
if word in vocabList:
returnVec[vocabList.index(word)] = 1
#否则输出打印信息
else: print("the word: %s is not in my Vocablary!" % word)
#向量的每一个元素为1或0,表示词汇表中的单词在文档中是否出现
return returnVec
4.2朴素贝叶斯分类器训练函数
def trainNB0(trainMatrix, trainCategory):
"""
Function: 朴素贝叶斯分类器训练函数
Args: trainMatrix:文档矩阵
trainCategory:类别标签向量
Returns: p0Vect:非侮辱性词汇概率向量
p1Vect:侮辱性词汇概率向量
pAbusive:侮辱性文档概率
"""
#获得训练集中文档个数
numTrainDocs = len(trainMatrix)
#获得训练集中单词个数
numWords = len(trainMatrix[0])
#计算文档属于侮辱性文档的概率
pAbusive = sum(trainCategory)/float(numTrainDocs)
#初始化概率的分子变量
p0Num = zeros(numWords); p1Num = zeros(numWords)
#初始化概率的分母变量
p0Denom = 0.0; p1Denom = 0.0
#遍历训练集trainMatrix中所有文档
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 = p1Num/p1Denom
p0Vect = p0Num/p0Denom
#返回两个类别概率向量和一个概率
return p0Vect, p1Vect, pAbusive
4.3朴素贝叶斯分类函数
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
"""
Function: 朴素贝叶斯分类函数
Args: vec2Classify:文档矩阵
p0Vec:非侮辱性词汇概率向量
p1Vec:侮辱性词汇概率向量
pClass1:侮辱性文档概率
Returns: 1:侮辱性文档
0:非侮辱性文档
"""
#向量元素相乘后求和再加到类别的对数概率上,等价于概率相乘
p1 = sum(vec2Classify * p1Vec) + log(pClass1)
p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
#分类结果
if p1 > p0:
return 1
else:
return 0
def testingNB():
"""
Function: 朴素贝叶斯分类器测试函数
Args: 无
Returns: testEntry:测试词汇列表
classifyNB(thisDoc, p0V, p1V, pAb):分类结果
"""
#从预先加载中调入数据
listOPosts, listClasses = loadDataSet()
#构建一个包含所有词的列表
myVocabList = createVocabList(listOPosts)
#初始化训练数据列表
trainMat = []
#填充训练数据列表
for postinDoc in listOPosts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
#训练
p0V, p1V, pAb = trainNB0(trainMat, 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))
4.4构建词袋模型
def bagOfWords2VecMN(vocabList, inputSet):
"""
Function: 词袋到向量的转换
Args: vocabList:词袋
inputSet:某个文档
Returns: returnVec:文档向量
"""
#创建一个所含元素都为0的向量
returnVec = [0]*len(vocabList)
#将新词集合添加到创建的集合中
for word in inputSet:
#如果文档中的单词在词汇表中,则相应向量位置加1
if word in vocabList:
returnVec[vocabList.index(word)] += 1
#返回一个包含所有文档中出现的词的列表
return returnVec
4.5准备数据验证
def textParse(bigString):
"""
Function: 切分文本
Args: bigString:输入字符串
Returns: [*]:切分后的字符串列表
"""
import re
#利用正则表达式,来切分句子,其中分隔符是除单词、数字外的任意字符串
listOfTokens = re.split(r'W*', bigString)
#返回切分后的字符串列表
return [tok.lower() for tok in listOfTokens if len(tok) > 2]
def spamTest():
"""
Function: 贝叶斯垃圾邮件分类器
Args: 无
Returns: float(errorCount)/len(testSet):错误率
vocabList:词汇表
fullText:文档中全部单词
"""
#初始化数据列表
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)
#切分文本
#print('i = :', i)
wordList = textParse(open('email/ham/%d.txt' % i).read())
#切分后的文本以原始列表形式加入文档列表
docList.append(wordList)
#切分后的文本直接合并到词汇列表
fullText.extend(wordList)
#标签列表更新
classList.append(0)
#创建一个包含所有文档中出现的不重复词的列表
vocabList = createVocabList(docList)
#初始化训练集和测试集列表
trainingSet = list(range(50)); testSet = []
#随机构建测试集,随机选取十个样本作为测试样本,并从训练样本中剔除
for i in range(10):
#随机得到Index
randIndex = int(random.uniform(0, len(trainingSet)))
#将该样本加入测试集中
testSet.append(trainingSet[randIndex])
#同时将该样本从训练集中剔除
del(trainingSet[randIndex])
#初始化训练集数据列表和标签列表
trainMat = []; trainClasses = []
#遍历训练集
for docIndex in trainingSet:
#词表转换到向量,并加入到训练数据列表中
trainMat.append(setOfWords2Vec(vocabList, docList[docIndex]))
#相应的标签也加入训练标签列表中
trainClasses.append(classList[docIndex])
#朴素贝叶斯分类器训练函数
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]:
#如果不一致则错误计数加1
errorCount += 1
#并且输出出错的文档
print("classification error",docList[docIndex])
#打印输出信息
print('the erroe rate is: ', float(errorCount)/len(testSet))
#返回词汇表和全部单词列表
#return vocabList, fullText



