<commit_before>---
layout: post
comments: true
title: 搜喵输入法：用seq2seq训练自己的拼音输入法
published: true
---

### 代码在 [https://github.com/crownpku/Somiao-Pinyin](https://github.com/crownpku/Somiao-Pinyin)


对于中文文本数据，我们每天都在为腾讯贡献着聊天记录，或者为百度贡献着搜索记录。而中文文本数据最大的入口，我觉得其实是搜狗输入法。这些年看上去，搜狗已经基本统治了中文输入法。PC，Mac，iPhone，安卓都装上了搜狗输入法，甚至码农朋友们装上ubuntu后的第一反应也是去装个linux版本的Sougou Input（虽然很多bug）。搜狗输入法真的好用，而且越用越顺手，背后一定是数据驱动的算法。

然而我对于把我的所有输入数据，包括各种密码，都不知不觉贡献给了搜狗还是觉得不舒服。搜狗完全有能力在我输入我复杂的前半部分密码的时候自动跳出和推荐我的后半部分密码。貌似现在只有iPhone的iOS系统能在识别出用户输入密码的时候自动跳转成为苹果自己的（我们窃以为安全的）输入法键盘。

Github上有一个韩国大神的project [Kyubyong/neural_chinese_transliterator](https://github.com/Kyubyong/neural_chinese_transliterator)，用seq2seq训练自己的中文拼音输入法。这是这篇博客的主题。我们的代码完全基于这个开源项目，加入了交互式的拼音测试环境 。感谢韩国大神！而未来是希望能将这个偏research的项目做成一个真正可用的产品，一个**模型和数据本地化**（别把数据上传云端）、**根据输入内容手动甚至自动选择预训练模型**（想象正在B站发弹幕或者在写毕业论文的不同场景）、**根据用户输入数据不断更新越来越好用**的拼音输入法。我把它暂且叫做“搜喵输入法”。


## seq2seq模型

拼音输入的本质上就是一个序列到序列的模型：输入拼音序列，输出汉字序列。所以天然适合用在诸如机器翻译的seq2seq模型上。

模型初始输入是一个随机采样的拼音字母的character embedding，经过一个CBHG的模型，输出是五千个汉字对应的label。

这里使用的CBHG模块是state-of-art的seq2seq模型，用在Google的机器翻译和语音合成中，结构如下：

图片来自 [Tacotron: Towards End-to-End Speech Synthesis](https://arxiv.org/abs/1703.10135)

![](/images/201709/1.jpg)


值得注意的几点：

1.模型先使用一系列的一维卷积网络，有一系列的filter，filter_size从1到K，形成一个Conv1D Bank。这样的作用相当于使用了一系列的unigrams, bigrams直到K-grams，尽可能多的拿到输入序列从local到context的完整信息。其实这样的模型，与[之前我们提到过](http://www.crownpku.com//2017/08/26/%E7%94%A8IDCNN%E5%92%8CCRF%E5%81%9A%E7%AB%AF%E5%88%B0%E7%AB%AF%E7%9A%84%E4%B8%AD%E6%96%87%E5%AE%9E%E4%BD%93%E8%AF%86%E5%88%AB.html)的IDCNN(Iterated Dilated Convolutionary Nerual Network)有异曲同工之妙。而IDCNN相比较起来有更少的参数，不知道如果把CBHG的Conv1D Bank换成IDCNN是怎样的效果。

2.模型在最终的BiGRU之前加入了多层的Highway Layers，用来提取更高层次的特征。Highway Layers可以理解为加入了本来不相邻层之间的“高速公路”，可以让梯度更好地向前流动；同时又加入一个类似LSTM中门的机制，自动学习这些高速公路的开关和流量。Highway Networks和Residual Networks、Dense Networks都是想拉近深度网络中本来相隔很远的层与层之间的距离，使很深的网络也可以比较容易地学习。

3.模型中还使用了Batch Normalization（继ReLU之后大家公认的DL训练技巧），Residual Connection（减少梯度的传播距离），Stride=1的Max-pooling（保证Conv的局部不变性和时间维度的粒度）以及一个时髦的BiGRU。[Tacotron: Towards End-to-End Speech Synthesis](https://arxiv.org/abs/1703.10135)这篇文章发表在2017年4月，最潮的DL技术用到了很多。


## 语料获取

理论上所有的中文文本语料，我们都可以通过xpinyin这样的工具转化为拼音数据。这相当于只要有中文文本，我们就有了带标注的训练数据。

[Kyubyong/neural_chinese_transliterator](https://github.com/Kyubyong/neural_chinese_transliterator)中使用了德国一个研究所的2007-2009的中文新闻语料。我们也可以使用中文维基百科dump和百度百科这样的语料。

再开脑洞，我们可以用所有中文的研究论文作为语料，训练学术版搜喵输入法；

用所有文言古文作为语料，训练文言文版搜喵输入法；

用所有Bilibili弹幕作为语料，训练网络鬼畜版搜喵输入法；

用所有自己的聊天记录作为语料，训练准确吻合自己语气的搜喵输入法；

等等等等。



## 模型训练

Clone搜喵输入法项目: https://github.com/crownpku/Somiao-Pinyin

### 系统要求

* Python (>=3.5)

* TensorFlow (>=r1.2)

* xpinyin (中文拼音标注)

* distance (在测试集上测试时计算预测结果和真实结果的差别)

* tqdm


### 训练：

1.下载 [Leipzig Chinese Corpus](http://wortschatz.uni-leipzig.de/en/download/)。 解压缩，把zho_news_2007-2009_1M-sentences.txt 拷贝到 data/ 文件夹。当然你也可以用你自己的中文语料，改成一样的格式，或者修改一下build_corpus.py里面的解析部分.

2.建立拼音-汉字的平行语料。

```
#python3 build_corpus.py
```

3.建立词典和生成训练数据。

```
#python3 prepro.py
```

4.训练模型，模型存在 log/ 文件夹中。所有的参数都在 `hyperparams.py`中。如果有好的GPU，可以把Batch size调大一些。现有的默认参数，一个Epoch要跑一个多小时。

```
#python3 train.py
```


### 命令行输入拼音测试：

运行下面的命令，进行命令行输入拼音的测试

```
python3 eval.py
```


### 如果你不想自己训练数据...

预训练好的模型，稍后放在百度网盘。

将解压缩生成的 /log 和 /data 放在工程的根目录下。记住要覆盖掉 /data 下面的pickle文件，因为pickle dump有随机性，自己运行 `prepro.py` 生成的pickle文件会和预训练模型起冲突。

然后运行下面的命令，进行命令行输入拼音的测试

```
python3 eval.py
```<commit_msg>update blog<commit_after>---
layout: post
comments: true
title: 搜喵输入法：用seq2seq训练自己的拼音输入法
published: true
---

### 代码在 [https://github.com/crownpku/Somiao-Pinyin](https://github.com/crownpku/Somiao-Pinyin)


对于中文文本数据，我们每天都在为腾讯贡献着聊天记录，或者为百度贡献着搜索记录。而中文文本数据最大的入口，我觉得其实是搜狗输入法。这些年看上去，搜狗已经基本统治了中文输入法。PC，Mac，iPhone，安卓都装上了搜狗输入法，甚至码农朋友们装上ubuntu后的第一反应也是去装个linux版本的Sougou Input（虽然很多bug）。搜狗输入法真的好用，而且越用越顺手，背后一定是数据驱动的算法。

然而我对于把我的所有输入数据，包括各种密码，都不知不觉贡献给了搜狗还是觉得不舒服。搜狗完全有能力在我输入我复杂的前半部分密码的时候自动跳出和推荐我的后半部分密码。貌似现在只有iPhone的iOS系统能在识别出用户输入密码的时候自动跳转成为苹果自己的（我们窃以为安全的）输入法键盘。

Github上有一个韩国大神的project [Kyubyong/neural_chinese_transliterator](https://github.com/Kyubyong/neural_chinese_transliterator)，用seq2seq训练自己的中文拼音输入法。这是这篇博客的主题。我们的代码完全基于这个开源项目，加入了交互式的拼音测试环境 。感谢韩国大神！而未来是希望能将这个偏research的项目做成一个真正可用的产品，一个**模型和数据本地化**（别把数据上传云端）、**根据输入内容手动甚至自动选择预训练模型**（想象正在B站发弹幕或者在写毕业论文的不同场景）、**根据用户输入数据不断更新越来越好用**的拼音输入法。我把它暂且叫做“搜喵输入法”。


## seq2seq模型

拼音输入的本质上就是一个序列到序列的模型：输入拼音序列，输出汉字序列。所以天然适合用在诸如机器翻译的seq2seq模型上。

模型初始输入是一个随机采样的拼音字母的character embedding，经过一个CBHG的模型，输出是五千个汉字对应的label。

这里使用的CBHG模块是state-of-art的seq2seq模型，用在Google的机器翻译和语音合成中，结构如下：

图片来自 [Tacotron: Towards End-to-End Speech Synthesis](https://arxiv.org/abs/1703.10135)

![](/images/201709/1.jpg)


值得注意的几点：

1.模型先使用一系列的一维卷积网络，有一系列的filter，filter_size从1到K，形成一个Conv1D Bank。这样的作用相当于使用了一系列的unigrams, bigrams直到K-grams，尽可能多的拿到输入序列从local到context的完整信息。其实这样的模型，与[之前我们提到过](http://www.crownpku.com//2017/08/26/%E7%94%A8IDCNN%E5%92%8CCRF%E5%81%9A%E7%AB%AF%E5%88%B0%E7%AB%AF%E7%9A%84%E4%B8%AD%E6%96%87%E5%AE%9E%E4%BD%93%E8%AF%86%E5%88%AB.html)的IDCNN(Iterated Dilated Convolutionary Nerual Network)有异曲同工之妙。而IDCNN相比较起来有更少的参数，不知道如果把CBHG的Conv1D Bank换成IDCNN是怎样的效果。

2.模型在最终的BiGRU之前加入了多层的Highway Layers，用来提取更高层次的特征。Highway Layers可以理解为加入了本来不相邻层之间的“高速公路”，可以让梯度更好地向前流动；同时又加入一个类似LSTM中门的机制，自动学习这些高速公路的开关和流量。Highway Networks和Residual Networks、Dense Networks都是想拉近深度网络中本来相隔很远的层与层之间的距离，使很深的网络也可以比较容易地学习。

3.模型中还使用了Batch Normalization（继ReLU之后大家公认的DL训练技巧），Residual Connection（减少梯度的传播距离），Stride=1的Max-pooling（保证Conv的局部不变性和时间维度的粒度）以及一个时髦的BiGRU。[Tacotron: Towards End-to-End Speech Synthesis](https://arxiv.org/abs/1703.10135)这篇文章发表在2017年4月，最潮的DL技术用到了很多。


## 语料获取

理论上所有的中文文本语料，我们都可以通过xpinyin这样的工具转化为拼音数据。这相当于只要有中文文本，我们就有了带标注的训练数据。

[Kyubyong/neural_chinese_transliterator](https://github.com/Kyubyong/neural_chinese_transliterator)中使用了德国一个研究所的2007-2009的中文新闻语料。我们也可以使用中文维基百科dump和百度百科这样的语料。

再开脑洞，我们可以用所有中文的研究论文作为语料，训练学术版搜喵输入法；

用所有文言古文作为语料，训练文言文版搜喵输入法；

用所有Bilibili弹幕作为语料，训练网络鬼畜版搜喵输入法；

用所有自己的聊天记录作为语料，训练准确吻合自己语气的搜喵输入法；

等等等等。



## 模型训练

Clone搜喵输入法项目: https://github.com/crownpku/Somiao-Pinyin

### 系统要求

* Python (>=3.5)

* TensorFlow (>=r1.2)

* xpinyin (中文拼音标注)

* distance (在测试集上测试时计算预测结果和真实结果的差别)

* tqdm


### 训练：

1.下载 [Leipzig Chinese Corpus](http://wortschatz.uni-leipzig.de/en/download/)。 解压缩，把zho_news_2007-2009_1M-sentences.txt 拷贝到 data/ 文件夹。当然你也可以用你自己的中文语料，改成一样的格式，或者修改一下build_corpus.py里面的解析部分.

2.建立拼音-汉字的平行语料。

```
#python3 build_corpus.py
```

3.建立词典和生成训练数据。

```
#python3 prepro.py
```

4.训练模型，模型存在 log/ 文件夹中。所有的参数都在 `hyperparams.py`中。如果有好的GPU，可以把Batch size调大一些。现有的默认参数，一个Epoch要跑一个多小时。

```
#python3 train.py
```


### 命令行输入拼音测试：

运行下面的命令，进行命令行输入拼音的测试

```
#python3 eval.py
```


### 如果你不想自己训练数据...

预训练好的模型，稍后放在百度网盘。

将解压缩生成的 /log 和 /data 放在工程的根目录下。记住要覆盖掉 /data 下面的pickle文件，因为pickle dump有随机性，自己运行 `prepro.py` 生成的pickle文件会和预训练模型起冲突。

然后运行下面的命令，进行命令行输入拼音的测试

```
#python3 eval.py
```
{'fraction_non_alphanumeric': 0.1230451460607849, 'fraction_numerical': 0.03540867512540572, 'mean_word_length': 10.26079734219269, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 14, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2772770', 'n_tokens_mistral': 5010, 'n_tokens_neox': 5591, 'n_words': 227}