Stay Hungry,Stay Foolish!

HMM 拼音输入法

拼音输入法案例

https://zhuanlan.zhihu.com/p/25132270

https://github.com/LiuRoy/Pinyin_Demo

在网上看到一篇关于隐马尔科夫模型的介绍,觉得简直不能再神奇,又在网上找到大神的一篇关于如何用隐马尔可夫模型实现中文拼音输入的博客,无奈大神没给可以运行的代码,只能纯手动网上找到了结巴分词的词库,根据此训练得出隐马尔科夫模型,用维特比算法实现了一个简单的拼音输入法。githuh地址:LiuRoy/Pinyin_Demo

 

原理简介

隐马尔科夫模型

抄一段网上的定义:

隐马尔可夫模型 (Hidden Markov Model) 是一种统计模型,用来描述一个含有隐含未知参数的马尔可夫过程。其难点是从可观察的参数中确定该过程的隐含参数,然后利用这些参数来作进一步的分析。

拼音输入法中可观察的参数就是拼音,隐含的参数就是对应的汉字。

viterbi算法

参考https://zh.wikipedia.org/wiki/维特比算法,思想是动态规划,代码比较简单就不赘述。

 

优化版

https://github.com/fanqingsong/pinyin_input_method

模型训练

在 corpus 目录中, 保存了一份 dict.txt, 为删减版本的jieba词库。

此词库为默认训练词库。

另外一个为 dict.backup.txt为原来repo的词库, 太大,训练时间太长。 所以调试阶段不适用。

模型所在数据库有也对应两个 hmm.sqlite hmm.backup.sqlite

运行命令

./bin/run.sh hmm/train.py

 

viterbi实现

代码建hmm/viterbi.py文件,此处会找到最多十个局部最优解,注意是十个局部最优解而不是十个全局最优解,但是这十个解中最优的那个是全局最优解,代码如下:

def viterbi(pinyin_list):
    """
    viterbi算法实现输入法

    Args:
        pinyin_list (list): 拼音列表
    """

    # query the char-prob pair
    # char must in starting table, named as start_char
    # prob = start_char prob * emit_prob
    # emit_prob is the probability that start_char emit to the start_pinyin
    start_pinyin = pinyin_list[0]

    start_char = Emission.join_starting(start_pinyin)
    print("------ start_char -------")
    print(start_char)

    V = {char: prob for char, prob in start_char}

    print("------ V -------")
    print(V)

    print("\r\n")

    # let's count from the second pinyin to calc viterbi matrix
    for i in range(1, len(pinyin_list)):
        pinyin = pinyin_list[i]

        print("------ i -------")
        print(i)

        print("------ pinyin -------")
        print(pinyin)

        prob_map = {}

        for phrase, prob in V.iteritems():
            print("------ phrase -------")
            print(phrase)

            print("------ prob -------")
            print(prob)

            prev_char = phrase[-1]

            # only get the most possible next_char, with highest probability
            result = Transition.join_emission(pinyin, prev_char)
            print("------ result -------")
            print(result)

            if not result:
                continue

            # next_prob = transfer probability(pre_char -> next_char) * emission probability(next_char -> pinyin)
            next_char, next_prob = result
            print("-------- next_char --------")
            print(next_char)

            # make new V of new char path, ie phrase.
            prob_map[phrase + next_char] = next_prob + prob

        if prob_map:
            # update V, in order to do further research
            V = prob_map
        else:
            return V

        print("\r\n")

    return V

 

结果展示

运行文件,简单的展示一下运行结果:

./bin/run.sh hmm/viterbi.py

输出

root@xxx:~/win10/mine/pinyin_input_method# ./bin/run.sh hmm/viterbi.py
PYTHONPATH=/usr/local/spark/python:/usr/local/spark/python/lib/py4j-0.10.4-src.zip::/root/win10/mine/pinyin_input_method
input:duan yu
------ start_char -------
[(u'\u77ed', -8.848355540206123), (u'\u6bb5', -8.848355540206123)]
------ V -------
{u'\u6bb5': -8.848355540206123, u'\u77ed': -8.848355540206123}


------ i -------
1
------ pinyin -------
yu
------ phrase -------
段
------ prob -------
-8.84835554021
------ result -------
(u'\u8a89', -0.1840036429769394)
-------- next_char --------
誉
------ phrase -------
短
------ prob -------
-8.84835554021
------ result -------
(u'\u8bed', 0.0)
-------- next_char --------
语


短语 -8.84835554021
段誉 -9.03235918318
input:
bye bye

 

 

汉字转拼音(pypinyin)

https://github.com/mozillazg/python-pinyin

>>> from pypinyin import pinyin, lazy_pinyin, Style
>>> pinyin('中心')
[['zhōng'], ['xīn']]
>>> pinyin('中心', heteronym=True)  # 启用多音字模式
[['zhōng', 'zhòng'], ['xīn']]
>>> pinyin('中心', style=Style.FIRST_LETTER)  # 设置拼音风格
[['z'], ['x']]
>>> pinyin('中心', style=Style.TONE2, heteronym=True)
[['zho1ng', 'zho4ng'], ['xi1n']]
>>> pinyin('中心', style=Style.TONE3, heteronym=True)
[['zhong1', 'zhong4'], ['xin1']]
>>> pinyin('中心', style=Style.BOPOMOFO)  # 注音风格
[['ㄓㄨㄥ'], ['ㄒㄧㄣ']]
>>> lazy_pinyin('中心')  # 不考虑多音字的情况
['zhong', 'xin']
>>> lazy_pinyin('战略', v_to_u=True)  # 不使用 v 表示 ü
['zhan', 'lüe']
# 使用 5 标识轻声
>>> lazy_pinyin('衣裳', style=Style.TONE3, neutral_tone_with_five=True)
['yi1', 'shang5']

 

jieba词库

https://github.com/fxsjy/jieba/tree/master/jieba

算法

  • 基于前缀词典实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图 (DAG)
  • 采用了动态规划查找最大概率路径, 找出基于词频的最大切分组合
  • 对于未登录词,采用了基于汉字成词能力的 HMM 模型,使用了 Viterbi 算法

https://raw.githubusercontent.com/fxsjy/jieba/master/jieba/dict.txt

AT&T 3 nz
B超 3 n
c# 3 nz
C# 3 nz
c++ 3 nz
C++ 3 nz
T恤 4 n
A座 3 n
A股 3 n
A型 3 n
A轮 3 n
AA制 3 n
AB型 3 n
B座 3 n
B股 3 n
B型 3 n
B超 3 n
B轮 3 n
BB机 3 n
BP机 3 n
C盘 3 n
C座 3 n
C语言 3 n
CD盒 3 n
CD机 3 n
CALL机 3 n
D盘 3 n
D座 3 n
D版 3 n
E盘 3 n
E座 3 n
E化 3 n
E通 3 n
F盘 3 n
F座 3 n
G盘 3 n
H盘 3 n
H股 3 n
I盘 3 n
IC卡 3 n
IP卡 3 n
IP电话 3 n
IP地址 3 n
K党 3 n
K歌之王 3 n
N年 3 n
O型 3 n
PC机 3 n
PH值 3 n
SIM卡 3 n
U盘 3 n
VISA卡 3 n
Z盘 3 n
Q版 3 n
QQ号 3 n
RSS订阅 3 n
T盘 3 n

 

posted @ 2021-04-22 16:25  lightsong  阅读(574)  评论(0编辑  收藏  举报
Life Is Short, We Need Ship To Travel