用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版
Dec 07 Python
python实现下载文件的三种方法
Feb 09 Python
详解Python读取配置文件模块ConfigParser
May 11 Python
Python实现的文本编辑器功能示例
Jun 30 Python
python安装Scrapy图文教程
Aug 14 Python
基于python OpenCV实现动态人脸检测
May 25 Python
OpenCV搞定腾讯滑块验证码的实现代码
May 18 Python
在Pycharm中调试Django项目程序的操作方法
Jul 17 Python
PyTorch笔记之scatter()函数的使用
Feb 12 Python
使用Python绘制台风轨迹图的示例代码
Sep 21 Python
5款实用的python 工具推荐
Oct 13 Python
python 爬取英雄联盟皮肤并下载的示例
Dec 04 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
用C/C++扩展你的PHP 为你的php增加功能
2012/09/06 PHP
PHP错误提示的关闭方法详解
2013/06/23 PHP
php5.2以下版本无json_decode函数的解决方法
2014/05/25 PHP
PHP 如何获取二维数组中某个key的集合
2014/06/03 PHP
PHP单例模式模拟Java Bean实现方法示例
2018/12/07 PHP
RSA实现JS前端加密与PHP后端解密功能示例
2019/08/05 PHP
laravel-admin自动生成模块,及相关基础配置方法
2019/10/08 PHP
PHP重载基础知识回顾
2020/09/10 PHP
extjs 学习笔记 四 带分页的grid
2009/10/20 Javascript
setTimeout的延时为0时多个浏览器的区别
2012/05/23 Javascript
javascript实现数字+字母验证码的简单实例
2014/02/10 Javascript
jQuery实现的类似淘宝网站搜索框样式代码分享
2015/08/24 Javascript
JS与jQuery遍历Table所有单元格内容的方法
2015/12/07 Javascript
jQuery简单实现彩色云标签效果示例
2016/08/01 Javascript
微信小程序(应用号)简单实例应用及实例详解
2016/09/26 Javascript
JavaScript基于对象去除数组重复项的方法
2016/10/09 Javascript
JS轮播图中缓动函数的封装
2020/11/25 Javascript
详解Vuejs2.0 如何利用proxyTable实现跨域请求
2017/08/03 Javascript
关于js对textarea换行符的处理方法浅析
2018/08/03 Javascript
解决idea开发遇到javascript动态添加html元素时中文乱码的问题
2020/09/29 Javascript
[10:49]2014国际邀请赛 叨叨刀塔第二期为真正的电竞喝彩
2014/07/21 DOTA
[01:06:43]完美世界DOTA2联赛PWL S3 PXG vs GXR 第二场 12.19
2020/12/24 DOTA
PyTorch CNN实战之MNIST手写数字识别示例
2018/05/29 Python
pyspark 读取csv文件创建DataFrame的两种方法
2018/06/07 Python
安装2019Pycharm最新版本的教程详解
2019/10/22 Python
在HTML5 canvas里用卷积核进行图像处理的方法
2018/05/02 HTML / CSS
高中军训感想300字
2014/03/04 职场文书
关于环保的建议书400字
2014/03/12 职场文书
平安建设实施方案
2014/03/19 职场文书
个人反四风对照检查材料思想汇报
2014/09/23 职场文书
开学第一周值周总结
2015/07/16 职场文书
小学英语教学随笔
2015/08/14 职场文书
2016优秀员工先进事迹材料
2016/02/25 职场文书
七个Python必备的GUI库
2021/04/27 Python
mysql中数据库覆盖导入的几种方式总结
2022/03/25 MySQL
Python使用Web框架Flask开发项目
2022/06/01 Python