机器学习入门 --- 贝叶斯算法

本文深入探讨了贝叶斯算法的基本原理,包括正向概率和逆向概率的概念,通过具体的实例如学校性别着装比例和拼写纠正问题,阐述了贝叶斯公式及其在实际问题中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

贝叶斯算法概述

贝叶斯要解决的问题:

正向概率:假设袋子里面有N个白球,M个黑球,伸手进去摸球,摸出黑球的概率是多大

逆向概率:如果我们事先并不知道袋子里面黑白球的比例,而是闭着眼睛摸出一个(或好几个)球,观察这些取出来的球的颜色之后,那么我们可以就此对袋子里面的黑白球的比例作出什么样的推测

例子

条件:在一个学校中,男生占60%,女生占40%,男生总是穿长裤,女生则一半穿长裤一半穿裙子

问题:

正向概率:随机选取一个学生,他(她)穿长裤的概率和穿裙子的概率是多大

逆向概率:迎面走来一个穿长裤的学生,只看得见他(她)穿的是否长裤,而无法确定他(她)的性别,能够推断出他(她)是女生的概率是多大吗?

假设学校里面人的总数是 U 个

穿长裤的男生: U ∗ P ( B o y ) ∗ P ( P a n t s ∣ B o y ) U*P(Boy)*P(Pants|Boy) UP(Boy)P(PantsBoy)

  • P ( B o y ) P(Boy) P(Boy) 是男生的概率,为 60%
  • P ( P a n t s ∣ B o y ) P(Pants|Boy) P(PantsBoy) 是条件概率,即在 B o y Boy Boy 条件下,穿长裤的概率是多大,为100%

穿长裤的女生: U ∗ P ( G i r l ) ∗ P ( P a n t s ∣ G i r l ) U*P(Girl)*P(Pants|Girl) UP(Girl)P(PantsGirl)

  • P ( G i r l ) P(Girl) P(Girl) 是女生的概率,为 40%
  • P ( P a n t s ∣ G i r l ) P(Pants|Girl) P(PantsGirl) 是条件概率,即在 G i r l Girl Girl 条件下,穿长裤的概率是多大,为50%

求解:穿长裤的人里面有多少女生?

穿长裤总人数: U ∗ P ( B o y ) ∗ P ( P a n t s ∣ B o y ) + U ∗ P ( G i r l ) ∗ P ( P a n t s ∣ G i r l ) U*P(Boy)*P(Pants|Boy)+U*P(Girl)*P(Pants|Girl) UP(Boy)P(PantsBoy)+UP(Girl)P(PantsGirl)

穿长裤的人里面女生人数:
P ( G i r l ∣ P a n t s ) = U ∗ P ( G i r l ) ∗ P ( P a n t s ∣ G i r l ) 穿 长 裤 总 人 数 P ( G i r l ∣ P a n t s ) = U ∗ P ( G i r l ) ∗ P ( P a n t s ∣ G i r l ) U ∗ P ( B o y ) ∗ P ( P a n t s ∣ B o y ) + U ∗ P ( G i r l ) ∗ P ( P a n t s ∣ G i r l ) P ( G i r l ∣ P a n t s ) = P ( G i r l ) ∗ P ( P a n t s ∣ G i r l ) P ( B o y ) ∗ P ( P a n t s ∣ B o y ) + P ( G i r l ) ∗ P ( P a n t s ∣ G i r l ) P ( G i r l ∣ P a n t s ) = P ( G i r l ) ∗ P ( P a n t s ∣ G i r l ) P ( P a n t s ) \begin{aligned} P(Girl|Pants)&=\frac{U*P(Girl)*P(Pants|Girl)}{穿长裤总人数} \\ P(Girl|Pants)&=\frac{U*P(Girl)*P(Pants|Girl)}{U*P(Boy)*P(Pants|Boy)+U*P(Girl)*P(Pants|Girl)} \\ P(Girl|Pants)&=\frac{P(Girl)*P(Pants|Girl)}{P(Boy)*P(Pants|Boy)+P(Girl)*P(Pants|Girl)} \\ P(Girl|Pants)&=\frac{P(Girl)*P(Pants|Girl)}{P(Pants)} \end{aligned} P(GirlPants)P(GirlPants)P(GirlPants)P(GirlPants)=穿UP(Girl)P(PantsGirl)=UP(Boy)P(PantsBoy)+UP(Girl)P(PantsGirl)UP(Girl)P(PantsGirl)=P(Boy)P(PantsBoy)+P(Girl)P(PantsGirl)P(Girl)P(PantsGirl)=P(Pants)P(Girl)P(PantsGirl)

贝叶斯公式

P ( A ∣ B ) = P ( A ) ∗ P ( B ∣ A ) P ( B ) P(A|B)=\frac{P(A)*P(B|A)}{P(B)} P(AB)=P(B)P(A)P(BA)

实例

拼写纠正实例
问题:检测到用户输入了一个不在字典中的单词,我们需要去猜
测:“用户到底真正想输入的单词是什么呢?

P(我们猜测他想输入的单词 | 他实际输入的单词)

用户实际输入的单词记为 D ( D 代表 Data ,即观测数据)

猜测1: P ( h 1 ∣ D ) P(h_1| D) P(h1D)
猜测2: P ( h 2 ∣ D ) P(h_2 | D) P(h2D)
猜测3: P ( h 3 ∣ D ) P(h_3| D) P(h3D)
这里我们统一为:P(h | D)
P ( h ∣ D ) = P ( h ) ∗ P ( D ∣ h ) P ( D ) P(h | D) = \frac{P(h) * P(D | h)}{P(D)} P(hD)=P(D)P(h)P(Dh)

对于不同的具体猜测 h 1 , h 2 , h 3 . . . h_1,h_2,h_3... h1,h2,h3... P ( D ) P(D) P(D) 都是一样的,所以在比较
P ( h 1 ∣ D ) P(h_1 | D) P(h1D) P ( h 2 ∣ D ) P(h_2 | D) P(h2D) 的时候我们可以忽略这个常数

所以他们之间成正比例关系:

P ( h ∣ D ) ∝ P ( h ) ∗ P ( D ∣ h ) P(h|D)\propto P(h)*P(D|h) P(hD)P(h)P(Dh)

对于给定观测数据,一个猜测是好是坏,取决于“这个猜测本身独
立的可能性大小(先验概率)”和“这个猜测生成我们观测
到的数据的可能性大小。

贝叶斯方法计算: P ( h ) ∗ P ( D ∣ h ) P(h) * P(D | h) P(h)P(Dh) P ( h ) P(h) P(h) 是特定猜测的先验概率

在计算完各个猜测的概率,进行比较再给出一个最终结果

垃圾邮件过滤实例

问题:给定一封邮件,判定它是否属于垃圾邮件

D 来表示这封邮件,注意 D 由 N 个单词组成,我们用 h+ 来表示垃圾邮件,h- 表示正常邮件
P ( h + ∣ D ) = P ( h + ) ∗ P ( D ∣ h + ) P ( D ) P ( h − ∣ D ) = P ( h − ) ∗ P ( D ∣ h − ) P ( D ) P(h+|D)=\frac{P(h+)*P(D|h+)}{P(D)} \\ P(h-|D)=\frac{P(h-)*P(D|h-)}{P(D)} P(h+D)=P(D)P(h+)P(Dh+)P(hD)=P(D)P(h)P(Dh)

先验概率: P ( h + ) P(h+) P(h+) P ( h − ) P(h-) P(h) 这两个先验概率都是很容易求出来的,只
需要计算一个邮件库里面垃圾邮件和正常邮件的比例就可以

假设 D 里面有 N 个单词 d 1 , d 2 , d 3 , . . . , d n d_1,d_2,d_3,...,d_n d1,d2,d3,...,dn P ( D ∣ h + ) = P ( d 1 , d 2 , . . , d n ∣ h + ) P(D|h+) = P(d_1,d_2,..,d_n|h+) P(Dh+)=P(d1,d2,..,dnh+) P ( d 1 , d 2 , . . , d n ∣ h + ) P(d_1,d_2,..,d_n|h+) P(d1,d2,..,dnh+) 就是说在垃圾邮件当中出现跟 D 这封邮件一模一样的一封邮件的概率是多大

P ( d 1 , d 2 , . . , d n ∣ h + ) P(d_1,d_2,..,d_n|h+) P(d1,d2,..,dnh+) 可扩展为: P ( d 1 ∣ h + ) ∗ P ( d 2 ∣ d 1 ∣ h + ) ∗ P ( d 3 ∣ d 2 ∣ d 1 ∣ h + ) ∗ . . . ∗ P ( d n ∣ d n − 1 ∣ . . . ∣ d 3 ∣ d 2 ∣ d 1 ∣ h + ) P(d_1|h+) * P(d_2|d_1|h+) * P(d_3|d_2|d_1|h+) *...*P(d_n|d_{n-1}|...|d_3|d_2|d_1|h+) P(d1h+)P(d2d1h+)P(d3d2d1h+)...P(dndn1...d3d2d1h+)

展开公式中,每一个词的累加导致很难计算,这里使用朴素贝叶斯理论,假设特征之间是独立、互不影响的,可化简为: P ( d 1 ∣ h + ) ∗ P ( d 2 ∣ h + ) ∗ P ( d 3 ∣ h + ) ∗ . . . ∗ P ( d n ∣ h + ) P(d_1|h+) * P(d_2|h+) * P(d_3|h+) *...*P(d_n|h+) P(d1h+)P(d2h+)P(d3h+)...P(dnh+) ,只要统计到 d i d_i di 这些单词在垃圾邮件中出现的概率即可

再对 d i d_i di 这些单词做在正常邮件中的概率统计 P ( d 1 ∣ h − ) ∗ P ( d 2 ∣ h − ) ∗ P ( d 3 ∣ h − ) ∗ . . . ∗ P ( d n ∣ h − ) P(d_1|h-) * P(d_2|h-) * P(d_3|h-) *...*P(d_n|h-) P(d1h)P(d2h)P(d3h)...P(dnh)
P ( h + ∣ D ) = P ( h + ) ∗ P ( D ∣ h + ) P ( D ) P ( h − ∣ D ) = P ( h − ) ∗ P ( D ∣ h − ) P ( D ) P(h+|D)=\frac{P(h+)*P(D|h+)}{P(D)} \\ P(h-|D)=\frac{P(h-)*P(D|h-)}{P(D)} P(h+D)=P(D)P(h+)P(Dh+)P(hD)=P(D)P(h)P(Dh)
最后计算出 P ( h ∗ ∣ D ) P(h*|D) P(hD),进行比较判断是否为垃圾邮件

代码实现拼写纠正

求解: a r g m a x c P ( c ∣ w ) − > a r g m a x c P ( w ∣ c ) P ( c ) / P ( w ) argmaxc P(c|w) -> argmaxc P(w|c) P(c) / P(w) argmaxcP(cw)>argmaxcP(wc)P(c)/P(w)
  • P ( c ) P(c) P(c), 文章中出现一个正确拼写词 c 的概率, 也就是说, 在英语文章中, c 出现的概率有多大
  • P ( w ∣ c ) P(w|c) P(wc), 在用户想键入 c 的情况下敲成 w 的概率. 因为这个是代表用户会以多大的概率把 c 敲错成 w
  • a r g m a x c argmaxc argmaxc, 用来枚举所有可能的 c 并且选取概率最大的
先验概率
# 把语料中的单词全部抽取出来, 转成小写, 并且去除单词中间的特殊符号
def words(text): return re.findall('[a-z]+', text.lower()) 

def train(features):
    model = collections.defaultdict(lambda: 1)
    for f in features:
        model[f] += 1
    return model
 
NWORDS = train(words(open('big.txt').read()))

按照原逻辑,只要是传进来一个词,就要返回出现这个词的原概率,但是如果传进来一个从来没有过见过的新词,返回的这个词的先验概率就是0了,这个情况不太妙, 因为概率为0这个代表了这个事件绝对不可能发生, 而在我们的概率模型中, 我们期望用一个很小的概率来代表这种情况. lambda: 1

编辑距离

两个词之间的编辑距离定义为使用了几次插入(在词中插入一个单字母)、删除(删除一个单字母)、交换(交换相邻两个字母)、替换(把一个字母换成另一个)的操作从一个词变到另一个词

编辑距离为 1:

#返回所有与单词 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

编辑距离为 2:
edits1() 的基础上再做一次循环

#返回所有与单词 w 编辑距离为 2 的集合
#在这些编辑距离小于2的词中间, 只把那些正确的词作为候选词
def edits2(word):
    return set(e2 for e1 in edits1(word) for e2 in edits1(e1))

这种编辑距离的方法,在对长单词的计算会非常大量,比如 something 编辑距离为2的单词居然达到了 114,324 个,所以还需要进行一个优化,在这些编辑距离小于2的词中间, 只把那些正确的词作为候选词,只能返回 4 个单词:
{‘seething’, ‘smoothing’, ‘something’, ‘soothing’}

正常来说把一个元音拼成另一个的概率要大于辅音 (因为人常常把 hello 打成 hallo 这样); 把单词的第一个字母拼错的概率会相对小, 等等

但是为了简单起见, 选择了一个简单的方法: 编辑距离为1的正确单词比编辑距离为2的优先级高, 而编辑距离为0的正确单词优先级比编辑距离为1的高

# 优化:在这些编辑距离小于2的词中间, 只把那些正确的词作为候选词
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])
测试
correct('appla')
'apply'
correct('learw')
'learn'
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值