监督学习之概率模型

贝叶斯分类是一类分类算法的总称,这类算法均以贝叶斯定理为基础,故统称为贝叶斯分类。而朴素朴素贝叶斯分类是贝叶斯分类中最简单,也是常见的一种分类方法。

一、基本概念

1、贝叶斯定理

贝叶斯定理(英语:Bayes’ theorem)是概率论中的一个定理,它跟随机变量的条件概率以及边缘概率分布有关。在有些关于概率的解释中,贝叶斯定理(贝叶斯公式)能够告知我们如何利用新证据修改已有的看法。核心公式如下:

$$P(A|B) = \frac{P(B|A)P(A)}{P(B)}$$

通俗的讲解就是如果知道A事件,B事件分别发生的概率,还有在A事件发生时B事件发生的概率。根据上面公式就可以知道在B事件发生的情况下A事件发生的概率是多少。

2、理解贝叶斯定理

贝叶斯公式的深入理解:

  • 我们把P(A)称为”先验概率”,因为A还没有与事件B关联上。
  • 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的可能性变小。

我们先预估一个”先验概率”,然后加入实验结果,看这个实验到底是增强还是削弱了”先验概率”,由此得到更接近事实的”后验概率”。
所以,贝叶斯定理可以理解为

$$后验概率 = 先验概率 x 调整因子$$
$$P(A|B) = P(A)*\frac{P(B|A)}{P(B)}$$

利用贝叶斯公式计算真实患病概率
某病症的医院检查结果为阳性,该检查准确率99%,又知道该病症在人群中发病概率为万分之一。那么计算自己检查结果为阳性且确实有病的概率。

分母P(检查结果为阳性)=患病被查到+没患病被查到

为什么检查结果为阳性且确实有病的概率是小于百分之一呢?因为检查结果的误诊率相对患病率要高很多。

二、朴素贝叶斯分类算法

叫它朴素贝叶斯分类是因为这种方法的思想真的很朴素,朴素贝叶斯的思想基础是这样的:对于给出的待分类项,求解在此项出现的条件下各个类别出现的概率,哪个最大,就认为此待分类项属于哪个类别。通俗来说,就好比这么个道理,你在街上看到一个黑人,我问你你猜这哥们哪里来的,你十有八九猜非洲。为什么呢?因为黑人中非洲人的比率最高,当然人家也可能是美洲人或亚洲人,但在没有其它可用信息下,我们会选择条件概率最大的类别,这就是朴素贝叶斯的思想基础。

1、执行步骤

step 1:准备工作阶段,这个阶段的任务是为朴素贝叶斯分类做必要的准备,主要工作是根据具体情况确定特征属性,并对每个特征属性进行适当划分,然后由人工对一部分待分类项进行分类,形成训练样本集合。这一阶段的输入是所有待分类数据,输出是特征属性和训练样本。这一阶段是整个朴素贝叶斯分类中唯一需要人工完成的阶段,其质量对整个过程将有重要影响,分类器的质量很大程度上由特征属性、特征属性划分及训练样本质量决定。
step 2:分类器训练阶段,这个阶段的任务就是生成分类器,主要工作是计算每个类别在训练样本中的出现频率及每个特征属性划分对每个类别的条件概率估计,并将结果记录。其输入是特征属性和训练样本,输出是分类器。这一阶段是机械性阶段,根据前面讨论的公式可以由程序自动计算完成。
step 3:应用阶段。这个阶段的任务是使用分类器对待分类项进行分类,其输入是分类器和待分类项,输出是待分类项与类别的映射关系。这一阶段也是机械性阶段,由程序完成。

2、优缺点

(1)优点

  • 算法逻辑简单,易于实现
  • 分类过程中时空开销小(假设特征相互独立,只会涉及到二维存储)
  • 对缺失数据不敏感,在数据较少的情况下依然可以使用该方法

(2)缺点

  • 由于用先验数据去预测分类,因此存在误差
  • 要求特征变量关联性不强。如果特征变量的有些特征关联性较强或者特征之间的重要性不一样的时候效果就不是那么好。

3、代码实现

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# coding:utf-8
'''
朴素贝叶斯算法
'''
from numpy import *
from functools import reduce

# 广告、垃圾标识
adClass = 1
#========================================================
# 生成训练集
#========================================================

def createDataSet() :
wordsList = [['周六', '公司', '一起', '聚餐','时间'],
['优惠', '返利', '打折', '优惠', '金融', '理财'],
['喜欢', '机器学习', '一起', '研究', '欢迎', '贝叶斯', '算法', '公式'],
['公司', '发票', '税点', '优惠', '增值税', '打折'],
['北京', '今天', '雾霾', '不宜', '外出', '时间', '在家', '讨论', '学习'],
['招聘', '兼职', '日薪', '保险', '返利']]
# 1 是垃圾邮件, 0 不是垃圾邮件
calssList = [0, 1, 0, 1, 0, 1]
return wordsList, calssList

#========================================================
# 文本工具
#========================================================

def doc2VecList(docList) :
'''合并去重,生成包含所有单词的集合'''
return list(reduce(lambda x, y : set(x) | set(y), docList))

def words2Vec(vecList, inputWords) :
'''把单子转化为词向量'''
resultVec = [0] * len(vecList)
for word in inputWords :
if word in vecList :
# 在单词出现的位置上的计数加1
resultVec[vecList.index(word)] += 1
else :
print('没有发现此单词')

return array(resultVec)

#========================================================
# 朴素贝叶斯算法
#========================================================

def trainNB(trainMatrix, trainClass) :
'''计算,生成每个词对于类别上的概率'''
numTrainClass = len(trainClass)
numWords = len(trainMatrix[0])

# 全部都初始化为1, 防止出现概率为0的情况出现
p0Num = ones(numWords)
p1Num = ones(numWords)
# 相应的单词初始化为2
p0Words = 2.0
p1Words = 2.0
# 统计每个分类的词的总数
for i in range(numTrainClass) :
if trainClass[i] == 1 :
#数组在对应的位置上相加
p1Num += trainMatrix[i]
p1Words += sum(trainMatrix[i])
else :
p0Num += trainMatrix[i]
p0Words += sum(trainMatrix[i])
# 计算每种类型里面, 每个单词出现的概率
p0Vec = log(p0Num / p0Words)
p1Vec = log(p1Num / p1Words)
# 计算1出现的概率
pClass1 = sum(trainClass) / float(numTrainClass)
return p0Vec, p1Vec, pClass1

def classifyNB(testVec, p0Vec, p1Vec, pClass1) :
'''朴素贝叶斯分类, max(p0, p1)作为推断的分类'''
# y=x 是单调递增的, y=ln(x)也是单调递增的。 , 如果x1 > x2, 那么ln(x1) > ln(x2)
# 因为概率的值太小了,所以我们可以取ln, 根据对数特性ln(ab) = lna + lnb, 可以简化计算
# sum是numpy的函数
p1 = sum(testVec * p1Vec) + log(pClass1)
p0 = sum(testVec * p0Vec) + log(1 - pClass1)
if p0 > p1 :
return 0
return 1

def printClass(words, testClass):
if testClass == adClass:
print(words, '推测为:广告邮件')
else:
print(words, '推测为:正常邮件')

#========================================================
# 主程序
#========================================================

if __name__ == '__main__' :
docList, classList = createDataSet()
# 生成词典
allWordsVec = doc2VecList(docList)

# 构建词向量矩阵
trainMat = list(map(lambda x : words2Vec(allWordsVec, x), docList))

# 训练计算每个词在分类上的概率, p0V:每个单词在非分类出现的概率, p1V:每个单词在是分类出现的概率
p0V, p1V, pClass1 = trainNB(trainMat, classList)
testWords = ['公司', '聚餐', '讨论', '贝叶斯']

# 当前需要预测的词向量
testVec = words2Vec(allWordsVec, testWords)
# 预测分类
testClass = classifyNB(testVec, p0V, p1V, pClass1)
printClass(testWords, testClass)

# 预测
testWords = ['公司', '保险', '金融']
testVec = words2Vec(allWordsVec, testWords)
testClass = classifyNB(testVec, p0V, p1V, pClass1)
printClass(testWords, testClass)
0%