1. 条件独立假设
条件独立假设简单的说就是特征x1和x2没有关系,比如说兔子的特征中,尾巴短和爱吃萝卜这两个特征它们分别和兔子相关,但两特征彼此之间无关,不是说尾巴短的都爱吃萝卜。所以有p(x2|x1)=p(x2),即无论x1是什么,x2的概率都不变。朴素贝叶斯就是以条件独立假设为基础的。
2. 概率的乘法
两个独立事件都发生的联合概率等于两个事件发生概率的乘积。
当P(A)与P(B)无关时,P(B)=P(B|A),所以P(A,B)=P(A)|P(B|A)=P(A)P(B)
比如:目标是判断该动物是不是兔子,有三个属性:长耳朵P(A)=70%,短尾巴P(B)=50%,爱吃萝卜P(C)=60%,当三个条件都为真时:
P(A,B,C)=P(A)P(B)P(C)=70%50%60%=21%,奇怪,三个条件都为真,结果概率却小了?概率小不小主要看跟谁比,再看看三个条件都不成立时:
(1-P(A))(1-P(B))(1-P(C))=30%50%40%=6%,对比这两个值21:6,就能判断它是一只兔子了。
3. 朴素贝叶斯
朴素贝叶斯(Naive Bayesian) 是贝叶斯分类器中应用最为广泛的算法之一, 之所以称为”朴素”是因为它有两个假设:特征之间相互独立,每个特征同等重要。
完整公式是:
前文说过它是贝叶斯网络的特殊情况:即网络中无边,各个节点都是独立的。
它的优点是:对缺失数据不太敏感,算法也比较简单,结果比较直观。而缺点是:基于假设属性之间相互独立,这个假设在实际应用中往往是不成立的,容易产生高度相关特征的双重计数.赋于更高的比重。所以我们尽量在数据降维(去相关性)之后,再使用它。
4. 例程
(1) 功能
通过训练判断句子的感情色彩
(2) 代码
# -*- coding: utf-8 -*-
from numpy import *
# 训练数据
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)
# 用词汇列表将训练集中的词转换成:句中存在为1,不存在为0
def setOfWords2Vec(vocabList, inputSet):
returnVec = [0]*len(vocabList)
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) # 正例在总数中占比
p0Num = ones(numWords); p1Num = ones(numWords) # p0Num是一个数组,大小是所有不重词数
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) #element-wise mult
p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
if p1 > p0:
return 1
else:
return 0
if __name__ == '__main__':
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))
(3) 运行结果
(['love', 'my', 'dalmation'], 'classified as: ', 0)
(['stupid', 'garbage'], 'classified as: ', 1)
3. sklearn中的工具
上例只为说明原理,一般使用朴素贝叶斯的时候都是调库,sklearn中提供以下常用工具:
GaussianNB:先验为高斯分布的朴素贝叶斯,用于连续型变量。
MultinomialNB:先验为多项式分布的朴素贝叶斯,多用于文本分类,统计出现次数。partial_fit()方法可以进行多次训练。
BernoulliNB:先验为伯努利分布(二值分布)的朴素贝叶斯,变量是布尔型的。
4. 用途
朴素贝叶斯常用于信息检索,文本分类,识别垃圾邮件等领域。
5. 一些想法
说一些自己的想法,也不一定对。
简单地说朴素贝叶斯就是按概率求和,然后比一下是正例的概率大还是反例的概率大。
从贝叶斯网络的角度看,条件独立性去掉了网络中所有的依赖连接,把网络结构简化成了单层,是一个非常简单的模型,就像线性拟合似的,所以外理不了一个场景里的多种模式,如需处理,只能用集成模型组合多个简单模型。直觉上,朴素贝叶斯更适合数据多,属性多,分层少的应用。说它经典不是因为它功能强,而是因为其它足够简单和基础,可以作为组合的基本单元。
它有一种用法是处理稀疏的特征,比如说用它处理文本,需要先去掉停用词(有点像减掉均值),否则有意义的信息会被巨量的无意义信息吞没。
朴素贝叶斯也并不一定用于分类或回归,也可用于分析导致结果的重要条件,比如分别给正反例中的出现的属性按频率排序。间接地提炼有意义的特征,有点像从常识中把反常的点挑出来。