机器学习 _ 统计模型之(三)朴素贝叶斯

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) 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# -*- 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) 运行结果

1
2
(['love', 'my', 'dalmation'], 'classified as: ', 0)
(['stupid', 'garbage'], 'classified as: ', 1)

3. sklearn 中的工具

上例只为说明原理,一般使用朴素贝叶斯的时候都是调库,sklearn 中提供以下常用工具:

GaussianNB:先验为高斯分布的朴素贝叶斯,用于连续型变量。

MultinomialNB:先验为多项式分布的朴素贝叶斯,多用于文本分类,统计出现次数。partial_fit() 方法可以进行多次训练。

BernoulliNB:先验为伯努利分布(二值分布)的朴素贝叶斯,变量是布尔型的。

4. 用途

朴素贝叶斯常用于信息检索,文本分类,识别垃圾邮件等领域。

5. 一些想法

说一些自己的想法,也不一定对。

简单地说朴素贝叶斯就是按概率求合,然后比一下是正例的概率大还是反例的概率大。

从贝叶斯网络的角度看,条件独立性去掉了网络中所有的依赖连接,把网络结构简化成了单层,是一个非常简单的模型,就像线性拟合似的,所以外理不了一个场景里的多种模式,如需处理,只能用集成模型组合多个简单模型。直觉上,朴素贝叶斯更适合数据多,属性多,分层少的应用。说它经典不是因为它功能强,而是因为其它足够简单和基础,可以作为组合的基本单元。

它有一种用法是处理稀疏的特征,比如说用它处理文本,需要先去掉停用词(有点像减掉均值),否则有意义的信息会被巨量的无意义信息吞没。

朴素贝叶斯也并不一定用于分类或回归,也可用于分析导致结果的重要条件,比如分别给正反例中的出现的属性按频率排序。间接地提炼有意义的特征,有点像从常识中把反常的点挑出来。