Word2Vecを使って夢野久作 世界を覗き見る

# FastTextというかWord2Vecすごい

あまり誰も共感してくれなかったのでQiitaに書きますが、近年見てきたいろいろな要素技術の中で FastText(Word2Vec) にはかなり驚かされた部類になります。

良く例題に出される「**東京 － 日本 ＋ フランス ＝ パリ**」などですが、これすごくないですか？？
※ 東京は日本の首都、そこから日本という国の情報を削除して、フランスという国の情報を足すと、フランスの首都のパリになる。

詳しい説明は、他にたくさんありますので譲りますが、単に、純粋に、こんな計算ができることがすごいですよね。
これをやるために別に「都市Classを作って、属性に国情報を持っている」みたいなことをしてないのです。 この計算をやるためだけのクラスなりなんなりを予定調和的に作って計算させるのであれば、そんなのは簡単ですが、これはそんなことはしておらず、例えばWikipedia等の大量の文章から学習して、文章の前後のコンテキストから類推して成り立っているのです。 首都だの国だの気にすることなくこの結果を出しているのです。 これ、すごくないですか？？？ と何度も言いたくなります。

「Aさんは、BさんとCさんを足して2で割ったような人」みたいな計算ができるってことですよ。

※ ここでは FastText と Word2Vec は特に区別せず説明しています

そのすごさをわかってほしいのでさっそく遊んでみましょう。

# 環境セットアップ

いろいろと遊ぶ環境を考えたのですが、``Jupyter notebook``が良いです。

今回は Windows で試してみたいので、Anacondaを使ってインストールします。
https://www.anaconda.com/download/
Python3.x versionの64bitを入れます。

インストールがおわったら、``スタートメニュー`` → ``Jupyter Notebook`` を起動します。 無事にたちがったでしょうか？？
※ 本筋から離れてしまうので Jupter の使い方は他にゆずります。

後で困らないように、最初に使うライブラリもインストールしてしまいます。
Anacondaをインストールしていたら、スタートメニューに ``Anaconda Prompt`` というのが出来ているかと思いますので、それを起動。 コマンドプロンプトが立ち上がるのでそこで下記コマンドを実行します。

```bash
> pip install gensim
  :

> pip install janome
  :
Successfully built janome
Installing collected packages: janome
Successfully installed janome-0.3.6
```

これでインストールが完了します。


# 元ネタの作成

よくWikipediaとかを使ったモデルが使われていますが、せっかくなので好きなデータでやりましょう。 ということで、長い文章が手軽に手に入るといえば「**青空文庫**」です。

ではさっそく青空文庫の**夢野久作**のリストから適当に好みの小説を開いて、XHTMLで今すぐ読む、などを選択して、ブラウザ上から全文をコピー（Ctrl+A → Ctrl+C）して、適当にメモ帳にペタペタ張り付けて、１つのテキストファイルにしておきます。 UTF-8 で改行コードは CRLF で保存しておくと、後で幸せになれます。
https://www.aozora.gr.jp/index_pages/person96.html

※ まったくもって夢野久作である必要はありません。 単に、独特な世界観を持ってた方がおもしろいかな、くらいのことと、私の趣味です。

# 試してみる

## 全体の流れ

ざっくり言うと下記の流れになります。

1. 元ネタの文書を わかち書き する
2. FastTextで学習する
3. いろいろ試してみる

## わかち書き

まずさっそく ``わかち書き`` というものに変換します。

日本語は英語とかの言語と違って文章内に単語単語の区切りがありません。 それだと言語解析がしづらいので区切りをつけます。

```
「私がウスウスと眼を覚ました時、こうした蜜蜂の唸るような音は、
まだ、その弾力の深い余韻を、私の耳の穴の中にハッキリと引き残していた。」
```

という元の文章があったとき、

```
私 が ウスウス と 眼 を 覚まし た 時 、 こうした 蜜蜂 の 唸る よう な 音 は 、
まだ 、 その 弾力 の 深い 余韻 を 、 私 の 耳 の 穴 の 中 に ハッキリ と 引き 残し て い た 。
```

のように一般的には半角スペースで単語の区切りを分けます。
すべて手でやってもいいのですが、ものすごい大変なので、ライブラリを使います。
有名なところでは MeCab や Chasen というのがありますが、今回は、Pythonから簡単に扱うことが出来る Janome というライブラリを使います。

では、さっそく Jupyter の方に移って、新たなCellに下記のコードを入力して実行(Ctrl+Enterなど)してください。

```python
import codecs
from janome.tokenizer import Tokenizer

tokenizer = Tokenizer()

text_file = codecs.open('original.txt','r','utf-8')
bindata = text_file.read()

write_file = codecs.open('wakachi.txt','w','utf-8')

lines = bindata.split('\r\n')
for line in  lines:
    tokens = tokenizer.tokenize(line)
    for token in tokens:
         write_file.write(token.surface + ' ')

write_file.close()
text_file.close()

print('fin.')
```

最初につくった元ネタのテキストは ``original.txt`` とかの名前で Jupyter のファイルと同じ場所においておいてください。
無事に実行できると ``wakachi.txt`` が出来上がっています。
エディタなどで見てみると綺麗に分かち書きされているのがわかると思います。
## FastText による学習

FastText をWindowsで遊ぶ時の一番のネックがC++のビルドです。
が、ここ、ずるをします。
[windowsでpythonでfastText](https://qiita.com/yakipudding/items/e798614ca833d264abf3)
http://tadaoyamaoka.hatenablog.com/entry/2017/04/19/000832
のような良記事があるのでご利用ください。
どうしても、こんな環境整えるのがめんどくさい＞＜という方がいましたら、**自己責任の下で**だれかがビルドしたバイナリファイル（``fasttext.exe``）をゲットしてください。意外と探すとでてきます。※あくまで自己責任で※

ということで無事に実行ファイルを入手できたとして。

Jupyter で、新たなCellにでも下記コードをいれて実行してください。

```python
import subprocess

cmd = 'fasttext.exe skipgram -input wakachi.txt -output model -dim 300'
r = subprocess.call(cmd)
print('fin.')
```

``fasttext.exe`` を実行するだけなので、別にコマンドプロンプトでやってもいいのですが、1つにまとまってた方が楽なので全部Jupyterでやっちゃいましょう。
MacやLinuxだと ``%%bash`` でいいのですが、Windowsだと上記のような感じで``subprocess``を使って実行します。
``fasttext.exe`` はパスの通ったところに置くか、なんなら Jupyter のファイルが置いてあるところに置いてしまっても良いです。

少しだけ時間がかかってコード実行が完了すると、``model.bin`` と ``model.vec`` というのが出来たと思います。 これが学習済みモデルになります。


## ついにいろいろと遊んでみる

さて、ここまで来たらもう準備万端ですいろいろ遊んでみます。

さっそく下記のコードをJupyterに入力して実行してみてください。

```python
import gensim

model = gensim.models.KeyedVectors.load_word2vec_format('model.vec', binary=False)

out = model.most_similar(
    positive=[u'良心'],
    negative=[]
)

for x in out:
    print (x[0], x[1])
```

さて何が出てきたでしょうか。ここは元ネタの学習データによるので何とも言えないのですが、わたしはこんなのが出てきました。
これは学習データ内で ``良心`` と意味が類似した(正確には、使われ方が類似した、なのかな？)用語の一覧が出てきたものです。 良心は○○と類似、している、というわけです。

```
価値 0.9578275680541992
発達 0.9571603536605835
常識 0.9543217420578003
個人 0.95307457447052
奇抜 0.9515473246574402
空想 0.9484235048294067
地帯 0.9474833011627197
極端 0.9462447762489319
大家 0.9441084265708923
学問 0.9440963268280029
```

> 学習データ次第ではありますが、ここでエラーになる場合があります。
> 学習データに出てこない単語を指定した場合にはエラーがでますので、別の単語を使ってください。
> model.vec をエディタでみると学習したデータがわかるのでそこをもとにやるのもありです。

では、ちょっと遊んでみます。

コード中の下記の個所をいじることでいろいろできます。
``positive`` のところに書いたものが足し算、 ``negative`` に書いたものが引き算的に扱われます。

```python
out = model.most_similar(
    positive=[u'少女',u'美'],
    negative=[]
)
```

上の例では ``少女`` と `美` を配列で ``Positive`` に入れることで足し算をしています。
結果は、、、

```
美少女 0.9637368321418762
試みる 0.92450350522995
仮 0.9190890789031982
美少年 0.9120854735374451
 :
```

うん... あってるようでなんというか...
（気を取り直して）引き算もやってみます。


```python
out = model.most_similar(
    positive=[u'美少女'],
    negative=[u'少女']
)
```

``negative`` にいれることで、引き算として扱います。この例では、美少女 から 少女 を引いてます。
つまり、本質的な"美"についての探索です。
さて、結果は...

```
変態 0.657293438911438
存在 0.6549825072288513
代々 0.6538251042366028
性慾 0.6440349817276001
芸術 0.6367372274398804
 :
```

これが 美 の本質的なものなのでしょうかね... むつかしい...
3つでも4つでも計算はできるのでいろいろ試してみてください。

上の例題は、これでもまだ見れる例を出しましたが。 いろいろ試すとなかなか素敵な結果を覗き見ることができましたｗ

# まとめ

どうでしたか？すごいですよね？？
ちょっと前と違って、今は、TwitterやFacebookなどに自分の書いた文章がたくさん残っていると思いますので、そういうのをネタにこれを使うと、自分では思ってもいなかった自分に気づけると思います。