机器学习学习笔记 第十五章 贝叶斯算法

版权声明:By BenkoZhao https://blog.csdn.net/u011419402/article/details/82952602

贝叶斯算法

  • 贝叶斯要解决的问题
    1. 正向概率
    2. 逆向概率
      • 举例:一个班级中,男生 60%,女生 40%,男生总是穿长裤,女生则一半穿长裤一半穿裙子
      • 正向概率:随机选取一个学生,他(她)穿长裤的概率和穿裙子的概率是多
      • 逆向概率:迎面走来一个穿长裤的学生,你只看得见他(她)穿的是否长裤,而无法确定他(她)的性别,你能够推断出他(她)是女生的概率是多大吗?
      • 假设班级中总人数 U
        穿 U × P ( B o y ) × P ( P a n t s B o y ) 穿长裤的男生:U\times P(Boy) \times P(Pants|Boy)
      • P ( B o y ) = 60 P(Boy)=60%
      • P ( P a n t s B o y ) P(Pants|Boy) 是条件概率,即在 Boy 这个条件下,穿长裤的概率,这里为 100 % 100\%
        穿 U × P ( G i r l ) × P ( P a n t s G i l r ) 穿长裤的女生:U\times P(Girl)\times P(Pants|Gilr)

    贝叶斯公式

    P ( A B ) = P ( B A ) P ( A ) P ( B ) \color{red}{P(A|B)=\frac{P(B|A)P(A)}{P(B)}}
    • 举个例子

      • 用户实际输入的单词 D,即观测数据
        • 猜测 1: P ( h 1 D ) P(h1|D) , 猜测 2: P ( h 2 D ) P(h2|D) , 猜测 3: P ( h 1 D ) P(h1|D) ,…
        • 利用贝叶斯公式可以得出结论:
          P ( h D ) = P ( h ) P ( D h ) P ( D ) P(h|D)=\frac{P(h)P(D|h)}{P(D)}
        • 在应用中,我们认为 P ( D ) P(D) 是一样的,所以可以直接约掉
        • 因此得到以下公式:
          P ( h D ) P ( h ) P ( D h ) P(h|D)\propto P(h)P(D|h)
        • 一般来说 P ( h ) P(h) 可以通过分析大量的文本获得,而 P ( D h ) P(D|h) 则可以分析正确与错误的单词之间的距离来获得
          • P ( h ) P(h) 是特定猜测的先验概率
          • 比如用户输入tlp ,那到底是top还是 tip?这个时候,当最大似然不能作出决定性的判断时,先验概率就可以插手进来给出指示—— “既然你无法决定,那么我告诉你,一般来说top出现的概率要高许多,所以更可能他想打的是 top ”
    • 模型比较理论

      • 最大似然:最符合观测数据的(即 P ( D h ) P(D|h) )最有优势
      • 奥卡姆剃刀:认为 P ( h ) P(h) 较大的模型有较大优势
        • 掷一个硬币,观察到的是“正”,根据最大似然估计的精神,我们应该 猜测这枚硬币掷出“正”的概率是 1,因为这个才是能最大化 P ( D h ) P(D|h) 的那个猜测

           剃刀原则不是一个理论而是一个原理,它的目的是为了精简抽象实体。它不能被证明也不能被证伪,因为它是一个规范性的思考原则。大部分情况下,应用奥卡姆剃刀原理是合适的;但是这不代表奥卡姆剃刀就是正确的。(知乎)
           总结一下,剃刀原则并不是一种定理(在数学上有推导,数学之美——刘未鹏),而是一种思维方式,可用于指导我们的工作,比如我们可以用A和B达到同样的效果,但B更简单,于是我们选择B。同时,也有人说可能因为能力不足,于是我们选择更简单的方式来处理问题,这也是剃刀的原则的一种应用吧。举个贴切的例子,做决策树分析的时候,采用9个属性的预测性能和5个属性的预测性能是相似的,那么我们就会选择5个属性来预测。
        在实际中,越常见的东西越好。

    • 垃圾邮件过滤实例:

      • 问题:给定一封邮件,判定它是否属于垃圾邮件 D 来表示这封邮件,注意 D 由 N 个单词组成。我们用 h+ 来表示 垃圾邮件,h- 表示正常邮件
        1. 根据贝叶斯公式:
          P ( h + D ) = P ( h + ) P ( D h + ) P ( D ) P(h+|D)=\frac{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)}
        2. 先验概率 P ( h + ) P(h+) P ( h ) P(h-) 很容易求出来,只需要计算一个邮件库里面垃圾邮件和正常邮件的比例就行了。
        3. D 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 + ) d1, d2, d3,P(D|h+) = P(d1,d2,..,dn|h+) P(d1,d2,..,dn|h+) 就是说在垃圾邮件当中出现跟我们目前这封邮件一模一样的一封邮件的概率是多大!
          • 实际上这个不好求,或者说求出来概率太低了
          • 利用条件概率的相乘方法: P ( d 1 , d 2 , . . , d n h + ) P ( d 1 h + ) × P ( d 2 d 1 , h + ) × P ( d 3 d 2 , d 1 , h + ) × . . . P(d1,d2,..,dn|h+)扩展为:P(d1|h+)\times P(d2|d1, h+)\times P(d3|d2,d1, h+)\times ...
          • P ( d 1 h + ) × P ( d 2 d 1 , h + ) × P ( d 3 d 2 , d 1 , h + ) × . . . P(d1|h+)\times P(d2|d1, h+)\times P(d3|d2,d1, h+)\times ... 这里我们可以认为 d i , d ( i 1 ) . . . d_i,d_(i-1)... 是条件无关的,因此上式可以简化为 P ( d 1 h + ) × P ( d 2 h + ) × P ( d 3 h + ) × . . . P(d1|h+)\times P(d2|h+)\times P(d3|h+) \times ...
        4. 对于 P ( d 1 h + ) × P ( d 2 h + ) × P ( d 3 h + ) × . . . P(d1|h+)\times P(d2|h+)\times P(d3|h+) \times ... ,我们只需要统计 d i d_i 这个词在垃圾邮件中出现的概率即可

下面我们尝试利用贝叶斯的先验概率来进行拼写检查器

import re, collections

def words(text):#此处将传进来的文章进行单词的提取,将无用的标点符号等都去掉,并把英文字母都变为小写
    return re.findall('[a-z]+', text.lower())
def train(features):#将文本传进来并进行词频统计
    model = collections.defaultdict(lambda:1)#使用dict时,如果引用的Key不存在,就会抛出KeyError。如果希望key不存在时,返回一个默认值,就可以用defaultdict
    for f in features:
        model[f] += 1#相应单词的value加一
    return model
NWORDS = train(words(open('big.txt').read()))

此时已经将文本传进来并且解析成字典统计好结果了

接下来就开始计算编辑距离

  • 也就是输入一个单词,则与它相差一个或两个单词的词有啥,比如know变成knov,knox等
  • 在这里我们算出编辑距离为1和为2的所有可能性
alphabet = 'abcdefghijklmnopqrstuvwxyz'#字母表
def edits(word):
    n = len(word)
    return set([word[0:i]+word[i+1:] for i in range(n)]+   #随机去掉一个字母
              [word[0:i]+word[i+1]+word[i]+word[i+2:] for i in range(n-1)]+  #相邻两个单词互换了
              [word[0:i]+c+word[i+1:] for i in range(n) for c in alphabet]+#随机有一个单词替换掉
              [word[0:i]+c+word[i:] for i in range(n+1) for c in alphabet]) #随机插入一个字母
#注意,其实以上情况考虑到了首字母与末字母替换或插入的情况,因为word[0:0]=''
def edits2(word):
    return set(e2 for e1 in edits(word) for e2 in edits(e1) if e2 in NWORDS)

这句比较绕,我们试着理解一下

  • 首先是for e1 in edits(word)会返回e1 然后e1会传入for e2 in edits(e1)中,算出所有的e2,此时其实已经可以返回集合,但是我们发现集合太大了,就拿something来说就有11w个,总量细思极恐
  • 因此我们再加一句if e2 in NWORDS,这样限制了所有的e2都在原有的词汇集合里面,大大缩减可能数量,也非常贴合我们程序的目的
def known(words):
    return set(w for w in words if w in NWORDS)
#这句话是将计算出来的所有词中存在于原语料库中的词返回
def correct(word):
    candidates = known([word]) or known(edits(word)) or known(edits2(word)) or [word] #将所有候选词挑出来
    #根据python的特性,其实假如第一个条件满足了,则会停止后面运算
    #其实这里我们默认了拼错一个字的可能性比拼错两个字的可能性高,为了简单处理~
    return max(candidates, key=lambda w:NWORDS[w])
#最后一句是一个匿名函数,相当于将候选词传入lambda w:NWORDS[w]的w中,并以返回结果作为排序的依据
#lambda w:NWORDS[w]相当于一个函数,以w为参数,返回冒号后的内容,即返回NWORDS
#假设w=‘something’, 则返回NWORDS('something')的值,也即此处为684,因为总共出现了684次
correct('somethina')#进行尝试,一开始我试了hella,本以为返回hello,结果返回hell了,用NWORDS['hell']查了一下,原来hell出现的概率比hello还高
'something'
NWORDS['something']
684

对唐宇迪老师的机器学习教程进行笔记整理
编辑日期:2018-10-6
小白一枚,请大家多多指教

猜你喜欢

转载自blog.csdn.net/u011419402/article/details/82952602