一、什么是拼写检查

鉴于我们做全国最好的、最通俗、最有深度的AI课程的宗旨,我们从一个既简单又复杂的问题开始今天的学习:单词拼写检查

什么是单词拼写检查,就是你写错了单词,系统推荐一个它认为最可能正确的单词给你。本课的知识,你稍微延伸一下,就可以做一个全文错别字检查程序出来,这个可是有商业价值的哦,别怪我没告诉您。

这个怎么做呢?你是否还没有反应过来,觉得这是AI应该解决的问题吗?我仿佛听到了同学在问:“AI不是要解决自动驾驶、自动翻译、人脸识别、自动飞行之类的事情吗? 一个拼写检查也算人工智能?它这么简单,完全可以不用人工智能的算法来解决啊!就普通的一些方法就可以解决啊?”

1、wps中的拼写检查

下图就是wps检查拼写的情况,我们输入了agret,wps认为是一个错误的单词,单词下面画了一条红色的波浪线,选中点击右键的时候,出现了一些提示词“Agrippa、agree、egret、great”,这些都是wps认为可能正确的词,我这里其实想输入agree,所以,我选择wps给我检查的agree作为结果。

这就是拼写检查。

二、我们需要解决什么问题

无论使用哪种输入法,你都无法创造一个新汉字,所以中文就没有检查“错别字”的意义了。但是英文有,如我想写一个fuck,不小心写成了fuk,那么人工智能的方法可以把fuk纠正为fuck,这就是我们这节课要重点讲的“英文拼写检查”。

很多同学面对一个问题,不知道如何开始,我们这里因为他们不知道如何把一个复杂的问题简单细化,我们这里就分步骤给大家分析一下,希望大家学会分步解决问题的方法:

1、将拼写检查细化为一个个小任务

各位看官,首先,我们再来分析一下我们要解决的问题:

我们都知道小沈阳英文不好,写了一段话:“I want to fuk you”,通过机器学习的方法,检查这几个英文的拼写是否正确,如果拼写错误,给出最可能正确的拼写。

那么步骤是什么呢?如下:

(1)、分词:将“I want to fuk you”以空格分开,变为I、want、to、fuk、you这几个单词,简称分词。这一步很简单,大学一年级的同学就会做。如果不会做,请您补一下老师的经典巨著《人工智能从入门到放弃》。什么看完本书,还不知道什么做?哈哈,就是使用python的split函数,代码如下:

1
"I want to fuk you".split()

这句代码,可以以空格为间隔符把句子分开。

(2)、找错词:将这几个词查“牛津高阶词典”,发现fuk查不到,那么它就应该是一个错词。请注意,这一步是很容易实现的,一个for循环,就能够知道fuk是不是在牛津词典中。虽然很浪费CPU资源,但是这里并不是算法的重点。

(3)、计算fuk对应的正确词可能的概率。 请记住这里的“概率”这个关键词,每一个经过残酷高考的同学,都应该学过概率,本课将要涉及的概率知识,我保证在我的讲解下,只需要高中知识便已足够。

fuk可能对应很多正确的词,那个词概念越高,那么就越可能是正确的词。

(4)、找出最大概率的词:第3步中,找出的词fuk对应的概率最大的词,例如fuk对应fuck的概率是0.92,fuk对应fck的概率是0.3,那么我们就认为fuk最可能的是fuck,我们会将fuck作为我们猜测的最可能是用户想要录入的词。

三、代码之下毫无隐藏:从本课的代码开始

在继续讲之前,我们给大家展示本课的第一个项目代码(word_correct.py),你可以从顶部菜单的初级课程源码下载中找到。如下:

1、拼写检查代码

将代码放到pycharm中,运行即可:

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
# -*- coding: utf8 -*-
 
import re, collections
 
# re是正则表达式,将text中的所有内容 变为小写,并提取出所有字母组成的单词
def words(text):
    return re.findall('[a-z]+', text.lower())
 
# 训练每个单词出现的次数,放到一个model这个字典中。
def train(features):
    # 使用defaultdict的好处在于当访问一个不存在的键值的时候会调用入参函数,
    # 并将结果作为这个key的value
    model = collections.defaultdict(lambda: 1)
    for f in features:
        model[f] += 1
    return model
 
# 将big.txt中的词汇读出来,并通过words转换为一个个单词,
file_context = file('big.txt').read()
NWORDS = train(words(file_context))
 
alphabet = 'abcdefghijklmnopqrstuvwxyz'
 
#实现编辑距离为1的操作,也就是说只改变一个字母
def edits1(word):
    n = len(word)
    # 删除一个字母
    deletes = [word[0:i]+word[i+1:] for i in range(n)]
    # 移动一个字母
    transposes = [word[0:i]+word[i+1]+word[i]+word[i+2:] for i in range(n-1)]
    # 替换一个字母
    replaces = [word[0:i]+c+word[i+1:] for i in range(n) for c in alphabet]
    # 插入一个字母
    inserts = [word[0:i]+c+word[i:] for i in range(n+1) for c in alphabet]
    return set(deletes + transposes + replaces + inserts)
 
#实现编辑距离为2的操作,也就是说只改变两个字母,并且是已知的正确的词
def known_edits2(word):
    return set(e2 for e1 in edits1(word) for e2 in edits1(e1) if e2 in NWORDS)
 
def known(words):
    return set(w for w in words if w in NWORDS)
 
def correct(word):
    candidates = known([word]) or known(edits1(word)) or known_edits2(word) or [word]
    # max
    return max(candidates, key=lambda w: NWORDS[w])
 
if __name__ == "__main__":
    word = "agret"
    print correct(word)
  • 自学学习能力强一点的同学,可以先仔细看一下代码,理解一下他的意思,尽可能的看明白。学习能力较弱的同学,最好能看一遍,能理解多少算多少,但是不要一点都不理解,就往下看,那只会打击您的信心,让你觉得应该提早放弃进入人工智能的大门。

  • 在51行,可以设置word的值,这里是设置的“agret”这个错别单词,运行python代码后,输出正确的agree这个单词,如下图所示:

你可以将word改为你想输入的错误单词,再运行程序,看输出是不是你想写的单词呢?

2、几点疑问:

(1)、上面的代码什么意思,基于什么样的原理。老师,您能不能讲一讲,我们们脑子转不了那么快呢!

(2)、我输入heav,代码给我返回的是head,但其实我想要的是heave这个词,拼写检查器不准确啊?

带着这两个问题,你们随意摆姿势,是躺着看,站着看,还是坐着看,你们随便。老师只能辛苦的码字了,不过我很愿意给大家分享这些姿势(知识),对了,我比较喜欢的是站立式。哈哈。

上面的代码使用了概率论,那么我们先复习一下概率论的相关知识吧。

四、学学条件概率

概率完全从头来讲,似乎不现实。因为,我们不可能重新给大家上一次高中或大学的课程,只有用到哪里复习到哪里了,遗憾!如果看完本课确实觉得相关的概率知识很难,可以看一下我们从《人工智能中级课程》、《人工智能高级课程》的视频部分,那里有关系概率的一些讲解。

首先,要明白P(我们的猜测的词 | fuk)的意思:

要明白P(我们的猜测的词 | fuk)是什么意思?请听下面的解释:

1、普通概率

首先复习一下P(A)是什么?P(A)表示概率, 例如:P(今天下雨),表示今天下雨的概率。 P(今晚怀孕),表示今天晚上怀孕的概率。

2、条件概率

条件概率 P(A|B)的形式表示条件概率,例如: P(今天下雨|今天打雷),表示,今天打雷的条件下,今天下雨的概率。这就是条件概率。 P(今晚怀孕|今天做10次),表示今晚做10次,今天怀孕的概率,这也是条件概率,其实这个概率应该和P(今晚怀孕|今天做1次)的概率应该差不多,所以不用太努力的。

拼写检查程序要计算的条件概率:P(我们的猜测的词 | fuk)。 P(我们的猜测的词 | fuk)是什么意思,这是一个条件概率,表示:小沈阳输入fuk的情况下,他实际想输入“我们的猜测的词 ”的概率。

P(我们的猜测的词 | fuk)可以具体化为P(fuck|fuk),fuck是猜测的词的其中一个,我们可能猜测出多个词,所以,可以得到一系列的概率,如下: P(fuke|fuk)、P(fuck|fuk)、P(fulk|fuk)、P(furk|fuk)、P(fcuk|fuk)、…… 。

上面的概率,大家完全理解了其意思了吗? 上面的概率,哪个最大,那么fuk,就越可能是哪个词。例如P(fuck|fuk)概率为0.9最大,那么用户最有可能想写fuck,但是写成了fuk。

上面的问题大家看明白了吗?如果没有看明白,可以问首页下方的老师,或者在页面中留言,我们尽力回答,谢谢。

五、计算P(我们的猜测的词 | fuk)的概率

一个错词fuk,可能对应很多正确的词,现在,我们剩下的任务就是计算每个P(我们的猜测的词 | fuk)的概率。找出最大的概率,即可找出最合适的“我们猜测的词”

现在的问题是P(我们的猜测的词 | fuk)怎么计算?根据贝叶斯定理,它应该等于:

P(我们的猜测的词 | fuk) = P(fuk|我们的猜测的词)P(我们的猜测的词) / P(fuk)

这个公式就是贝叶斯公式,如果对贝叶斯公式有疑惑,可以查询一下相关资料,或者老师会在合适的时候,推出一集关于贝叶斯公式的视频。贝叶斯公式如下:

将我们猜测的词带入上面的公式,那么得到以下的一些概率:

1
2
3
4
5
P(fuke|fuk) = P(fuk|fuke)P(fuke) / P(fuk) 
P(fuck|fuk) = P(fuk|fuck)P(fuck) / P(fuk)
P(fulk|fuk) = P(fuk|fulk)P(fulk) / P(fuk) 
P(furk|fuk) = P(fuk|furk)P(furk) / P(fuk) 
P(fcuk|fuk) = P(fuk|fcuk)P(fcuk) / P(fuk)

比较这几个概率,选出最大的一个,作为想输入的字。

首先看看分母,由于所有的这些概率都要计算P(fuk),而我们其实并不知道fuk这个错别字在整个样本空间中出现的概率。也许一次都没出现过,也许出现过很多次。如果一次都没有出现,是不是说 P(fuk)=0等,注意,这里分母不能等于0,所以一次都不出现,我们认为是一个很小的概率。

我们是否真的需要计算出P(fuk)的值,答案是否定的,因为比较几个数的大小,可以把他们相同的部分约去。 什么,同学,还不懂!!!

好吧,看看下面的例子: 5/4 和 6/4 谁大,难道我们要先用计算器算出来吗?不需要吧,他们的分母都包含4,所以约去,直接比较5和6谁大就可以了,最后幼儿园的知识告诉我们6大,ok,再最后小学的知识告诉我们6/4大于5/4。

因为上面几个式子分母都是P(fuk),所以,可以将分母约去。

好的,根据上面的推理,那么P(fuke|fuk) 、P(fuck|fuk)等几个概率,约去 P(fuk),就可以得到如下的表达方式:

1
2
3
4
5
P(fuke|fuk) 对应 P(fuk|fuke)P(fuke)
P(fuck|fuk) 对应P(fuk|fuck)P(fuck) 
P(fulk|fuk) 对应P(fuk|fulk)P(fulk) 
P(furk|fuk) 对应P(fuk|furk)P(furk) 
P(fcuk|fuk) 对应P(fuk|fcuk)P(fcuk)

这里使用了“对应”这个词汇,是因为他们确实不相等了,但是大小关系,还是可以比较出来。 我是不是讲得太仔细了,前戏太多了,没办法,这就是我们的风格,做最简单易懂的《人工智能》课程。

六、公式中哪些概率是真正可以计算出来的

上面的表达式中,我们仔细观察P(fuke) 、P(fuck) 、P(fulk)、P(furk)、P(fcuk)这几个词的概率是可以计算出来的。P(fuke)表示,我们写文章的时候,写出fuke的概率。

1、计算某个词出现的概率的思路

P(fuke) 、P(fuck) 、P(fulk)、P(furk)、P(fcuk)这几个概率和拼写习惯有关。

拼写习惯是一个新概念,什么意思呢?就是比较真实的反应生活中大家写文章,哪些词用得多,哪些词用得少。也就是词语的使用频度。

要真实的反应写作或者生活中大家使用某一个词的频度,我们最好的办法是找几本英文巨著来统计,例如《莎士比亚全集》、《哈利波特》等。

从这些书中统计出现每一个单词的概率,例如我们统计出《莎士比亚全集》中一共有100 0000个单词,发现the出现了5000次,那么the出现的概率是5000/1000000。也就是P(the)=5000/1000000。 当然这是《莎士比亚全集》中the出现的概率,有的同学会说,就统计一本书准确吗?要不要把世界上的所有书都统计一下,然后再来看看the在所有书中出现的概率。 上面同学的想法是正确的,但是不可行,统计所有书的单词工作量太大,要收集很多书,实际操作起来不可行。所以,我们统计一两本书就差不多了。而一两本书,也能够比较准确的反应一些实际情况。

有的同学认为,统计自己的一篇作文就可以了,自己写的作文一共才几百个单词,很多单词出现的概率为0,那么上面最终每个单词出现的概率都为0,就没有比较的意义了。

七、计算P(fuke) 、P(fuck) 、P(fulk)、P(furk)、P(fcuk)的概率

现在就简单了,假设《莎士比亚全集》中一共有1000000个单词,fuke出现了200次,fuck出现了500次,fulk出现了231次,furk出现了301,fcuk出现了241次,那么他们的概率分别是:

1
2
3
4
5
P(fuke) =  200/1000000
P(fuck) =  500/1000000
P(fulk) =  231/1000000
P(furk) =  301/1000000
P(fcuk) =  241/1000000

八、公式中不能被计算出的概率

下面的表达式中,P(fuke) 、P(fuck) 、P(fulk)、P(furk)、P(fcuk)是我们已经能够计算出的概率:

1
2
3
4
5
P(fuke|fuk) 对应P(fuk|fuke)P(fuke) 
P(fuck|fuk) 对应P(fuk|fuck)P(fuck) 
P(fulk|fuk) 对应P(fuk|fulk)P(fulk) 
P(furk|fuk) 对应P(fuk|furk)P(furk)
P(fcuk|fuk) 对应P(fuk|fcuk)P(fcuk)

表达式中,剩下的P(fuk|fuke)、P(fuk|fuck)、P(fuk|fulk)、P(fuk|furk)、P(fuk|fcuk),这几个概率怎么计算呢?只要把这几个概率计算出来,就ok了。

首先明白这几个条件概率的意思。 P(fuk|fuke)表示本来想输入fuke,但是输成fuk的概率。什么情况下,想输入fuke却输成fuk呢? 我觉得有两种情况:

  1. 确实是笔误,输入fuke输成了fuk。

  2. 确实是忘记fuke怎么写,而只记得fuk。

这个概率怎么统计呢?好像是无法统计的。因为,我们确实无法知道在写《莎士比亚全集》的时候,莎士比亚这位编剧,想输入fuke的时候,却输成了fuk,当然就无法求出其概率了。甚至我们自己都无法用一年的时间来统计我们写错各个词的概率。

那怎么办,应该是无法计算吧,那么我们能不能比较呢?比较这几个概率:P(fuk|fuke)、P(fuk|fuck)、P(fuk|fulk)、P(fuk|furk)、P(fuk|fcuk)呢?

一个人写10次fuke,可能会写2次fuk,写100次fuke,不可能写20次fuk吧,因为,第一次写错了,可以原谅,第二次写错了,也可以原谅,但是写错20次也太笨了吧。

所以多数情况下,也许某个人想的是fuke,但是真的写出fuke的概率很大,也即使这个人多次输入错误后,就会写这个词了,他后面就会写正确了。最后P(fuk|fuke)这个的概率就很小了,因为,写得越多,出错的可能就越小。

幸好,我们人类的行为大致是一样的,最后我们可以近似的推断P(fuk|fuck)、P(fuk|fulk)、P(fuk|furk)、P(fuk|fcuk)的概率也很小。小到什么程度,我觉得应该是P(fuk|fuke)、P(fuk|fuck)、P(fuk|fulk)、P(fuk|furk)、P(fuk|fcuk)这几个的概率基本一样。就是莎士比亚写100万字的剧本中,每个错别字出现的概率都一样。

就是说,我所有的错误都是笔误,或者是不小心写错了。我们可以近似的认为,我们写fuck、fulk、furk等,写成fuk的概率其实差不多。其实就是,我写错fuk的时候,其实我知道自己已经写错了,因为每个单词我写了很多遍,我已经知道哪个是正确的,哪个是错误的了。

好了,这就是我们最重要的结论: P(fuk|fuke)、P(fuk|fuck)、P(fuk|fulk)、P(fuk|furk)、P(fuk|fcuk)这几个的概率基本一样。注意,我可没有说完全一样哈,也就是为了不让那些钻牛角尖的同学来质问我,哈哈。

在进一步的讲:P(fuk|fuke)、P(fuk|fuck)、P(fuk|fulk)、P(fuk|furk)、P(fuk|fcuk)这几个的概率基本一样是什么意思呢?我这里的解释是:

对于任何一个词,我们拼写时,拼写错误成其他词的概率基本是一样的。不知道这个解释你满不满意呢!!!

好了,由于时间原因,不想大家学习太累,我们本课就讲到这里,我们下节课再见。