用Python从0开始实现一个中文拼音输入法的思路详解


Posted in Python onJuly 20, 2019

众所周知,中文输入法是一个历史悠久的问题,但也实在是个繁琐的活,不知道这是不是网上很少有人分享中文拼音输入法的原因,接着这次NLP Project的机会,我觉得实现一发中文拼音输入法,看看水有多深,结果发现还挺深的,但是基本效果还是能出来的,而且看别的组都做得挺好的,这次就分 享一下我们做的结果吧。 (注:此文假设读者已经具备一些隐马尔可夫模型的知识)

任务描述

实现一个中文拼音输入法。

经过分析,分为以下几个模块来对中文拼音输入法进行实现:

  • 核心功能包括拼音切分(SplitPinyin.py)
  • HMM模型训练(TrainMatrix.py)
  • Trie树构建与搜索接口实现(PinyinTrie.py)
  • 维特比算法实现以及提供给UI的服务接口(GodTian_Pinyin.py)
  • 最后的UI实现(gui.py)

技术路线

在中文拼音输入法中,我们需要完成拼音序列到汉字序列的转换,比如输入“nihao”,输入法会给出我们想输入的字“你好”,到这里我们就可以问出几个问题:

  • **如何切分拼音? **
  • 如: 用户输入”xiana”, 输入法应该判断用户想输入”xian a”(闲啊) 还是”xia na”(夏娜) 还是”xi an a”(西安啊)?
  • 如何实时给用户以反馈?
  • 对于切分好的拼音,怎样找出用户最想输入的一串中文显示给用户?
  • 用户输入的拼音是错的的情况下,如何容忍这种错误?该如何显示?

也许我们还能问出更多的问题,中文拼音输入法就是这样,总有可以继续抠下去的细节。

那么我们如何解决上面的问题?我们的方案如下:

如何切分拼音?

这 里我们暂时采用最长匹配的方式,也就是说,如果用户输入的首个串是拼音或者是某个合法拼音的前缀,那么我们会继续向后发现,等待用户输入,直到用户输完后 发现这个字符(假设是第n个)与原来n-1个不是合法的拼音也不是合法的拼音的前缀,那么此时将前面n-1串切分成拼音,这就完成了一个拼音的发现,比如 说输入”xiant”(想输xiantian),则我们会扫描这个串,一直到”xian”,到”xiant”的时候发现既不是合法拼音的前缀也不是合法拼 音,那么从t前面划分开,得到”xian't”,同样的道理发现后续的拼音。

在实时任务中,用户即使没有输完我们仍应该显示东西,那么我们先切分 拼音,最多只会有最后一个是不完整的拼音前缀,那么我们将完整的和不完整的分开处理。假设是”xian't”的情况,我们将”xian”放入 viterbi算法中,通过HMM得出概率最大的一个输出串,然后将最后的”t”在训练过的Trie树中搜索出所有以”t”为前缀的字,以及他们出现的频 率,取频率最高的若干个,作为viterbi算法的下一个状态的可能集合,然后得到他们的拼音,与前面n-1个拼音组合起来跑Viterbi算法,得到最 可能的一个中文串,由于这些频率最高的字的拼音(即我们可能的观测值)可能不相同,我们只能将相同音的字作为一次viterbi算法运行的下一状态,这样 viterbi跑的次数就是这些字里面不同音的个数,但是由于总数固定,异音越多,每个音对应的越少,所以总时间是没有差别的。

具体Trie树会在后面讲解。学习过程中有不懂的可以加入我们的学习交流秋秋圈784中间758后面214,与你分享Python企业当下人才需求及怎么从零基础学习Python,和学习什么内容。相关学习视频资料、开发工具都有分享

 用Python从0开始实现一个中文拼音输入法的思路详解

如何实时给用户以反馈?

上 面其实已经初步解释了如何实时反馈,实时反馈我们要做的就是用户每输一个字母,我们就能够显示出用户可能想要打的字,那么,以一个字母开头的拼音有很多, 每个拼音对应的字也可能有很多,也即结果有很多,但是我们又不能漏掉,所以只能考虑所有的字,比较选出概率最大的若干个字,这时候我们可以采用Trie树 来解决。Trie树就是前缀树,说白了就是将拼音的字母按顺序顺着根插入到树中,每个叶子节点就是一个拼音,这个拼音就是顺着根一路走下来取的字母的顺序 组合,这样我们就可以找出以任意字符串为前缀的所有拼音,方法就是dfs遍历每一个以其为前缀的子树的叶子节点,这时候我们叶子节点存的其实是一个字 典,key为这个拼音对应的可能的字,value为这个字出现的频率,以作为比较。

对于切分好的拼音,怎样找出用户最想输入的一串中文显示给用户?

这里我们使用隐马尔可夫模型,将用户想输入的中文字作为隐状态,用户输入的拼音为显状态,通过最大似然估计即频率估计出HMM的三个矩阵的值,最后通过viterbi算法找出概率最大的若干个中文字串显示出来。

用户输入的拼音是错的的情况下,如何容忍这种错误?该如何显示?

由于考虑到实现高度容错的复杂性,我们假设用户会输入正确的拼音,在想分割的时候会自行添加分隔符”‘“,由于大部分输入法用户绝大部分时间都会输入正确的拼音,所以,这样一个假设既简化了实现的过程,又没有损失太大的用户体验。

用到的数据

由于训练HMM模型的需要,我们从搜狗实验室找到了SogouQ用户查询数据集,预处理成合法的句子之后大约有360M,且为了避免查询句太短,我们也增加了将近30M的搜狐新闻数据作为训练语料,这里面包含了很多的长句子。

通过这两个语料的训练,我们得到了长句和短句皆可表现较好效果的HMM模型。并且我们还可以继续拓展语料,以增加我们HMM模型的准确性,这是后话,不提。

遇到的问题及解决方案,

UI界面的问题,由于UI设计的复杂性与不同系统的考虑,出现了许多莫名其妙的BUG,这使得我们花了许多时间。

viterbi算法的效率问题,由于以某个字母开头的拼音对应的字有很多个,假设我们取最优的K个,我们需要将这K个与前面已有的拼音组 合,然后跑一遍Viterbi算法,由于Viterbi算法从一个状态转移到另一个状态的计算量很大,我们使用了记忆(cache)的方法来加速,具体方 法就是记录下某一个完整拼音串所对应的viterbi算法的最后一个状态的相关情况,这样如果我们再次遇到这个拼音串(A) 加上另一个拼音(B)跑viterbi的情况,我们就不需要从这个组合串的开头开始跑viterbi算法了,而是直接从A 串跑完viterbi的最后一个状态(从记忆单元读取)开始,向B进行转移。

这个记忆单元会随着程序而一直存在,并且我们对这个对象做了持久化, 在输入法启动时我们会读取这个文件(记忆单元),这也就意味着,如果我们曾经输入过某个拼音串,那么我们以后再输入同样的拼音串的时候,不再需要跑核心算 法,而是直接显示结果,这样在速度上就取得了显著的提高,就会出现,输入法越用越好用,越用越快的好处,当然这牺牲了一些存储空间,但是如今我们都不缺存 储空间。

重复计算的问题,比如在用户觉得打错了的时候,往后退格,这时就会退到某一个前缀,但

是其实这个前缀我们是算过了的,也显示过了的,就是说 我们退回到我们以前显示过的内容的时候,如果不加优化,那么又会重新跑一遍核心的viterbi算法,这样就会很慢,那么我们还是利用cache思想,将 输入的拼音串以及对应的显示结果相对应并且存起来,这样我们就做到了飞速的退格操作。

Python语言固有的性能问题,解决这个问题只有更换语言,事实上用C++语言实现的话我相信会快很多,这在后面可以考虑用C++实现,这也是完全可行的。

性能评价

输入比较迅速,绝大多数输入能在1秒以内显示。输入过的句子再输入和退格操作都是毫秒级别的。

给出程序的运行环境

1.Python 2.7

2.需要安装的Python包: Tkinter, cPickle, pypinyin等模块 执行方法及参数

在项目Project目录下,运行

$ python gui.py

即可。

Future Works

由上面我们可以看到其实可以做的工作还很多,比如

  • 改换编译型语言,如C++,大幅减小计算开销
  • 不断随着用户的输入更新HMM模型
  • 将软件嵌入系统中
  • 我们观察到,长句输入很少有多个是想打的,不想短句可能想打的情况很多,所以很多与输入拼音串长度相同的句子我们可以换成短句

总结

以上所述是小编给大家介绍的用Python从0开始实现一个中文拼音输入法的思路详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Python 相关文章推荐
python实现简单的TCP代理服务器
Oct 08 Python
python通过线程实现定时器timer的方法
Mar 16 Python
python监控键盘输入实例代码
Feb 09 Python
Python开发的十个小贴士和技巧及长常犯错误
Sep 27 Python
python实现随机漫步方法和原理
Jun 10 Python
python安装scipy的步骤解析
Sep 28 Python
TensorFlow实现指数衰减学习率的方法
Feb 05 Python
Python3实现飞机大战游戏
Apr 24 Python
使用Python实现将多表分批次从数据库导出到Excel
May 15 Python
基于TensorFlow的CNN实现Mnist手写数字识别
Jun 17 Python
python集合的新增元素方法整理
Dec 07 Python
python搜索包的路径的实现方法
Jul 19 #Python
Pycharm 文件更改目录后,执行路径未更新的解决方法
Jul 19 #Python
在自动化中用python实现键盘操作的方法详解
Jul 19 #Python
Python的Tkinter点击按钮触发事件的例子
Jul 19 #Python
python实现图片九宫格分割
Mar 07 #Python
django最快程序开发流程详解
Jul 19 #Python
python打印9宫格、25宫格等奇数格 满足横竖斜相加和相等
Jul 19 #Python
You might like
php foreach 使用&(与运算符)引用赋值要注意的问题
2010/02/16 PHP
360通用php防护代码(使用操作详解)
2013/06/18 PHP
php编程每天必学之验证码
2016/03/03 PHP
javascript 读取XML数据,在页面中展现、编辑、保存的实现
2009/10/27 Javascript
setTimeout与setInterval在不同浏览器下的差异
2010/01/24 Javascript
js汉字转拼音实现代码
2013/02/06 Javascript
javascript教程之不完整的继承(js原型链)
2014/01/13 Javascript
jquery教程限制文本框只能输入数字和小数点示例分享
2014/01/13 Javascript
jQuery.lazyload+masonry改良图片瀑布流代码
2014/06/20 Javascript
jQuery实现左右切换焦点图
2015/04/03 Javascript
Jquery操作Ajax方法小结
2015/11/29 Javascript
JavaScript暂停和继续定时器的实现方法
2016/07/18 Javascript
Nodejs中解决cluster模块的多进程如何共享数据问题
2016/11/10 NodeJs
ES6(ECMAScript 6)新特性之模板字符串用法分析
2017/04/01 Javascript
快速解决处理后台返回json数据格式的问题
2018/08/07 Javascript
JS基于Location实现访问Url、重定向及刷新页面的方法分析
2018/12/03 Javascript
node.js学习笔记之koa框架和简单爬虫练习
2018/12/13 Javascript
JS实现商品橱窗特效
2020/01/09 Javascript
Python 的 Socket 编程
2015/03/24 Python
利用Python爬取微博数据生成词云图片实例代码
2017/08/31 Python
python队列queue模块详解
2018/04/27 Python
python 实现在txt指定行追加文本的方法
2018/04/29 Python
Python 删除整个文本中的空格,并实现按行显示
2018/07/24 Python
python绘制中国大陆人口热力图
2018/11/07 Python
python简单实现AES加密和解密
2019/03/28 Python
selenium跳过webdriver检测并模拟登录淘宝
2019/06/12 Python
python web框架 django wsgi原理解析
2019/08/20 Python
简单介绍一下pyinstaller打包以及安全性的实现
2020/06/02 Python
keras 简单 lstm实例(基于one-hot编码)
2020/07/02 Python
蛋白质世界:Protein World
2017/11/23 全球购物
80年代复古T恤:TruffleShuffle
2018/07/02 全球购物
马来西亚在线药房:RoyalePharma
2019/12/01 全球购物
是否有自动比较结构的方法
2015/06/03 面试题
物理学专业求职信
2014/07/04 职场文书
源码解读Spring-Integration执行过程
2021/06/11 Java/Android
clear 万能清除浮动(clearfix:after)
2023/05/21 HTML / CSS