markovifyで日本語の文章を学習して、マルコフ連鎖により文章生成を行う

# TL; DR
mecab 等を利用してサンプルとなる文章を分かち書きしたのちに、区切りを改行にして `markovify.NewlineText` に渡すことで日本語の文章からモデルが作成できる。英語の場合と同様、 `make_sentence` を利用してマルコフ連鎖の結果を得られる。本稿では日本語文章を学習させて文章生成をした。

# 目的
現在、趣味で[しゅうまい君](https://twitter.com/shuumai)、[からしちゃん](https://twitter.com/karashichan)のようにマルコフ連鎖を利用して文章生成をするbotを作成している。

マルコフ連鎖の辞書作成と連鎖を行うコードを書いた経験はあるが、今回は既存のライブラリを利用してみようと考えた。そこで、GitHubでスター数が多い（2018/03/14時点で:star:1352） `jsvine/markovify` を利用し、日本語文章をサンプルとしてモデルを作成しつつ文章生成を行うことを目標とした。
[jsvine/markovify: A simple, extensible Markov chain generator.](https://github.com/jsvine/markovify)

# markovify
markovifyとは、マルコフ連鎖のモデル生成と文章生成のためのライブラリである。

[マルコフ連鎖 - Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%9E%E3%83%AB%E3%82%B3%E3%83%95%E9%80%A3%E9%8E%96)

シンプルでありながら高機能なライブラリだが、Qiitaにはこれを取り扱う記事が存在しなかったため、この記事を書いて投稿した。

## 動作例
様子見として、[PEP 20 -- The Zen of Python](https://www.python.org/dev/peps/pep-0020/)を学習させて、文章を生成させた。

ソースコード： https://gist.github.com/kakakaya/14d109a0398d04b654ab1d6d53580e8d

```python:zen.py
# learn model from text.
text_model = markovify.Text(zen_of_python)
# ... and generate from model.
for _ in range(5):
    print(text_model.make_sentence())
```
```output.txt
Sparse is better than implicit.
Now is better than implicit.
If the implementation is easy to explain, it's a bad idea.
Explicit is better than nested.
Explicit is better than *right* now.
```
ランダムに生成しているため当然内容は無意味だが、The Zen of Pythonは文章の形が整っているからか、文章としては自然であるという面白い結果が得られた。

# 実験：江戸川乱歩を学習して文章を生成する
## 概要
markovifyの使い方を確認したので、より本来の目的に近付けるため日本語の文章を学習、生成させる。そこで、今回は学習データとして青空文庫にて公開されている江戸川乱歩の作品を利用してみる。但し、markovifyでは英文章、あるいはそれに類するもののみの読み込みをサポートしており、日本語の文章は自分で最小単位ごとに分割する必要がある。そこで、mecabを利用して分かち書きを行う。

## 準備
全部パッケージ管理システムなり `pip` なりで簡単に入るので説明は割愛します。

- Python
    - https://www.python.org
    - 3.7.0リリース予定日まであと3ヶ月ですね。新機能も楽しみです。
- mecab
    - http://taku910.github.io/mecab/
    - 日本語文章における形態素解析ツール。今回は分かち書きのためだけに利用する。
    - 最近開発されたjuman++の方が高精度という報告（ https://qiita.com/riverwell/items/438e88427363511e9f28 ）もあるが、今回は知名度の高さからmecabを採用した。
- mecab-python3
    - https://github.com/SamuraiT/mecab-python3
    - Python3用mecabライブラリ。
    - 最終更新が4年前までだが、問題なく使えたのでこれを利用した。
- markovify
    - https://github.com/jsvine/markovify
    - 本稿の主題でもあるPython2/3用マルコフ連鎖生成ライブラリ。
- 青空文庫
    - https://github.com/aozorabunko/aozorabunko
    - 著作権が消滅した、あるいは著者が許諾した作品を公開する電子図書館。
    - GitHubで全文が公開されてるのは今回初めて知った。
    - しかし、`git clone`で無限に時間が掛かってしまったので、本家青空文庫サイトから手作業込みでクローリングする羽目になった。

## 実装
読み込んで学習させるだけ。だが、いくつかのハマりポイントがあった。
ソースコード： https://gist.github.com/kakakaya/141838e738a2fd9667b5e4fd2b79c4c7

- MeCabがエラーを出さずに死亡する
    - 改行を含む文章だとダメらしい（それはそう）
    - 記号によっても死ぬことがある？
- Python側プロセスが死んでいてもMeCabが動いていることがある
    - この場合エラーが出る？
- MeCabが分割した形態素を取り出そうとしたら `UnicodeDecodeError` が発生することがある
    - 原因不明。try-catchで対処。
- markovifyは括弧でエラーを起こす
    - https://github.com/jsvine/markovify/issues/84
    - 今回は記号を全部弾く仕様にした。

## 実験の結果
次のような文章が生成された。

- `小林君は、大きなアーム・チェアに腰をすえて、フラフラと歩いている。`
    - 歩いているのか、座っているのか。
- `」明智がびっくりして、ぴったり、かべにからだをくくりつけてあったことである。`
    - 壁に体を括り付けるのは難しそうだ。
- `黒山の見物人には、人のいないようです。`
    - どっちやねん。

100個生成した結果はこちら
https://gist.github.com/kakakaya/1daeb8e5d9a9f8f48a78af7aa5c8e568

# まとめと感想
`jsvine/markovify` というライブラリを使って日本語の文章を学習させ、文章生成を行った。シンプルながら使いやすいことが分かったため、先述したbot作成にも活用するつもりである。

ところで円城塔のエピローグという小説が文章の自動生成をテーマにしており、面白いのでおすすめです（布教）。