偶然间听说阿里云云栖社区这个“寒假换装攻略活动”,还送键盘!
正好缺一个键盘(ノへ ̄、)
人穷志短,人穷志短,不开心的话说两遍,回到现实,想想把朴素贝叶斯算法再重新整理整理,来拿键盘( ̄︶ ̄)↗
tip:文章可能很长,希望看完了能有所收获
机器学习|朴素贝叶斯算法(一)-贝叶斯简介及应用
机器学习|朴素贝叶斯算法(二)-用sklearn实践贝叶斯
机器学习|朴素贝叶斯算法(三)-深入理解朴素贝叶斯原理
一、 贝叶斯
贝叶斯简介:
贝叶斯(RE V Thomas Bayes),英国数学家。
贝叶斯算法源于用来-解决一个“逆向概率”的问题。
贝叶斯要解决的问题:
- 正向概率:假设袋子里面有N个白球,M个黑球,闭着眼伸手去摸球,摸出白球的概率是多少
- 逆向概率:如果我们事先并不知道袋子里面黑白球的比例,而是闭着眼睛摸出一个(或好几个)球,观察这些取出来的球的颜色之后,那么我们可以就此对袋子里面的黑白球的比例作出什么样的推测
为什么用贝叶斯:
- 现实世界本身就是不确定的,人类的观察能力是有局限性的
- 我们日常所观察到的只是事物表面上的结果,因此我们需要提供一个猜测
下面举一个简单的概率问题来说明此事:
问题:男60%,女40%,男生总是穿长裤,女生则一半穿长裤一半穿裙子
正向概率:随机选取一个学生,他(她)穿长裤的概率和穿裙子的概率是多大
逆向概率:迎面走来一个穿长裤的学生,你只看得见他(她)穿的是否长裤,而无法确定他(她)的性别,你能够推断出他(她)是女生的概率是多大吗?
假设学校里面人的总数是 $U $个
穿长裤的男生:$U \times P(Boy) \times P(Pants|Boy)$
$P(Boy)$ 是男生的概率 = 60%
$P(Pants|Boy)$ 是条件概率,即在 Boy 这个条件下穿长裤的概率是多大,这里是 100% ,因为所有男生都穿长裤
穿长裤的女生:$ U \times P(Girl) \times P(Pants|Girl)$
求解:穿长裤的人里面有多少女生?
穿长裤总数:$U \times P(Boy) \times P(Pants|Boy) + U \times P(Girl) \times P(Pants|Girl)$
结果: $\frac{U \times P(Girl) \times P(Pants|Girl) } {U \times P(Boy) \times P(Pants|Boy) + U \times P(Girl) \times P(Pants|Girl)}$
与总人数有关吗?
$\frac{U \times P(Girl) \times P(Pants|Girl) }{U \times P(Boy) \times P(Pants|Boy) + U \times P(Girl) * P(Pants|Girl)}$
容易发现这里校园内人的总数U是无关的,可以消去
$P(Girl|Pants) = \frac{P(Girl) \times P(Pants|Girl) }{P(Boy) \times P(Pants|Boy) + P(Girl) \times P(Pants|Girl)}$
可以发现:
分母其实就是$ P(Pants)$
分子其实就是$ P(Pants, Girl)$
贝叶斯公式:$P\left ( A|B \right )= \frac{P\left ( B|A \right )P\left ( A \right )}{P\left ( B \right )}$
二、贝叶斯应用:
拼写纠正实例
垃圾邮件过滤实例
1.拼写纠正实例
问题是我们看到用户输入了一个不在字典中的单词,我们需要去猜测:这个家伙到底真正想输入的单词是什么呢?
P(我们猜测他想输入的单词 | 他实际输入的单词)
用户实际输入的单词记为 D ( D 代表 Data ,即观测数据)
猜测1:$P(h_1 | D)$,猜测2:$P(h_2 | D)$,猜测3:$P(h_3 | D)$......
统一为:$P(h | D)$
根据上文推导出来的贝叶斯公式:$P(h | D) = \frac{P(h) \times P(D | h)} { P(D)}$
对于不同的具体猜测 h1 h2 h3 .. ,P(D) 都是一样(因为都是同一个观测数据D嘛)的,所以在比较$ P(h_1 | D)$ 和 $P(h_2 | D)$ 的时候我们可以忽略这个常数
$P(h | D) ∝ P(h) * P(D | h)$
上式的意思表明了:对于给定观测数据,一个猜测是好是坏,取决于“这个猜测本身独立的可能性大小“(先验概率,Prior )(即$P(h)$)和“这个猜测生成我们观测到的数据的可能性大小”(即似然,$P(D | h)$)。
贝叶斯方法计算: $P(h) * P(D | h),P(h) $是特定猜测的先验概率
比如用户输入tlp ,那到底是 top 还是 tip ?这个时候,当最大似然不能作出决定性的判断时,先验概率就可以插手进来给出指示—— “既然你无法决定,那么我告诉你,一般来说 top 出现的程度要高许多,所以更可能他想打的是 top ”
模型比较理论
最大似然:最符合观测数据的(即 $P(D | h)$ 最大的)最有优势的
奥卡姆剃刀:$ P(h)$ 较大的模型有较大的优势
掷一个硬币,观察到的是“正”,根据最大似然估计的精神,我们应该猜测这枚硬币掷出“正”的概率是 1(也就是这枚硬币是特制的,两面都是正),因为这个才是能最大化 $P(D | h) $的那个猜测
如果平面上有 N 个点,近似构成一条直线,但绝不精确地位于一条直线上。这时我们既可以用直线来拟合(模型1),也可以用二阶多项式(模型2)拟合,也可以用三阶多项式(模型3),特别地,用 N-1 阶多项式便能够保证肯定能完美通过 N 个数据点。那么,这些可能的模型之中到底哪个是最靠谱的呢?
根据奥卡姆剃刀:在两个模型在训练集训练出来的结果一样好时,选择简单的(防止过拟合,把模型中特有的参数作为特征),也就是选择阶数最小的那个模型。
2.垃圾邮件过滤实例
问题:给定一封邮件,判定它是否属于垃圾邮件
D 来表示这封邮件,注意 D 由 N 个单词组成。我们用 h+ 来表示垃圾邮件,h- 表示正常邮件
$P(h_+|D) = \frac{P(h_+) \times P(D|h_+)} { P(D)}$
$P(h_- |D) =\frac{P(h_- ) \times P(D|h_- )} { P(D)}$
先验概率:$P(h_+)$ 和$ P(h_-)$ 这两个先验概率都是很容易求出来的,只需要计算一个邮件库里面垃圾邮件和正常邮件的比例就行了。
设D 里面含有 N 个单词 $d_1,d_2,d_3,P(D|h_+) = P(d_1,d_2,..,d_n|h_+)$
$P(d_1,d_2,..,d_n|h_+) $就是说在垃圾邮件当中出现跟我们目前这封邮件一模一样的一封邮件的概率是多大!
显然,如果要一模一样也就是说要邮件中每个单词都一样,那真是很苛刻了,所以我们现实中要降低判断垃圾邮件的标准,只要求和我们已知的垃圾邮件有一定的相似度就行了。
$P(d_1,d_2,..,d_n|h_{+}) $由全概率公式扩展为: $P(d_1|h_+) \times P(d_2|d_1, h_+) \times P(d_3|d_2,d_1, h_+) \times ..$
这里我们假设 $d_i$与 $d_{i-1} $是完全条件无关的(因为朴素贝叶斯(naïve bayes)假设特征之间是独立,互不影响)
简化为$ P(d_1|h_+) \times P(d_2|h_+) \times P(d_3|h_+) \times ..$,再次降低了评判的标准,而现在每一个概率又都是可计算的了
对于$P(d_1|h_+) \times P(d_2|h_+) \times P(d_3|h_+) \times ..$只要统计 $d_i$ 这个单词在垃圾邮件中出现的频率即可。
下面来看一下老师在课程中给出的贝叶斯-拼写检查器的代码。
求解:argmaxc P(c|w) -> argmaxc P(w|c) P(c) / P(w)
P(c):文章中出现一个正确拼写词 c 的概率, 也就是说, 在英语文章中, c 出现的概率有多大
P(w|c):在用户想键入 c 的情况下敲成 w 的概率. 因为这个是代表用户会以多大的概率把 c 敲错成 w
argmaxc:用来枚举所有可能的 c 并且选取概率最大的
import re, collections
###贝叶斯-拼写检查器###
# 把语料中的单词全部抽取出来, 转成小写, 并且去除单词中间的特殊符号
def words(text): return re.findall('[a-z]+', text.lower())
#避免一个单词未在语料库中出现但却出现了(那不就返回0了),为了避免,用很小的lambda代替
def train(features):
model = collections.defaultdict(lambda: 1)
for f in features:
model[f] += 1
return model
NWORDS = train(words(open('big.txt').read()))
alphabet = 'abcdefghijklmnopqrstuvwxyz'
##编辑距离:两个词之间的编辑距离定义为使用了几次插入(在词中插入一个单字母), 删除(删除一个单字母), 交换(交换相邻两个字母), 替换(把一个字母换成另一个)的操作从一个词变到另一个词
#返回所有与单词 w 编辑距离为 1 的集合
def edits1(word):
n = len(word)
return set([word[0:i]+word[i+1:] for i in range(n)] + # deletion
[word[0:i]+word[i+1]+word[i]+word[i+2:] for i in range(n-1)] + # transposition
[word[0:i]+c+word[i+1:] for i in range(n) for c in alphabet] + # alteration
[word[0:i]+c+word[i:] for i in range(n+1) for c in alphabet]) # insertion
#返回所有与单词 w 编辑距离为 2 的集合
#在这些编辑距离小于2的词中间, 只把那些正确的词作为候选词
def known_edits2(word):
return set(e2 for e1 in edits1(word) for e2 in edits1(e1) if e2 in NWORDS)
#正常来说把一个元音拼成另一个的概率要大于辅音 (因为人常常把 hello 打成 hallo 这样); 把单词的第一个字母拼错的概率会相对小, 等等.但是为了简单起见, 选择了一个简单的方法: 编辑距离为1的正确单词比编辑距离为2的优先级高, 而编辑距离为0的正确单词优先级比编辑距离为1的高.
def known(words): return set(w for w in words if w in NWORDS)
#如果known(set)非空, candidate 就会选取这个集合, 而不继续计算后面的
def correct(word):
candidates = known([word]) or known(edits1(word)) or known_edits2(word) or [word]
return max(candidates, key=lambda w: NWORDS[w])
OK,如果看完这一篇对贝叶斯还是不了解的话,没关系!
接下来我会用python机器学习的库来简单实践一下,再深入的谈一谈对朴素贝叶斯算法原理的理解。