【阿旭机器学习实战】【28】自己动手写一个单词拼写检查器---基于贝叶斯公式

【阿旭机器学习实战】系列文章主要介绍机器学习的各种算法模型及其实战案例,欢迎点赞,关注共同学习交流。

本文主要介绍了拼写检查器的基本原理,并且通过贝叶斯公式自己动手写了一个单词拼写检查器。可用于错误单词拼写的检查。

1. 拼写检查器基本原理—基于贝叶斯概率公式

问题:
在所有正确的拼写词中, 我们想要找一个正确单词 c, 使得其对于错误单词w 的条件概率最大。

问题求解:
P(c|w) = P(w|c) P( c ) / P(w)
比如:appla是条件w,apple和apply是正确的词c,对于apple和apply来说P(w)都是一样的,所以我们在上式中忽略它, 写成: P(w|c) P( c )

  • P( c )表示文章中出现这个正确拼写的词 c 的概率, 也就是说, 在英语文章中, c 出现的概率有多大。
    假设可以认为单词在文章中出现的概率越大,则正确拼写的概率就越大,可以用单词出现次数来代替这个量。好比说, 英语中出现 the 的概率 P(‘the’) 就相对高, 而出现 P(‘zxzxzxzyy’) 的概率接近0(假设后者也是一个词的话).
  • P(w|c), 在用户想键入 c 的情况下敲成 w 的概率。这个是代表用户会以多大的概率把 c 敲错成 w。

2. 构建单词集并统计单词数

import re
# 我们导入一个比较大的文本集,并且统计文本中各单词数量
text = open('text.txt').read()
# 展示文本中前1000个字符
text[:1000]
'The Project Gutenberg EBook of The Adventures of Sherlock Holmes\nby Sir Arthur Conan Doyle\n(#15 in our series by Sir Arthur Conan Doyle)\n\nCopyright laws are changing all over the world. Be sure to check the\ncopyright laws for your country before downloading or redistributing\nthis or any other Project Gutenberg eBook.\n\nThis header should be the first thing seen when viewing this Project\nGutenberg file.  Please do not remove it.  Do not change or edit the\nheader without written permission.\n\nPlease read the "legal small print," and other information about the\neBook and Project Gutenberg at the bottom of this file.  Included is\nimportant information about your specific rights and restrictions in\nhow the file may be used.  You can also find out about how to make a\ndonation to Project Gutenberg, and how to get involved.\n\n\n**Welcome To The World of Free Plain Vanilla Electronic Texts**\n\n**eBooks Readable By Both Humans and By Computers, Since 1971**\n\n*****These eBooks Were Prepared By Thousan'
# 将文本中所有的单词取出,并将其转小写,只保留a-z字符
words = re.findall('[a-z]+', text.lower())
# 统计每个单词的出现次数
dic_words = {}
for t in words:
    dic_words[t] = dic_words.get(t,0) + 1
# 展示前10个单词及其数目
list(dic_words.items())[:10]
[('the', 80030),
 ('project', 288),
 ('gutenberg', 263),
 ('ebook', 87),
 ('of', 40025),
 ('adventures', 17),
 ('sherlock', 101),
 ('holmes', 467),
 ('by', 6738),
 ('sir', 177)]

3. 计算单词间的距离,并返回候选单词

3.1 编辑距离:

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

3.2 计算与拼写单词编辑距离为1、2的正确单词

统计编辑距离为1的单词集

# 字母表
alphabet = 'abcdefghijklmnopqrstuvwxyz'

#返回所有与单词 word 编辑距离为 1 的集合
def one_edit(word):
    n = len(word)
    # 1次删除操作的单词集
    del_words = [word[0:i]+word[i+1:] for i in range(n)]
    
    # 1次交换操作的单词集
    trans_words = [word[0:i]+word[i+1]+word[i]+word[i+2:] for i in range(n-1)]
    
    # 1次替换操作的单词集
    alter_words = [word[0:i]+c+word[i+1:] for i in range(n) for c in alphabet]
    
    # 1次插入操作的单词集
    insert_words = [word[0:i]+c+word[i:] for i in range(n+1) for c in alphabet]
    
    one_edit_words = set(del_words + trans_words + alter_words + insert_words)
    
    return one_edit_words

统计编辑距离为2的单词集

def two_edit(word):
    two_edit_words = set()
    for e1 in one_edit(word):
        for e2 in one_edit(e1):
            two_edit_words.add(e2)
    return two_edit_words
e1 = one_edit('something')
e2 = two_edit('something')
len(e1) + len(e2)
114818

与单词something 编辑距离为1或者2的单词居然达到了 114818 个,这里我们可以进行优化,仅选取那些正确的词作为候选词。

3.3 将编辑距离小于2的词中,选正确的词作为候选词

all_edit_words = e1 | e2
correct_word = []
dict_words = set(dic_words.keys())
for word in all_edit_words:
    if word in dict_words:
        correct_word.append(word)
print(correct_word)
['soothing', 'seething', 'something', 'smoothing']

我们可以看到,优化之后只返回 4 个单词: [‘soothing’, ‘seething’, ‘something’, ‘smoothing’]

4. 编写单词拼写检查器

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

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

一般把hello打成hallo的可能性比把hello打成halo的可能性大。

4.1 定义拼写检查器-----返回最优单词

def known(words):
    # 返回正确的单词集合
    w = set()
    for word in words:
        if word in dic_words:
            w.add(word)
    return w

# 先计算编辑距离,再根据编辑距离找到最匹配的单词
def correct(word):
    # 获取候选单词
    # 判断单词是否正确,若正确则直接返回
    if word in dic_words:
        return word
    
    # 如果known(set)非空, candidates 就会选取这个集合, 而不继续计算后面的,优先为1次编辑距离>2次编辑距离
    candidates = known(one_edit(word)) or known(two_edit(word)) or word
    # 若字典中不存在相近的词,则直接返回该单词
    if word == candidates:
        return word
    # 返回频率最高的词
    max_num = 0
    for c in candidates:
        if dic_words[c] >= max_num:
            max_num = dic_words[c]
            candidate = c
    return candidate

4.2 拼写器使用举例

correct('learw')
'learn'
correct('tess')
'less'
correct('appl')
'apply'
correct('fdsfsdgergg')
'fdsfsdgergg'

如果内容对你有帮助,感谢点赞+关注哦!

更多干货内容持续更新中…

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿_旭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值