详细代码已开源,可查看night-killer/HMM_pinyin: 一个基于HMM的拼音输入法 (github.com)

一、文件说明

HMM_pinyin└── corpus // 语料└── params // 模型参数└── init_prob.json // 初始概率矩阵└── emiss_prob.json // 发射概率矩阵└── trans_prob.json // 转移概率矩阵└── pinyin_states.json // 同音字记录 └──  preprocessing.py // 预处理语料的脚本,用于过滤杂乱字符└── count_for_hmm.py // 计算HMM模型所需要的参数,即params下的文件└── pysplit.py // 用于处理拼音序列切分的模块└── hmm.py // HMM类,包含viterbi算法实现部分└── input_method.ui // 输入法ui界面└── input_method.py // 根据ui界面对应生成的python代码└── input_method_slots.py // 定义输入法的接口和响应动作└── main.py // 程序的入口└── readme.md└── readme.pdf

二、功能简介

实现一个简单的基于HMM的拼音输入法

三、原理

汉语有很多字拼音相同。可以根据上下文来挑选概率大的字。如“wo”可对应汉字“我、窝、握、卧”等,“de”也可以对于“的、得、地、德”等。但是“wode”对应的概率比较大的应该属于“我的”。

我们把拼音作为观察值,把汉字当作状态,那么可以用HMM来建模拼音输入。转移概率可从语料库训练而得,生成概率则需考虑多音字(在不知道多音字概率的情况下可假设等概率)。

四、HMM模型&viterbi算法

4.1、HMM模型

  1. 马尔可夫性可描述为
    ∀h,s≤t,P(X(t+h)=y∣X(s)=x(s))=P(X(t+h)=y∣X(t)=x(t))\forall h,s\le t,\mathrm{P}(X(t+h)=y|X(s)=x(s))=\mathrm{P}(X(t+h)=y|X(t)=x(t)) ∀h,s≤t,P(X(t+h)=y∣X(s)=x(s))=P(X(t+h)=y∣X(t)=x(t))

  2. 三个重要参数

    • 初始概率矩阵π:πi=P(q1=i)\pi:\pi_i=\mathrm{P}(q_1=i)π:πi​=P(q1​=i)
    • 转移概率矩阵a:aij=P(qt+1=j∣qt=i)a:a_{ij}=\mathrm{P}(q_{t+1}=j|q_t=i)a:aij​=P(qt+1​=j∣qt​=i)
    • 发射概率矩阵b:bj(k)=P(ot=k∣qt=j)b:b_j(k)=\mathrm{P}(o_t=k|q_t=j)b:bj​(k)=P(ot​=k∣qt​=j)
  3. 本次实验求解问题为HMM的预测问题,即已知模型参数和观测序列,求解对应的状态序列。

4.2、viterbi算法

  1. 初始化:
    δ1(i)=πibi(o1),1≤i≤N\delta_1(i)=\pi_ib_i(o_1),1\le i\le N δ1​(i)=πi​bi​(o1​),1≤i≤N

    Φ1(i)=0\Phi_1(i)=0 Φ1​(i)=0

  2. 迭代求解:
    δt(j)=max1≤i≤Nδt−1(i)aijbj(ot)\delta_t(j)=\mathrm{max}_{1\le i\le N}\delta_{t-1}(i)a_{ij}b_j(o_t) δt​(j)=max1≤i≤N​δt−1​(i)aij​bj​(ot​)

    Φt(j)=argmax1≤i≤Nδt−1(i)aijbj(ot)\Phi_t(j)=\mathrm{arg max}_{1\le i\le N}\delta_{t-1}(i)a_{ij}b_j(o_t) Φt​(j)=argmax1≤i≤N​δt−1​(i)aij​bj​(ot​)

  3. 终止:
    P∗=max1≤i≤NδT(i)\mathrm{P^*}=\mathrm{max}_{1\le i\le N}\delta_T(i) P∗=max1≤i≤N​δT​(i)

    qT∗=argmax1≤i≤NδT(i)q_T^*=\mathrm{argmax}_{1\le i\le N}\delta_T(i) qT∗​=argmax1≤i≤N​δT​(i)

  4. 最优路径(隐状态序列)回溯:
    qt∗=Φt+1(qt+1∗),t=T−1,T−2,...,2,1q_t^*=\Phi_{t+1}(q_{t+1}^*),t=T-1,T-2,...,2,1 qt∗​=Φt+1​(qt+1∗​),t=T−1,T−2,...,2,1

五、实验过程

5.1、语料获取与处理

由于原语料文件corpus/pinyin_train.txt中语料有限,且拼音标注文件pinyin.txt中拼音标注有限,因此我从网上找了一份人民日报2014年的新闻语料corpus/2014_corpus.txt作为补充,同时利用pypinyin对语料进行拼音标注来生成自己的拼音标注库。

由于原始语料中存在许多不需要的符号和字符,所以编写了preprocessing.py来对原始语料进行过滤提取,提取使用正则表达式。

chinese = re.compile(r'[\u4E00-\u9FA5`~!@#$%^&*()_\-+=<>?:"{}|,.;·~!@#¥%……&*()——\-+={}|《》?:“”【】、;‘,。、|\n]{1,}')

最后生成预处理语料corpus/2014_corpus_pre.txt

5.2、模型参数计算

在HMM类中使用了以下5项数据实现:

  1. init_prob:存储汉字的初始概率矩阵。

    {"汉字1": 概率1,"汉字2": 概率2,// ..."汉字n":概率n
    }
    
  2. trans_prob:存储汉字之间的转移概率矩阵(采用二元模型)。

    {"汉字1": {"前置字1": 概率11, "前置字2": 概率12, /*...*/, "前置字n": 概率1n},"汉字2": {"前置字2": 概率21, "前置字2": 概率22, /*...*/, "前置字m": 概率2m},// ..."汉字w": {"前置字1": 概率w1, "前置字2": 概率w2, /*...*/, "前置字t": 概率wt}}
    
  3. emiss_prob:存储汉字到拼音的转移概率矩阵。

    {"汉字1": {"拼音1": 概率11, "拼音2": 概率12, /*...*/, "拼音n": 概率1n},"汉字2": {"拼音2": 概率21, "拼音2": 概率22, /*...*/, "拼音m": 概率2m},// ..."汉字w": {"拼音1": 概率w1, "拼音2": 概率w2, /*...*/, "拼音t": 概率wt}}
    
  4. pinyin_states:存储同一个拼音的多个汉字,供计算时遍历使用。

    {"拼音1": ["汉字1", "汉字2", /*...*/, "汉字n"],"拼音2": ["汉字2", "汉字2", /*...*/, "汉字m"],// ... "拼音w": ["汉字1", "汉字2", /*...*/, "汉字t"]}
    
  5. pyList:存储拼音规则表。

    ["合法的拼音1", "合法的拼音2", ..., "合法的拼音n"]
    

在计算时,首先从语料中提取汉字句段,通过正则表达式实现。

chinese = re.compile(r'[\u4e00-\u9fa5]{2,}')

然后,在每个句段的前后加上BOSEOS分别作为句首和句尾的标识符,方便进行处理和矩阵的统一化存储。

在计算好1-4矩阵的概率后,以上述1-4的格式作为json文件进行存储(因为会产生高维稀疏矩阵,所以不采用传统的矩阵方式便于压缩空间)1

最后,通过以下拼音规则求出pyList2

  1. 根据拼音规则得出以下三个列表

    smList = ['b', 'p', 'm', 'f', 'd', 't', 'n', 'l', 'g', 'k', 'h', 'j', 'q', 'x', 'z', 'c', 's', 'r', 'zh', 'ch', 'sh', 'y', 'w'] // 声母表
    ymList = ['a', 'o', 'e', 'i', 'u', 'v', 'ai', 'ei', 'ui', 'ao', 'ou', 'iu', 'ie', 've', 'er', 'an', 'en', 'in', 'un', 'ang', 'eng', 'ing', 'ong', 'uai', 'ia' ,'uan' ,'uang', 'uo', 'ua'] // 韵母表,为了简化规则,表中除了韵母外也增加了另一些拼音组合
    ztrdList = ['a', 'o', 'e', 'ai', 'ei', 'ao', 'ou', 'er', 'an', 'en', 'ang', 'zi', 'ci', 'si', 'zhi', 'chi', 'shi', 'ri', 'yi', 'wu', 'yu', 'yin', 'ying', 'yun', 'ye', 'yue', 'yuan'] // 整体认读音节表
    
  2. 然后将smList中的字符串和ymList中的字符串进行组合可构成合法的拼音(即声母和韵母组合),将ztrList中的字符串直接作为合法的拼音(整体认读音节本身就是合法的拼音)。

5.3、拼音解码为汉字实现

viterbi算法实现时为了防止出现精度爆炸现象,所以对每个概率都做了log平滑,且将乘法转换为加法防止数位溢出

self.min_f = -3.14e+100 # 用于log平滑时所取的最小值,用于代替0
  1. 将输入的拼音字符串根据pyList表和设定规则切分为不同的拼音组合,通过pysplit文件中的函数实现(采用递归方式即深度优先搜索找出所有合法拼音切分)。

  2. 生成数组viterbi用于记录每个位置上各状态的概率,格式如下:

    viterbi[pos][word] = (probability, pre_word)
    # pos是目前节点的位置,word为当前汉字即当前状态,probability为从pre_word上一汉字即上一状态转移到目前状态的概率
    

viterbi算法实现部分

  1. 针对每个拼音切分,首先根据第一个拼音,从pinyin_states中找出所有可能的汉字s,然后通过init_prob得出初始概率,通过emiss_prob得出发射概率,从而算出viterbi[0][s]。

    viterbi[0][s] = (self.init_prob.get(s, self.min_f) + self.emiss_prob.get(s, {}).get(seq[n][0], self.min_f), -1) # seq[n][0]为第一个拼音
    
  2. 同样遍历pinyin_states,找出所有可能与当前拼音相符的汉字s,利用动态规划算法从前往后,推出每个拼音汉字状态的概率viterbi[i+1][s]。

    viterbi[i + 1][s] = max([(viterbi[i][pre][0] + self.emiss_prob.get(s, {}).get(seq[n][i + 1], self.min_f) + self.trans_prob.get(s, {}).get(pre, self.min_f), pre) for pre in self.pinyin_states.get(seq[n][i])])
    
  3. 最后取概率最大的串(可从大到小取多个串),即概率最大的viterbi[n][s](s为末尾的汉字),然后对串进行回溯即可得对应拼音的汉字。

5.4、GUI界面编写

采用PyQt5进行编写3,可通过记录击键次数来判断模型的好坏。

参考


  1. iseesaw/Pinyin2ChineseChars: 实现基于Bigram+HMM的拼音汉字转换系统(输入法Demo) (github.com) ↩︎

  2. 再谈Python之拼音拆分 - 简书 (jianshu.com) ↩︎

  3. Lancer-He/pinyin_IME_HMM: 基于pyqt和hmm的拼音输入法 (github.com) ↩︎

基于HMM的拼音输入法相关推荐

  1. 基于马尔可夫HMM的拼音输入法自然语言处理实现

    两个py文件,一个是利用文本训练并保存,另外一个是拼音转汉字,基于隐马尔可夫模型HMM,拼音输入法可以按注音符号与汉语拼音两种汉字拼音方案分成两大类.汉语拼音输入法的编码是依据汉语拼音方案(汉字的读音 ...

  2. 基于HMM的拼音转汉字程序

    本文将讲述怎样利用HMM进行拼音转汉字. 准备阶段 python 2.7: 安装 python 工具包 ChineseTone,直接使用 pip install 安装: 运行程序的过程中,可能还会用到 ...

  3. 基于文本服务框架的拼音输入法研究与实现

    摘要:目前的输入法大多采用输入法管理器-输入法编辑器(IMM-IME)进行开发,对于微软发布的新型输入法技术―文本服务框架(TSF)的研究一直比较滞后,该文论述了 TSF 的基本构成.主要接口.输入法 ...

  4. emWin 2天速成实例教程012_基于STM32单片机的全键盘中文汉字拼音输入法

    备注:(1)打开工程目录下的"Exe\GUISimulationDebug.exe"即可看到效果.(2)看完教程000-005就基本会用emWin做项目,其他章节可以需要时再参考. ...

  5. 数学之美:拼音输入法的数学原理

    现代输入法大多首选拼音输入法,其原因便是拼音符合人的认知规律,尽管需要多输入几个字母,但速度并不慢.它不需要专门去学习,输入时不用中断思维去考虑,而且对于前后鼻音具有很好的冗余性. 而拼音转汉字的算法 ...

  6. Tails 3.13 发布,更新 Intel 微码,改进拼音输入法支持

    百度智能云 云生态狂欢季 热门云产品1折起>>>   Tails 3.13 发布了,Tails 是基于 Debian 的 Linux 发行版,可以帮助用户匿名使用互联网,几乎可以在任 ...

  7. 拼音输入法的数学原理

    拼音输入法的数学原理 声明:引用请注明出处http://blog.csdn.net/lg1259156776/ 引言 过去的25年里,中文输入法经历从自然音节编码输入,到偏旁笔画拆字输入,再回归自然音 ...

  8. python假如输入错误重新输入_用Python写一个拼音输入法

    拼音输入法的简单实现可以归结为使用维特比算法求解汉字隐马尔可夫模型的问题:将用户输入的拼音转换为字层,使用维特比算法求解得到概率最大的一个"字链",这个"字链" ...

  9. 输入法半角和全角的快捷转换_华宇拼音输入法 一款完全免费的国产输入法_第1页...

      华宇拼音输入法历史追溯   "华宇拼音输入法"是华宇自主研发的一款文字输入软件,其历史可以追溯到二十多年前.   1999年 华宇拼音输入法初入江湖,前身是李国华博士编写的&q ...

  10. Ubuntu 12.04中设置安装Google拼音输入法

    写在最前 好久没写文章了,随着近期时间的充裕,肯定会加快会博文更新的速度.言归正传,在安装英文Linux系统后(作为开发来说,本人更倾向于安装英文语言环境 的Linux系统,这样各种提示,尤其是错误提 ...

最新文章

  1. 表达式类型的实现数据结构_Redis系列(九)底层数据结构之五种基础数据类型的实现...
  2. AI领域人才,做什么工作可以年薪百万?
  3. 珠海 第十届亚洲机器人锦标赛_2016年第十届亚洲机器人锦标赛在广东珠海成功举办...
  4. java 创建动态int数组_在Scala中创建动态增长数组的最佳方法是什么?
  5. python查天气预报_一个用Python编写抓取天气预报的代码示例
  6. ❤️缓存集合(一级缓存、二级缓存、缓存原理以及自定义缓存—源码+图文分析,建议收藏) ❤️
  7. 随手记_ubuntu下配置vscode+cmake c++开发环境
  8. linux电子教室软件,在Deepin Linux系统下安装和设置Veyon多媒体电子教室的方法
  9. Java 习题 (12)
  10. 上海市青少年算法2021年9月月赛(丙组)
  11. layui.css地址,layui+高德获取经纬度(可点击更换位置)
  12. 教师节我用Python做了个学生点名系统送给老师当礼物,这回毕业稳了
  13. Baumer工业相机堡盟工业相机如何通过BGAPISDK显示彩色相机和黑白相机的图像(C#)
  14. 群晖218J安装mysql_群晖218j和218+选择哪个?
  15. 谷歌EfficientNet高效卷积网络的学习和使用
  16. Docker学习资源汇总
  17. 12-1 蓝色天空 : 创建一个背景为蓝色的Pygame窗口 12-2 游戏角色 : 找一幅你喜欢的游戏角色位图图像或将一幅图像转换为位图。 创建一个类, 将该角色绘制到屏幕中央, 并将该图像的背景色
  18. Android StatusBar 黑底白字
  19. Django学习之模型层---多表操作之查(一)
  20. CentOS 7+环境下PHP使用Imagick+ghostscript将PDF文件转为图片

热门文章

  1. python 按比例缩小图片
  2. 2021-10-10
  3. 唯物论、辩证法和认识论
  4. 英特尔核显自定义分辨率_英特尔核芯显卡设置如何操作【图文】
  5. deepin显卡驱动管理器在哪_deepin显卡设置
  6. mysql临时表更新_MySql 临时表
  7. C++中toupper、tolower 、isalpha、isalnum、isdigit、islower、isupper、isblank、isspace的用法
  8. 基于web的实验室设备管理系统
  9. android dumpsys 分析,Android中dumpsys命令用法简单介绍
  10. java获取本机外网ip