朴素贝叶斯法
前面介绍过KNN算法和感知机算法都要求分类器对于给定新数据给出明确的分类结果,现在如要求不仅给出确定类别,还要给出猜测的概率估计值,之前的方法则行不通;而朴素贝叶斯方法就是基于概率论的分类方法,在分类器给出一个最优的类别猜测结果,同时给出这个猜测的概率估计值。
什么是朴素贝叶斯法?
朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法,对于给定的训练集,首先基于特征条件独立假设学习输入/输出的联合概率分布;然后基于此模型,对于给定的输入x,利用贝叶斯定理求出后验概率最大的输出y.朴素贝叶斯法实现简单,学习与预测的效率都很高,但是当特征之间的相关性很大时,效果不是很大。
在具体了解算法的实现过程之前,首先要明白涉及到的概率论知识
条件概率:http://zh.wikipedia.org/wiki/%E6%9D%A1%E4%BB%B6%E6%A6%82%E7%8E%87
表示在事件B发生的条件下,事件A发生的概率。
先验概率:http://zh.wikipedia.org/wiki/%E5%85%88%E9%AA%8C%E6%A6%82%E7%8E%87
后验概率:http://zh.wikipedia.org/wiki/%E5%90%8E%E9%AA%8C%E6%A6%82%E7%8E%87
全概率公式:
贝叶斯公式(逆概率公式):
以上知识在概率论课本中有详细的介绍。
朴素贝叶斯分类实现过程
step1:这个阶段的任务是为朴素贝叶斯分类做必要的准备,主要工作是根据具体情况确定特征属性,并对每个特征属性进行适当划分,然后由人工对一部分待分类项进行分类,形成训练样本集合。这一阶段的输入是所有待分类数据,输出是特征属性和训练样本。这一阶段是整个朴素贝叶斯分类中唯一需要人工完成的阶段,其质量对整个过程将有重要影响,分类器的质量很大程度上由特征属性、特征属性划分及训练样本质量决定。
step2:分类器训练阶段,这个阶段的任务就是生成分类器,主要工作是计算每个类别在训练样本中的出现频率及每个特征属性划分对每个类别的条件概率估 计,并将结果记录。其输入是特征属性和训练样本,输出是分类器。这一阶段是机械性阶段,根据前面讨论的公式可以由程序自动计算完成。
step3:这个阶段的任务是使用分类器对待分类项进行分类,其输入是分类器和待分类项,输出是待分类项与类别的映射关系。这一阶段也是机械性阶段,由程序完成。
简单言之
1、对于给定的训练数据求出先验概率和条件概率;
2、给定新的数据,算出其后验概率,去其中较大的概率所对应的类别。
在分类过程中计算先验概率:
k = 1,2,…k
条件概率:
由于假设特征直接是独立的所以可以直接求出
当个特征的条件概率,
后验概率
在程序中实现也就是如何实现这些,之外就是特征的处理。
具体的实际应用
1、文本分类
2、过滤垃圾邮件
根据机器学习实战里面的python实现关于文本分类的代码如下
'''
将文本转换为单词向量,这里要主要文本的编码方式,不然由于工具限制会出现错误
'''
from numpy import *
# -*- coding: utf-8 -*-
import sys
default_encoding = 'utf-8'
if sys.getdefaultencoding() != default_encoding:
reload(sys)
sys.setdefaultencoding(default_encoding)
# ==================准备数据,从文本中构建词向量================
#下面三个函数主要功能:词表到向量的转换
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 #样本,前者是进行词条切分后的文档集合,文本集被切分成一系列的词条集合,
# 其中标点符号从文本中去掉, 如何处理的这里暂时不讨论。 后者: 文本对应的类
#根据loadDataSet()第一个返回值:
#创建一个包含在所以文本中出现的不重复词的列表;
#
def createVocabList(dataSet):
vocabSet = set([]) #create empty set 集合产生不重复的词
for document in dataSet: #document 表示某个文本对应的词条向量
vocabSet = vocabSet | set(document) #union of the two sets
return list(vocabSet) #最后集合转换成列表 最后的词汇表 没有排序
#输入:词汇表和某个文档(已经处理的对应向量) 输出:文档向量,向量的每一元素1 或 0 分别表示在文档出现或者不出现
def setOfWords2Vec(vocabList, inputSet):
returnVec = [0]*len(vocabList) #为0 与词汇表等长的向量
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
# ========================================================
#===============训练算法:从词向量计算概率 ====================
# 朴素贝叶斯分类器训练函数
def trainNB0(trainMatrix,trainCategory): #输入:文本矩阵 和 对应的标签向量
numTrainDocs = len(trainMatrix) # 文本数目
numWords = len(trainMatrix[0]) # 单词数量
pAbusive = sum(trainCategory)/float(numTrainDocs) # 由于类别 1 和 0 所以就是
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: # 计算类别为1 的概率 p1 p0 = 1 - p1
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
#====================上面的分类是统计了特征的出现个数,而不仅仅是出现而已===
#====在实际分类之前,还需解决函数中的一些缺陷
# 1、某个概率为0; 2、 下溢出,很多很小的数乘积会近似0 取对数; 贝叶斯估计
#
#
#======朴素贝叶斯分类函数=====
#参数:要分类的向量; 三个概率向量 P(y=1)*P(x1|y=1)*P(x2|y=1) 等价 下面转换成了log就是相加
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
p1 = sum(vec2Classify * p1Vec) + log(pClass1) #element-wise mult
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 bagOfWords2VecMN(vocabList, inputSet):
returnVec = [0]*len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] += 1
return returnVec
# 测试算法:使用朴素贝叶斯进行交叉验证
# 文本解析及完整的垃圾邮件测试函数
#将大字符串解析为字符串列表;该函数去掉了小于两个字符的字符串;并且全部小写;还可以更多操作
def textParse(bigString): #input is big string, #output is word list
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,encoding= 'gbk').read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(1)
wordList = textParse(open('email/ham/%d.txt' % i,'r').read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
vocabList = createVocabList(docList)#create vocabulary
trainingSet = list(range(50)); #需要转换成list类型,
print ('\n',trainingSet ,'\n')
testSet=[] #create test set
for i in range(10):
randIndex = int(random.uniform(0,len(trainingSet)))
testSet.append(trainingSet[randIndex])
del(trainingSet[randIndex]) ##=============
trainMat=[]; trainClasses = []
for docIndex in trainingSet:#train the classifier (get probs) trainNB0
trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))
trainClasses.append(classList[docIndex])
p0V,p1V,pSpam = trainNB0(array(trainMat),array(trainClasses))
errorCount = 0
for docIndex in testSet: #classify the remaining items
wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])
if classifyNB(array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
errorCount += 1
print ("classification error",docList[docIndex])
print ('the error rate is: ',float(errorCount)/len(testSet))
#return vocabList,fullText
其他参考资料:
http://www.cnblogs.com/leoo2sk/archive/2010/09/17/naive-bayesian-classifier.html
http://my.oschina.net/u/1247611/blog/158887
http://www.ruanyifeng.com/blog/2013/12/naive_bayes_classifier.html