【NLP入門】fasttextでWikipediaを学習して類似語を抽出してみる♪

今夜は昨夜に引き続き、題名のとおりのことを実施します。
qiitaにも記事が多くあって、やりつくされているけど、ウワン的には再整理していきたいと思います。
ほぼ参考のとおり進めますが、備忘録としてなるべく丁寧に具体的に記載しようと思います。
【参考】
・[FacebookのfastTextでFastに単語の分散表現を獲得する](https://qiita.com/icoxfog417/items/42a95b279c0b7ad26589)
###必要なツールのインストール
```
$ sudo apt install make
$ sudo apt install g++
```
###fasttextのインストール
```
$ git clone https://github.com/facebookresearch/fastText.git
$ cd fastText
$ make
c++ -pthread -std=c++11 -march=native -O3 -funroll-loops -DNDEBUG -c src/args.cc
...
c++ -pthread -std=c++11 -march=native -O3 -funroll-loops -DNDEBUG -c src/fasttext.cc
c++ -pthread -std=c++11 -march=native -O3 -funroll-loops -DNDEBUG args.o autotune.o matrix.o dictionary.o loss.o productquantizer.o densematrix.o quantmatrix.o vector.o model.o utils.o meter.o fasttext.o src/main.cc -o fasttext
```
###wikipediaのダウンロード
[attardi/wikiextractor](https://github.com/attardi/wikiextractor)
まず、Wikiextracor.pyという展開ツールを上記サイトから、以下のとおりgitします。

```
$ git clone https://github.com/attardi/wikiextractor.git
```
次に、Wikipediaの dump データは [https://dumps.wikimedia.org/jawiki/latest/](https://dumps.wikimedia.org/jawiki/latest/) にあるので，このページから以下をダウンロードします。

```
jawiki-latest-pages-articles.xml.bz2  02-Sep-2019 17:31 2930998407
```
そして、先ほどのwikiextractorでディレクトリに５００MBずつのファイルに展開します。今回は記事１１６万ページ分６個のファイルができました。

```
$ python3 wikiextractor/WikiExtractor.py -b 500M -o path/to/cprpus jawiki-latest-pages-articles.xml.bz2
...
INFO: Finished 7-process extraction of 1164622 articles in 1158.8s (1005.1 art/s)
INFO: total of page: 1679026, total of articl page: 1164622; total of used articl page: 1164622
```
これら６個のファイルを以下のように１ファイルに合体します。

```
抽出されたテキストデータを1つのテキストファイルにまとめます。
$ cd ./path/to/corpus/AA
$ cat wiki_00 wiki_01 wiki_02 wiki_03 wiki_04 wiki_05 >wiki
```
次に合体したファイルをmecabにより、分かち書きして出力します。辞書は昨夜のmecab-ipadic-neologdを使います。

```
$ mecab -O wakati -d /usr/share/mecab/dic/mecab-ipadic-neologd wiki -o wiki_wakati
input-buffer overflow. The line is split. use -b #SIZE option.
input-buffer overflow. The line is split. use -b #SIZE option.
...
```
###fasttext skipgramで学習
最初は、次元指定せずにやってみました。学習したmodelのサイズから100次元なようです。
ファイルが大きいので、300次元でやりました。学習は１時間４０分程度かかりました。

```
$ ./fasttext skipgram -input ../wiki_wakati -output model300 -dim 300
Read 567M words
Number of words:  1011294
Number of labels: 0
Progress:  58.9% words/sec/thread:   39389 lr:  0.020538 avg.loss:  0.362351 ETA:   0h41m 4s
Progress: 100.0% words/sec/thread:   39451 lr:  0.000000 avg.loss:  0.215725 ETA:   0h 0m 0s
```
###類似単語を出力する
上記参考と同じように類似単語を出力してみます。

```ex_fasttext_.py
from gensim.models import FastText
model=FastText.load_fasttext_format("model.bin")
print(model.most_similar("リンゴ"))
```
デフォルトで学習したものの結果は以下のとおりです。
ちょっと、リンゴ成分が少ない気がします。

```
$ python3 ex_fasttext_.py model.bin 1,595,207kB
[('イチゴ', 0.8332647085189819), 
('トマト', 0.8174118399620056), 
('ホロムイイチゴ', 0.8127106428146362), 
('サクランボ',　0.8120944499969482), 
('キャベツ', 0.8119571208953857), 
('キュウリ', 0.8038774728775024), 
('レタス', 0.801289439201355),
('木イチゴ', 0.7995303273200989), 
('りんご', 0.7988823056221008), 
('果物', 0.7899028062820435)]
```
３００次元で学習したものは以下のとおりです。
リンゴに近いという意味ではこちらはいい感じの並びになっています。

```
$ python3 ex_fasttext_.py model300.bin 4,738,854kB
[('リンゴ酢', 0.7259324789047241), 
('りんご', 0.7181535959243774), 
('リンゴワイン', 0.6998193264007568), 
('リンゴー', 0.6919084787368774), 
('リンゴジュース', 0.6816540956497192), 
('イチゴ', 0.6684622764587402), 
('イヌリンゴ', 0.6634740233421326), 
('リンゴ並木', 0.6623485684394836), 
('果物', 0.6577243804931641), 
('サクランボ', 0.6575568914413452)]
```
次に参考ではpython eval.py EXILEで類似度の高い単語が並んでいますが、そんなことはできないので、以下のように簡単なプログラムを書いてやってみました。
※ちなみに、現在提供されているeval.pyはファイル読み込みのアプリになっていてこういうことはできません

```word_eval.py
from gensim.models import FastText
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('model', type=str)
parser.add_argument('word', type=str)
args = parser.parse_args()

model=FastText.load_fasttext_format(args.model)
print(model.most_similar(args.word))
```
結果は以下のとおりになりました。
これは結構まともな結果になりました。

```
$ python3 word_eval.py model300.bin EXILE
[('EXILES', 0.8910697102546692), 
('EXILE魂', 0.8796107172966003), 
('GLAY×EXILE', 0.8737531900405884), 
('週刊EXILE', 0.8262338638305664), 
('月刊EXILE', 0.7968215942382812), 
('劇団EXILE', 0.7787322998046875), 
('MAKIDAI', 0.7554136514663696),
('ATSUSHI', 0.7457683086395264), 
('TRIBE', 0.7340785264968872), 
('EXI', 0.728649914264679)]
```
次の例がpython eval.py EXILE --negativeって、これもできないので、以下のコードを書きました。

```word_eval.py
from gensim.models import FastText
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('model', type=str)
parser.add_argument('--positive','-p', type=str, required=False, default='')
parser.add_argument('--negative','-n', type=str, required=False, default='')
args = parser.parse_args()

model=FastText.load_fasttext_format(args.model)
print(model.most_similar(positive=args.positive,negative=args.negative))
```
これで、一応以下のような結果が得られました。
しかし、どうも意味わかりません。

```
$ python3 word_eval.py model300.bin -n EXILE
[('寚', 0.11694395542144775), 
('時東', 0.0812937468290329), 
('永嶋', 0.07949072122573853), 
('風伯', 0.07428240031003952), 
('田牛', 0.06561749428510666), 
('褞袍', 0.05880854278802872), 
('いは', 0.05830223113298416), 
('定村', 0.05788616091012955), 
('邦助', 0.05730722099542618), 
('目丸', 0.05564088374376297)]
```

###まとめ
・WikipediaのArticlesを学習した
・学習したモデルを使って類似語を抽出してみた
・Negativeはなんとなくうまく抽出できなかったようだ

・学習精度を上げたいと思う
・やはり、この手のものは以下をやってみたいと思う
「フランス」－「パリ」＋「東京」＝「日本」
「男性－女性」を「おじ」や「王」に足すと、「おば」や「女王」になる