机器学习:单词拼写纠正器python实现
主要推送关于对算法的思考以及应用的消息。培养思维能力,注重过程,挖掘背后的原理,刨根问底。本着严谨和准确的态度,目标是撰写实用和启发性的文章,欢迎您的关注。
01
—
朴素贝叶斯分类实战
前面介绍了贝叶斯的基本理论,朴素贝叶斯分类器,拉普拉斯修正,文章的链接如下:
机器学习:说说贝叶斯分类
朴素贝叶斯分类器:例子解释
朴素贝叶斯分类:拉普拉斯修正
在这3篇推送中用例子详细阐述了贝叶斯公式和朴素贝叶斯如何做分类,以及如何修正一些属性某些取值概率。
下面,借助朴素贝叶斯分类器的基本思想,编写一个单词拼写纠正器,它大致实现的功能如下:
- 如果用户输入的单词存在,则直接提示在字典中发现,并返回
- 如果单词不在词典中,纠正器会猜测用户的可能输入,然后做出最多两步的距离调整,并返回纠正后,用户最可能想输入的前三个单词
- 如果经过最多的两步调整后,还是未找到,则提示想输入的单词在字典中不存在。
02
—
纠正器实现原理
1 如用户输入了 hella,纠正后发现的3个最有可能的输入如下:
'want to input: hello', 'hell', 'fella'
2 如用户输入了appropreate,纠正器纠正后:
'want to input: appropriate'
3 如用户输入了owesomes,纠正器纠正后:
'want to input: awesome'
4 如用户输入了grduallyare,纠正器纠正后:
grduallyare not found in dictionary!
以上是纠正器能实现的纠正实例,那么该如何实现这么一个单词拼写错误检查和纠正的工具呢。
如果用户实际输入的单词为 w(word的简写), 然后拼写纠正器猜测用户实际想输入的单词为 c1, c2 , c3 , ....... 因此,我们可以猜测用户输入了 P(c1 | w) ,P(c2 | w),P(c3 | w)等等这些多种猜测。如果发现P(c1 | w) 的概率最大,那么用户很有可能想输入的那个单词为 c1 。这个概率可以统一表示为:
P(c | w)
如何求解这个概率的最大值?
将以上概率做如下转化来求解:用户想输入的很可能在语料库的这个 c 时,有可能被错误的输入为了 w1,w2,w3 ,...... 则这个概率可以统一表示为:
P(w | c)
用户错误地输入成 w1,w2,w3,......,它们之间是相互独立的,因此可以根据朴素贝叶斯分类器的理论,进一步将后验概率 P(c | w) 的求解转化为求解如下的目标函数:
max ( P(c) * P(w | c) / P(w) )
上式中 P(c)为先验概率,下载一个比较丰富的单词拼写都正确的英文单词库后,统计下每个单词出现的频次,就是单词 c 的出现的概率;
P(w) 是与问题分类无关的量,因为用户有可能输入任意一个单词;
P(w | c) 是一个类条件概率:用户想输入c(c在语料库中是有对应的,在此处需要注意:我们取的语料库不能100%保证一定存在任意一个正确的单词,所以在统计的过程中,假定单词至少出现1次),但是被错误地输入为了 wi 的概率。
P(w | c) 的求解方法通常会有很多种,比如用户想输入hello,但是实际输入了 hella,它们之间的区别仅仅是最后一个字符输入错误,这个出现的概率还是挺大的吧;但是,再看看下面这个例子。
如果用户想输入awesome, 但是实际输入成了owesomes,输错了1个字符,多添加了 1个字符,这种情况发生的概率就比上面那种小一些吧。
因此,在本文中设计的纠正器没有直接去量化 P(w | c) 这个概率,而是采取了从定性上进行分析,通常经过一步调整出现的概率大于经过两步调整出现的概率。所以,当纠正器遇到一个待纠正的词语时,它会纠正一步,如果发现了,就直接返回了;否则才会进行两步调整,这种调整的优先级的原理是根据 P(w | c) 。
这样先验概率 P(c) 和类条件概率 P(w | c) 的求解方法就弄明白了,当一步纠正就能在语料库找到对应后,就不会进行两步纠正,但是一步纠正会返回多个,此时再根据P(c)找出这些中的出现频次最多的,这样最终的结果便是猜测到的用户最有可能想输入的单词。
03
—
纠正器Python代码
构建先验概率P(c),语料库下载了老友记的1-10部+呼啸山庄全部组成的单词库。
import re, collections
def tolower(text):
return re.findall('[a-z]+',text.lower())
def prior(cwords):
model = collections.defaultdict(lambda:1)
for f in cwords:
model[f]+=1
return model
ipath = './bigword.txt'
uipath = ipath.encode("utf8")
htxt = open(uipath,'r',errors ='ignore')
cwords = tolower(htxt.read())
#get P(c)
nwords = train(cwords)
nwords
类条件概率
alpha = 'abcdefghijklmnopqrstuvwxyz'
#一步调整
def version1(word):
n = len(word)
add_a_char = [word[0:i] + c + word[i:] for i in range(n+1) for c in alpha]
delete_a_char = [word[0:i] + word[i+1:] for i in range(n)]
revise_a_char = [word[0:i] + c + word[i+1:] for i in range(n) for c in alpha]
swap_adjacent_two_chars = [word[0:i] + word[i+1]+ word[i]+ word[i+2:] for i in range(n-1)]
return set( add_a_char + delete_a_char +
revise_a_char + swap_adjacent_two_chars)
#两步调整
def version2(word):
return set(e2 for e1 in edits1(word) for e2 in edits1(e1))
朴素贝叶斯分类器
def identify(words):
return set(w for w in words if w in nwords)
def getMax(wanteds):
threewanteds=[]
maxword = max(wanteds,key=lambda w : nwords[w])
threewanteds.append('want to input: '+ maxword)
wanteds.remove(maxword)
if len(wanteds)>0:
maxword = max(wanteds,key=lambda w : nwords[w])
threewanteds.append(maxword)
wanteds.remove(maxword)
if len(wanteds)>0:
maxword = max(wanteds,key=lambda w : nwords[w])
threewanteds.append(maxword)
return threewanteds
def bayesClassifier(word):
#如果字典中有输入的单词,直接返回
if identify([word]):
return 'found: '+ word
#一步调整
wanteds = identify(version1(word))
if len(wanteds)>0:
return getMax(wanteds)
#两步调整
wanteds = identify(version2(word))
if len(wanteds)>0:
return getMax(wanteds)
#不再修正,直接提示这个单词不在当前的词典中
else:
return [word + ' not found in dictionary!' ]
测试1 :
测试2 :
测试3 :
测试4 :
如有需要这个拼写检查器的Jupyter notebook的,想自己亲自实践下的,请@我。
- 从源代码到Runtime发生的重排序编译器重排序指令重排序内存系统重排序阻止重排序
- 内存屏障保证缓存一致性优化
- 最新XSS 0day漏洞来袭,影响最新版本IE浏览器(含POC)
- Java内存模型—JMMhappens-before规则
- 那些年我们一起用过的Hybrid App
- 来看看美帝人民的安全意识:安全研究人员指责iOS版Outlook存在多处安全隐患
- 不是原配也可以-对接非原生配体
- oj放苹果
- 漏洞追踪:最新IE UXSS漏洞技术分析
- 如何使用Python超参数的网格搜索ARIMA模型
- int string相互转换
- Android 增量更新和升级
- web服务器集群集群是什么?分布式是什么?集中式是什么?例子缺点
- 图的遍历算法
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 如何实现一个圆弧倒计时进度条
- R语言Kaggle泰坦尼克号性别阶级模型数据分析案例
- 以图搜图系统概述
- GitHub Pages 配置 letsencrypt 开启HTTPS
- R语言中ARMA,ARIMA(Box-Jenkins),SARIMA和ARIMAX模型用于预测时间序列数据
- 以图搜图系统工程实践
- R语言线性判别分析(LDA),二次判别分析(QDA)和正则判别分析(RDA)
- 用R语言实现神经网络预测股票实例
- R语言社区主题检测算法应用案例
- C++ vector学习笔记
- 锂电池充电慢?手把手教你制作锂电池快充充电器
- c++ cin, get学习笔记
- c++ sort 学习笔记
- CSS3 引入方式 注释 颜色属性 学习笔记
- 使用 Makefile 构建指令集