原文链接:04 数据挖掘之朴素贝叶斯算法
01 数据挖掘之Apriori关联规则算法
02 数据挖掘之k均值聚类算法
03 数据挖掘之ID3决策树算法
照例,数据挖掘迎来了第四次实践作业,此次内容是贝叶斯的一些算法,在学习课程的过程中,觉得这些算法对以后也许会有些许帮助,因此在此作为笔记记录。
参考博客:Python3 实现朴素贝叶斯分类,朴素贝叶斯分类:原理
贝叶斯原理是英国数学家托马斯·贝叶斯提出的。贝叶斯是个很神奇的人,他的经历类似梵高。生前没有得到重视,死后,他写的一篇关于归纳推理的论文被朋友翻了出来,并发表了。这一发表不要紧,结果这篇论文的思想直接影响了接下来两个多世纪的统计学,是科学史上著名的论文之一。
首先引入一个概念:正向概率
。
正向概率:假设一个袋子里有10个白球和10个黑球,伸进手随机摸一个,摸出黑球的概率是多大呢?显然摸出黑球的概率是1/2,这就是正向概率问题。
但这种情况往往是上帝视角,即了解了事情的全貌再做判断。
与之对应的就是逆向概率问题
。在现实生活中,我们很难知道事情的全貌。贝叶斯则从实际场景出发,提了一个问题:如果我们事先不知道袋子里面黑球和白球的比例,而是通过我们摸出来的球的颜色,能判断出袋子里面黑白球的比例么?
逆向概率:事先我们不知道袋子里面黑球和白球的比例,而是通过我们摸出来的球的颜色,去判断出袋子里面黑白球的比例,这就是逆向概率问题。
实际上就是求在当前事件A发生的条件下,事件B发生的概率。
这里取出的球是黑球的概率是事件A,袋子里的黑球的比例是事件B,实际上所求的概率就是当已知取出黑球的概率的时候袋子里的黑球的比例,记作事件B∣A,也就是条件概率。
先上几个公式,数学里应该学过,如果忘记了也没关系,有例子会帮助理解。
已知某Play Tennis的训练样本集:
day | outlook | temperature | humidity | wind | Playtennis |
---|---|---|---|---|---|
1 | sunny | hot | high | weak | no |
2 | sunny | hot | high | strong | no |
3 | overcast | hot | high | weak | yes |
4 | rainy | mild | high | weak | yes |
5 | rainy | cool | normal | weak | yes |
6 | rainy | cool | normal | strong | no |
7 | overcast | cool | normal | strong | yes |
8 | sunny | mild | high | weak | no |
9 | sunny | cool | normal | weak | yes |
10 | rainy | mild | normal | weak | yes |
11 | sunny | mild | normal | strong | yes |
12 | overcast | mild | high | strong | yes |
13 | overcast | hot | normal | weak | yes |
14 | rainy | mild | high | strong | no |
求样本 X =
解:
设 每 个 类 的 类 别 事 件 为 C , 则 每 个 不 同 类 别 的 概 率 P ( C i ) 可 以 根 据 训 练 样 本 计 算 : 设每个类的类别事件为C,则每个不同类别的概率P(\mathop{{C}}\nolimits_{{i}})可以根据训练样本计算: 设每个类的类别事件为C,则每个不同类别的概率P(Ci)可以根据训练样本计算:
P(Playtennis="yes")=9/14=0.643
P(Playtennis="no")=5/14=0.357
为 计 算 P ( X ∣ C i ) , i = 1 , 2 , 则 需 要 先 计 算 下 面 的 条 件 概 率 : 为计算P(X|\mathop{{C}}\nolimits_{{i}}),i = 1,2,则需要先计算下面的条件概率: 为计算P(X∣Ci),i=1,2,则需要先计算下面的条件概率:
P(Outlook="Sunny" | Playtennis ="yes") = 2/9 = 0.222
P(Outlook="Sunny" | Playtennis ="no") = 3/5 = 0.600
P(Temperature="hot" | Playtennis ="yes") = 2/9 = 0.222
P(Temperature="hot" | Playtennis ="no") = 2/5 = 0.400
P(Humidity="hot" | Playtennis ="yes") = 3/9 = 0.333
P(Humidity="hot" | Playtennis ="no") = 4/5 = 0.800
P(Windy="Strong" | Playtennis ="yes") = 3/9 = 0.333
P(Windy="Strong" | Playtennis ="no") = 3/5 = 0.600
利用以上概率,可以得到:
P(X | Playtennis ="yes") = 0.222×0.222×0.333×0.333 = 0.005
P(X | Playtennis ="no") = 0.600×0.400×0.800×0.600 = 0.115
P(X | Playtennis ="yes")P(Playtennis = "yes") = 0.005×0.643 = 0.003
P(X | Playtennis ="no")P(Playtennis = "no") = 0.115×0.357 = 0.041
可以看到,P(X | Playtennis =“no”)P(Playtennis = “no”)的概率是最大的,且为0.041,所以应该将该样本X指派为类:
C 2 , P l a y t e n n i s = “ n o ” \mathop{{C}}\nolimits_{{2}},Playtennis = “no” C2,Playtennis=“no”
即,不去打球。
先通过一个示例引入,再来对一些概念进行描述,可能会更加容易理解。
也叫做先验概率
,也就是某个时间发生的概率。P(X=a)或P(Y=b),这类仅与单个随机变量有关的概率称为边缘概率。
在示例中,我们首先求的*P(Play tennis=“yes”)和P(Play tennis=“no”)*就是这里的边缘概率(先验概率)。
表示两个事件共同发生的概率。A与B的联合概率表示为P(A∩B)或者P(A,B)——有些课本写成P(AB)。
在示例中会发现并没有直接的体现出来,但其实在第二步求条件概率的时候的分子就是这里的联合概率。也不难理解,就是当左右两边的事件同时满足的概率,就是联合概率。
也叫做后验概率
,事件A在另外一个事件B已经发生条件下的发生概率,条件概率表示为P(A|B),读作“在B条件下A的概率”。
在示例中,第二步求的就是条件概率。
它是一种简单但极为强大的预测建模算法。之所以称为朴素贝叶斯,**是因为它假设每个输入变量是独立的。**这个假设很硬,现实生活中根本不满足,但是这项技术对于绝大部分的复杂问题仍然非常有效。
朴素贝叶斯模型由两种类型的概率组成:
1 、 每 个 类 别 的 概 率 P ( C j ) ; 2 、 每 个 属 性 的 条 件 概 率 P ( A i ∣ C j ) 。 1、每个类别的概率P(\mathop{{C}}\nolimits_{{j}}); \\ 2、每个属性的条件概率P(\mathop{{A}}\nolimits_{{i}} | \mathop{{C}}\nolimits_{{j}})。 1、每个类别的概率P(Cj);2、每个属性的条件概率P(Ai∣Cj)。
概念了解多了,就容易被整糊涂,看了qiu_zhi_liao的博客,发现一个图,很优秀,贴上:
贝叶斯原理是最大的概念,它解决了概率论中“逆向概率”的问题。
在这个理论基础上,人们设计出了贝叶斯分类器,朴素贝叶斯分类是贝叶斯分类器中的一种,也是最简单,最常用的分类器。
朴素贝叶斯之所以朴素是因为它假设属性是相互独立的,因此对实际情况有所约束,**如果属性之间存在关联,分类准确率会降低。**不过好在对于大部分情况下,朴素贝叶斯的分类效果都不错。
(1)朴素贝叶斯模型发源于古典数学理论,有稳定的分类效率。
(2)对小规模的数据表现很好,能个处理多分类任务,适合增量式训练,尤其是数据量超出内存时,我们可以一批批的去增量训练。
(3)对缺失数据不太敏感,算法也比较简单,常用于文本分类。
(1)理论上,朴素贝叶斯模型与其他分类方法相比具有最小的误差率。但是实际上并非总是如此,这是因为朴素贝叶斯模型给定输出类别的情况下,假设属性之间相互独立,这个假设在实际应用中往往是不成立的,在属性个数比较多或者属性之间相关性较大时,分类效果不好。而在属性相关性较小时,朴素贝叶斯性能最为良好。对于这一点,有半朴素贝叶斯之类的算法通过考虑部分关联性适度改进。
(2)需要知道先验概率,且先验概率很多时候取决于假设,假设的模型可以有很多种,因此在某些时候会由于假设的先验模型的原因导致预测效果不佳。
(3)由于我们是通过先验和数据来决定后验的概率从而决定分类,所以分类决策存在一定的错误率。
(4)对输入数据的表达形式很敏感。
由示例可以很轻易的知道朴素贝叶斯的算法过程,无非就是先计算先验概率,然后通过联合概率来求各条件概率,最后利用以上概率计算样本的条件概率。三个步骤,正好对应了贝叶斯的三个公式。
计算训练集现有类别的先验概率
计算测试样本在训练集类别中的条件概率
利用上述概率计算样本的条件概率,取出最大概率,进行分类。
# naive_bayes.py -- 朴素贝叶斯
import math
class NB():
def __init__(self):
self.cla_all_num = 0
self.cla_num = {}
self.cla_tag_num = {}
self.landa = 1 # 拉普拉斯修正值
def train(self, taglist, cla): # 训练,每次插入一条数据
# 插入分类
self.cla_all_num += 1
if cla in self.cla_num: # 是否已存在该分类
self.cla_num[cla] += 1
else:
self.cla_num[cla] = 1
if cla not in self.cla_tag_num:
self.cla_tag_num[cla] = {} # 创建每个分类的标签字典
# 插入标签
tmp_tags = self.cla_tag_num[cla] # 浅拷贝,用作别名
for tag in taglist:
if tag in tmp_tags:
tmp_tags[tag] += 1
else:
tmp_tags[tag] = 1
def P_C(self, cla): # 计算分类 cla 的先验概率
return self.cla_num[cla] / self.cla_all_num
def P_all_C(self): # 计算所有分类的先验概率
tmpdict = {}
for key in self.cla_num.keys():
tmpdict[key] = self.cla_num[key] / self.cla_all_num
return tmpdict
def P_W_C(self, tag, cla): # 计算分类 cla 中标签 tag 的后验概率
tmp_tags = self.cla_tag_num[cla] # 浅拷贝,用作别名
if tag not in self.cla_tag_num[cla]:
return self.landa / (self.cla_num[cla] + len(tmp_tags) * self.landa) # 拉普拉斯修正
return (tmp_tags[tag] + self.landa) / (self.cla_num[cla] + len(tmp_tags) * self.landa)
def test(self, test_tags): # 测试
res = ''
res_P = None
for cla in self.cla_num.keys():
log_P_W_C = 0
for tag in test_tags:
log_P_W_C += math.log(self.P_W_C(tag, cla))
tmp_P = log_P_W_C + math.log(self.P_C(cla)) # P(w|Ci) * P(Ci)
if res_P is None:
res = cla
res_P = tmp_P
if tmp_P > res_P:
res = cla
res_P = tmp_P
return res
def set_landa(self, landa):
self.landa = landa
def clear(self): # 重置模型
self.cla_all_num = 0
self.cla_num.clear()
self.cla_tag_num.clear()
if __name__ == '__main__':
nb = NB() # 生成模型
# 训练模型
# 年龄,收入,是否学生,信用等级 ---> 是否买了电脑
nb.train(['<30', '高', '否', '一般'], '否')
nb.train(['<30', '高', '否', '好'], '否')
nb.train(['30-40', '高', '否', '一般'], '是')
nb.train(['>40', '中', '否', '一般'], '是')
nb.train(['>40', '低', '是', '一般'], '是')
nb.train(['>40', '低', '是', '好'], '否')
nb.train(['30-40', '低', '是', '好'], '是')
nb.train(['<30', '中', '否', '一般'], '否')
nb.train(['<30', '低', '是', '一般'], '是')
nb.train(['>40', '中', '是', '一般'], '是')
nb.train(['<30', '中', '是', '好'], '是')
nb.train(['30-40', '中', '否', '好'], '是')
nb.train(['30-40', '高', '是', '一般'], '是')
nb.train(['>40', '中', '否', '好'], '否')
# 测试模型
testdata = ['<30', '中', '是', '一般']
print('测试结果:', nb.test(testdata))
朴素贝叶斯分类常用于文本分类,尤其是对于英文等语言来说,分类效果很好。它常用于垃圾文本过滤、情感预测、推荐系统等。
第一阶段:准备阶段
在这个阶段我们需要确定特征属性,比如上面案例中的“天气”、“温度”、“湿度”等,同时明确预测值是什么。并对每个特征属性进行适当划分,然后由人工对一部分数据进行分类,形成训练样本。
这一阶段是整个朴素贝叶斯分类中唯一需要人工完成的阶段,其质量对整个过程将有重要影响,分类器的质量很大程度上由特征属性、特征属性划分及训练样本质量决定。
第二阶段:训练阶段
这个阶段就是生成分类器,主要工作是计算每个类别在训练样本中的出现频率及每个特征属性划分对每个类别的条件概率。
输入是特征属性和训练样本,输出是分类器。
第三阶段:应用阶段
这个阶段是使用分类器对新数据进行分类。
输入是分类器和新数据,输出是新数据的分类结果。
前面也提到,朴素贝叶斯用处很多,常用于垃圾文本过滤、情感预测、推荐系统等,这里举一个平常生活中经常见到的例子——拼写纠错实例。
P ( W ∣ C ) P ( C ) P ( W ) \frac{P(W|C)P(C)}{P(W)} P(W)P(W∣C)P(C)
按道理这样就该结束了,但其实还可以简化,因为用户可能输入任何单词,因此对于任何C来讲,出现W的概率P(w)是一样的,而且我们最终只需要比较的是这个概率的大小,而与分母无关,所以直接忽略掉这个P(w),直接写成P(W|C)/P(C)。
为什么要这么简化?原因很简单,因为P(W)不好求。为什么不好求?因为天知道他要输入什么单词。
在这里P©就是先验概率,这个先验概率是比较好求的,在一个巨大的语料库中统计单词C出现的词频就可以得到。那么进而P(W∣C)也是可求的,语料库中的单词都是正确的,那么与错误输入W相似的单词的概率我们就可以得到,而P(W∣C)只需要计算W与C的相似程度就可得到。
我们把这个相似程度叫做编辑距离,也就是需要修改几个字母得到正确单词,编辑距离越小,那么概率值就越大。
例如:输入单词appl,假设语料库中与之相似的单词有apple,apply,application。这三个单词中前两者者只需要增加一个字母,那么对应的编辑距离都为1,而最后一个单词需要增加的字母为7,那么编辑距离就是7。
为什么要这么简化?原因很简单,因为P(W)不好求。为什么不好求?因为天知道他要输入什么单词。
在这里P(C)就是先验概率,这个先验概率是比较好求的,在一个巨大的语料库中统计单词C出现的词频就可以得到。那么进而P(W∣C)也是可求的,语料库中的单词都是正确的,那么与错误输入W相似的单词的概率我们就可以得到,而P(W∣C)只需要计算W与C的相似程度就可得到。
我们把这个相似程度叫做编辑距离,也就是需要修改几个字母得到正确单词,编辑距离越小,那么概率值就越大。
例如:输入单词appl,假设语料库中与之相似的单词有apple,apply,application。这三个单词中前两者者只需要增加一个字母,那么对应的编辑距离都为1,而最后一个单词需要增加的字母为7,那么编辑距离就是7。
显然系统在不考虑其他因素的条件下,比如前后单词之间的关系等,如果语料库中的apply的词频高于apple,那么系统应该给出正确单词apply。