一个好的拼音输入法能极大提升中文使用者的生活效率。从最早智能机上的 Bag of Words 词袋模型(按照语料库中汉字出现频率排序)到使用 n-gram 语言模型的 viterbi 方法,再到从 RNN 派生出的 seq2seq 深度学习方法、云输入工具等,都是利用可用算力对拼音转文字的尝试。

这次实验中以 n-gram 为主,使用三元模型构建拼音输入法,并与 seq2seq 进行对比,实现拼音到汉字序列的转换,在测试集上的准确度达到 0.79(句)与 0.95(字)。

整体内容分为四大块:语料库选择与清洗、模型的具体实现、参数选择与优化、结果分析。

语料库

语料库是拼音输入法的灵魂。从实际实验情况来看,从 n-gram 升级到 NNLM 以致 seq2seq 模型,投入大量 GPU 时间训练所带来的提升可能还没有清洗语料库带来的提升大。对语料库的处理主要分为两个部分,即爬取与清洗:

爬取

给出的语料库为 2016 全年的新浪新闻。

四年来中文语言的变化非常之大,新的句式频繁出现,专有名词也在不断产生,因此爬取了近三个月的新浪新闻数据与清华新闻网上的全部数据;新闻的句式较为正式,在日常拼音输入法的使用中还会涉及很多口语化表达,因此引入微博、贴吧、豆瓣与知乎的数据集。

清洗

拼音输入法只能输入汉字,因此针对标点切分

delims = r',|,|\.|。|、|\(|(|\)|)|<|《|>|》|\'|\"|“|”|【|】|:|…|·|\t|\#|\-'

仅提取纯汉字内容

def is_type(word):
    if len(word) > 1:
        return min('\u4e00' <= char <= '\u9fff' for char in word]
    else:
        return False

序列化

语料库大小为

语料库 大小 /MB 清洗后 /MB 条数 /w
新浪新闻 /旧 948 611 1761
新浪新闻 /新 559 179 500
清华新闻 464 51 138
微博数据 536 110 428
贴吧数据 425 144 498
豆瓣数据 643 186 294
知乎问答 3712 1034 3203

使用 pypinyin 对汉字序列标音,并建立字典将拼音与汉字分别映射到连续整数序列

利用 pickle 将序列化后语料库存储到二进制文件,共四种组合如下

语料库 大小 /MB 拼音数量 汉字数量
给定新闻 786 410 9006
全部新闻
社交平台
2252 416 11080
知乎问答 1935 414 12494
全部语料库 4198 416 13623

模型

n-gram

对于拼音序列 #$w_i^n#$,最有可能与之对应的汉字序列 #$t_i^n#$ 为

$$\hat{t}_i^n=\mathop{\arg\max}_{t_i^n}P(t_i^n|w_i^n)$$

使用贝叶斯公式

$$\hat{t}_i^n=\mathop{\arg\max}_{t_i^n}\frac{P(w_i^n|t_i^n)\cdot P(t_1^n)}{P(w_1^n)}$$

也即

$$\hat{t}_i^n=\mathop{\arg\max}_{t_i^n}P(w_i^n|t_i^n)\cdot P(t_1^n)$$

使用马尔可夫假设

$$P(w_1^n|t_1^n)\approx\prod_{i=1}^nP(w_i|t_i)$$

二元模型

$$P(t_1^n)\approx\prod_{i=1}^nP(t_i|t_{i-1})$$

三元模型

$$P(t_1^n)\approx\prod_{i=1}^nP(t_i|t_{i-2}t_{i-1})$$

其中二元模型有一个开始符 ^ 与一个终结符 $,三元模型分别有两个

进行平滑化处理

$$P(t_i|t_{i-1})=\lambda\cdot P(t_i|t_{i-1})+(1-\lambda)\cdot P(t_i)$$

并定义字与拼音的权重比为 #$k#$,即

$$P(t_i|t_{i-2}t_{i-1})\cdot P(t_i|w_i)^k$$

然后使用 viterbi 算法求出最大可能性选择

seq2seq

先构建 conv1d bank 提取尽可能多的输入序列特征,加入 highway 促进梯度流动

seq2seq

参考自 Kyubyong 并针对拼音序列进行改进,加入新的语料库训练模型

参数选择与结果分析

n-gram

分析

预设 #$\lambda=0.95#$, #$k=0.1#$ 训练的结果为(给定的测试集)

语料库 数据大小 /MB 句准确率 字准确率
给定新闻 430 0.641 0.907
全部新闻
社交平台
611 0.728 0.939
知乎问答 563 0.732 0.937
全部语料库 895 0.784 0.951

使用给定新闻语料库对 #$\lambda#$ 进行分析,固定 #$k=0#$

lambda

可以看到 #$\lambda#$ 在 #$[0.90, 1)#$ 上差异不大

使用全部语料库对 #$k#$ 进行分析,固定 #$\lambda=0.95#$

lambda

可以看到在 #$k=0.3#$ 时正确率达到峰值

结论

对于一般模型,普遍认为正确率在 #$\lambda#$ 取 #$[0.9, 1.0)#$,#$k#$ 取 #$[0.1, 0.3]#$ 时达到峰值

采用全部语料库作为训练集,在 #$\lambda=0.95#$, #$k=0.3#$ 时准确度达到峰值 0.790 与 0.952

实验得出最优参数为

语料库 #$\lambda#$ #$k#$ 句准确率 字准确率
给定新闻 0.95 0.1 0.641 0.907
全部新闻
社交平台
0.95 0.3 0.739 0.941
知乎问答 0.95 0.1 0.732 0.937
全部语料库 0.95 0.3 0.790 0.952

一些典型错句

  • )是我的母亲 - 因果关系
  • 给阿姨()一杯卡布奇诺 - 倒序相关
  • 我去给你买一个(子) - 受限于 tri-gram 长度
  • 你吼(辣么)大声干什么嘛 - 网络流行语
  • 寻寻觅觅冷冷清清凄凄惨惨戚戚) - 古文
  • 碳碳键键能能否否定定律) - 人类也无法轻易识别的句子

一些典型正确句子

  • 正确率接近 80%,展示具体样例没有什么意义

seq2seq

使用了给定数据的 10% 规模进行训练,在给定的数据集上句、字准确度分别为 0.33 和 0.80

字准确度与 n-gram 相近,如果改为 top-k 匹配测试句准确度可能会大幅上升

使用 20% 规模进行训练,在给定数据集上句、字准确度分别为 0.41 和 0.87

使用更大规模语料库进行训练并等待完全收敛,会有可以预见的高准确率

To-dos

  1. 收集诗词与古文语料库,增加对古诗文的定向匹配
  2. 进行 viterbi 算法时仅选取 top-k 概率以加快速度
  3. 优化序列化语料库构建方式,使之支持增量添加
  4. 继续进行 seq2seq 实验