素人の言語処理100本ノック:90


[言語処理100本ノック 2015](http://www.cl.ecei.tohoku.ac.jp/nlp100/)の挑戦記録です。環境はUbuntu 16.04 LTS ＋ Python 3.5.2 \:\: Anaconda 4.1.1 (64-bit)です。過去のノックの一覧は[こちら](http://qiita.com/segavvy/items/fb50ba8097d59475f760)からどうぞ。

## 第10章: ベクトル空間法 (II)
>第10章では，前章に引き続き単語ベクトルの学習に取り組む．

### 90. word2vecによる学習
>81で作成したコーパスに対して[word2vec](https://code.google.com/archive/p/word2vec/)を適用し，単語ベクトルを学習せよ．さらに，学習した単語ベクトルの形式を変換し，86-89のプログラムを動かせ．

#### 出来上がったコード：

```python:main.py
# coding: utf-8
import pickle
from collections import OrderedDict
import numpy as np
from scipy import io
import word2vec

fname_input = 'corpus81.txt'
fname_word2vec_out = 'vectors.txt'
fname_dict_index_t = 'dict_index_t'
fname_matrix_x300 = 'matrix_x300'

# word2vecでベクトル化
word2vec.word2vec(train=fname_input, output=fname_word2vec_out,
	size=300, threads=4, binary=0)

# その結果を読み込んで行列と辞書作成
with open(fname_word2vec_out, 'rt') as data_file:

	# 先頭行から用語数と次元を取得
	work = data_file.readline().split(' ')
	size_dict = int(work[0])
	size_x = int(work[1])

	# 辞書と行列作成
	dict_index_t = OrderedDict()
	matrix_x = np.zeros([size_dict, size_x], dtype=np.float64)

	for i, line in enumerate(data_file):
		work = line.strip().split(' ')
		dict_index_t[work[0]] = i
		matrix_x[i] = work[1:]

# 結果の書き出し
io.savemat(fname_matrix_x300, {'matrix_x300': matrix_x})
with open(fname_dict_index_t, 'wb') as data_file:
	pickle.dump(dict_index_t, data_file)
```

#### 実行結果：

main.pyを実行すると[問題81](http://qiita.com/segavvy/items/216888c3549cea3d8e81)で作った「corpus81.txt」を読み込んでword2vecで学習し、「matrix_x300.mat」と「dict_index_t」に出力します。処理時間は手元のマシンで4分ほどでした。

作成した2つのファイルのフォーマットは[問題85](http://qiita.com/segavvy/items/f1a7f3200c3b771e8568)で作成するものと同じなので、ファイルを置き換えれば[問題86](http://qiita.com/segavvy/items/d0cfabf328fd6d67d003)〜[問題89]()のプログラムがそのまま実行できるようになっています。以下、その結果です。

##### [問題86](http://qiita.com/segavvy/items/d0cfabf328fd6d67d003)の結果
「United_States」のベクトルを表示するので、300個の数字が並ぶだけです。

```console:問題86の実行結果
[  2.32081000e-01   1.34141400e+00   7.57177000e-01   9.18121000e-01
   1.41462400e+00   4.61902000e-01  -3.19372000e-01  -9.11796000e-01
   6.74263000e-01   8.88596000e-01   8.66489000e-01   4.41949000e-01
  -6.52780000e-02  -5.73398000e-01  -1.72020000e-01   2.79280000e-01
  -1.61161000e-01   4.50549000e-01   7.46780000e-02  -3.13907000e-01
  -4.32671000e-01   6.18620000e-02  -1.27725100e+00   6.85341000e-01
   3.03760000e-02  -3.19811000e-01  -7.68924000e-01  -2.62472000e-01
   4.91034000e-01   9.34251000e-01  -6.05433000e-01  -5.19170000e-02
  -6.72454000e-01   1.55326600e+00  -7.37928000e-01   1.66526200e+00
  -6.69270000e-02   8.88963000e-01  -6.68554000e-01   2.86349000e-01
  -1.27271300e+00  -1.21432000e-01   1.26359000e+00   1.25684600e+00
   1.97781000e-01   8.14802000e-01   2.05766000e-01  -4.26121000e-01
   7.07411000e-01   7.51749000e-01   6.40161000e-01  -3.28497000e-01
   4.20656000e-01   4.26616000e-01  -2.29688000e-01  -4.02054000e-01
  -2.33294000e-01  -6.42150000e-02  -7.11624000e-01   1.82619000e-01
  -7.58055000e-01  -2.03132000e-01   5.12000000e-04   1.31971700e+00
   1.03481400e+00   2.22623000e-01   6.24024000e-01   9.64505000e-01
  -7.62032000e-01  -3.60960000e-02   4.45112000e-01  -5.08120000e-01
  -1.00680500e+00  -2.55381000e-01   8.55365000e-01   6.17396000e-01
  -7.78720000e-01  -6.18505000e-01   1.21397000e-01  -1.69275000e-01
   6.60319000e-01  -3.36548000e-01  -5.62175000e-01  -2.04378300e+00
  -7.94834000e-01  -4.65775000e-01  -7.54679000e-01   3.90806000e-01
  -8.01828000e-01  -4.92555000e-01   3.47642000e-01  -4.28183000e-01
  -1.99666800e+00   1.82001000e-01  -1.70085000e-01   9.28966000e-01
  -1.96638600e+00   9.23961000e-01   4.84498000e-01  -5.24912000e-01
   1.02234000e+00   4.62904000e-01   4.10672000e-01   6.97174000e-01
   6.19435000e-01   8.32230000e-02   1.41234000e-01   6.12439000e-01
  -1.45182000e+00   1.85729000e-01   5.67926000e-01  -3.29128000e-01
  -3.83217000e-01   3.79447000e-01  -5.50135000e-01  -4.12838000e-01
  -4.16418000e-01   1.05820000e-02   6.92200000e-02  -6.27480000e-02
   1.24219800e+00  -3.96815000e-01  -4.01746000e-01  -6.71752000e-01
   7.81617000e-01  -8.54749000e-01  -1.07806700e+00   7.44280000e-02
  -1.91329200e+00  -1.21407300e+00  -5.23873000e-01  -1.01673500e+00
   4.35801000e-01   1.73546700e+00  -7.54100000e-01  -5.14167000e-01
  -2.15539000e-01  -6.96321000e-01   1.45136000e-01   6.40906000e-01
  -4.21082000e-01  -3.60932000e-01  -2.98236100e+00   1.05500300e+00
  -5.42376000e-01   2.06387000e-01   2.28400000e-02  -1.87433000e-01
  -4.26448000e-01  -7.00808000e-01  -1.91694000e-01  -6.80270000e-02
   8.37304000e-01   6.18913000e-01   3.09183000e-01  -2.22531000e-01
  -3.08164000e-01   1.91496000e+00  -2.05698000e-01  -1.38298000e+00
   1.08415000e-01   5.35886000e-01  -2.32130000e-02   6.94406000e-01
  -4.17144000e-01  -1.90199000e+00   6.69315000e-01  -6.32312000e-01
  -3.45570000e-02  -6.03989000e-01   3.56266000e-01  -1.02690000e+00
   4.67688000e-01   5.27140000e-02   3.66741000e-01   1.92638600e+00
   6.22386000e-01   4.83680000e-01   1.00020800e+00   4.46445000e-01
   4.13120000e-01   2.12195000e-01   1.56286000e-01   1.33522500e+00
   6.97672000e-01   5.66884000e-01   1.53622000e-01   6.39750000e-01
  -2.03707000e-01   2.10565800e+00  -1.17320000e-01   8.55233000e-01
   2.61317700e+00  -2.14519000e-01   8.55025000e-01   9.06171000e-01
  -4.56919000e-01  -1.40941000e-01  -6.24079000e-01  -1.26463800e+00
  -9.31688000e-01   9.94177000e-01  -6.76021000e-01  -9.58533000e-01
   4.40553000e-01  -1.23600000e-03  -5.81909000e-01   3.57520000e-01
  -7.99588000e-01   1.11611700e+00  -4.93985000e-01   1.23746500e+00
  -7.51088000e-01  -9.28216000e-01   3.05621000e-01  -5.11757000e-01
   1.05883000e-01   4.88388000e-01   8.31103000e-01  -5.05967000e-01
  -1.01836400e+00  -2.54270000e-01  -4.25978000e-01   2.21318000e-01
  -7.14479000e-01   3.37610000e-01  -6.56314000e-01  -3.55550000e-01
   2.31042000e-01  -9.86197000e-01  -7.63255000e-01   1.04544800e+00
   1.57370400e+00   1.95025900e+00   5.00542000e-01  -5.48677000e-01
   5.21174000e-01  -2.04218000e-01  -2.11823000e-01  -2.30830000e-01
   1.45851700e+00  -2.69244000e-01  -8.57567000e-01   1.28116000e+00
   1.18514300e+00   7.82615000e-01  -7.24170000e-02  -1.07394300e+00
  -5.76223000e-01   5.17903000e-01   6.55052000e-01   1.56492300e+00
   1.58710000e-01   1.64205300e+00   4.27021000e-01   1.65960000e-01
   1.27899000e-01   2.45154000e-01  -3.33136000e-01   3.69693000e-01
   6.90610000e-01  -5.47800000e-01   1.87585000e-01   6.63304000e-01
  -1.18193300e+00  -3.42415000e-01  -1.97505000e-01   1.55585000e+00
   6.80237000e-01   7.02119000e-01  -1.39572100e+00  -2.07230000e-02
  -4.62809000e-01  -4.94772000e-01   2.25839000e-01   3.32944000e-01
  -7.71918000e-01  -8.55043000e-01  -5.98472000e-01  -1.60165800e+00
  -3.56646000e-01  -3.89552000e-01  -7.58449000e-01   2.03913000e-01
   2.84149000e-01  -5.72755000e-01  -4.92234000e-01  -1.15743600e+00
  -5.41931000e-01  -7.22312000e-01   8.08674000e-01  -3.62800000e-02
   2.92228000e-01   4.90371000e-01   5.50050000e-01   1.82185000e-01
  -2.12689000e-01  -1.03393500e+00   1.97234000e-01  -2.13381000e-01]
```

##### [問題87](http://qiita.com/segavvy/items/663454567a191cf1b968)の結果
「United States」と「U.S.」のコサイン類似度の算出ですが、きちんと高い値になっています。

```console:問題87の実行結果
0.858448973235
```

##### [問題88](http://qiita.com/segavvy/items/26ec387217b030a15c21)の結果
「England」と近いものを探す問題ですが、[問題88](http://qiita.com/segavvy/items/26ec387217b030a15c21)の結果とは異なり、きちんとイギリスの国が最初に並びます。word2vecすごい。

```console:問題88の実行結果
Wales	0.7539543550055905
Scotland	0.7386559299178808
Britain	0.6479338653237635
Ireland	0.6348035977770026
Sweden	0.6046247805709913
Spain	0.6012807753931683
Germany	0.5945993118023707
England.	0.5886246671101062
Norway	0.5712078065200615
London	0.5622154447245881
```

##### [問題89](http://qiita.com/segavvy/items/2d21c9b5ab7e338dcf51)の結果
アテネの首都を推測する問題です。[問題89](http://qiita.com/segavvy/items/2d21c9b5ab7e338dcf51)では正解の「Greece」は8位でコサイン類似度も0.75とやや低かったのですが、word2vecでは3位で0.81と優秀です。

```console:問題89の実行結果
Spain	0.8975386269080241
Austria	0.8165995526197494
Greece	0.8115120679668039
Egypt	0.8108041287727046
Italy	0.7967845991447613
Russia	0.7903349902284371
Denmark	0.784935131008747
Sweden	0.7731913094622944
Germany	0.7689020148989952
Portugal	0.7634638759682534
```


### word2vecとは
「word2vec」は単語をベクトル化するためのオープンソースの実装です。第9章でこれまでやってきたことをやってくれるものなのですが、次元圧縮にニューラルネットワークを使っているのが大きな特徴です。
詳細は「word2vec」でググるとたくさん情報が見つかりますので、ここでは割愛します。なお、私はオライリーの[word2vecによる自然言語処理](https://www.oreilly.co.jp/books/9784873116839/)で勉強しましたが、なかなか分かりやすかったです。

### word2vecのインストール
pythonで使えるword2vecの実装はいくつかあるようですが、今回はpipで見つけたword2vecのラッパーライブラリを使いました。`pip install word2vec`でインストールできます。

```console:
segavvy@ubuntu:~$ pip search word2vec
brocas-lm (1.0)                  - Broca's LM is a free python library
                                   providing a probabilistic language model
                                   based on a Recurrent Neural Network (RNN)
                                   with Long Short-Term Memory (LSTM). It
                                   utilizes Gensim's Word2Vec implementation
                                   to transform input word sequences into a
                                   dense vector space. The output of the model
                                   is a seqeuence of probability distributions
                                   across the given vocabulary.
word2vec-wikification-py (0.16)  - A package to run wikification
sense2vec (0.6.0)                - word2vec with NLP-specific tokens
ShallowLearn (0.0.5)             - A collection of supervised learning models
                                   based on shallow neural network approaches
                                   (e.g., word2vec and fastText) with some
                                   additional exclusive features
theano-word2vec (0.2.1)          - word2vec using Theano and Lasagne
word2vec (0.9.1)                 - Wrapper for Google word2vec
word2veckeras (0.0.5.2)          - word2vec based on Kearas and gensim
segavvy@ubuntu:~$ pip install word2vec
Collecting word2vec
  Downloading word2vec-0.9.1.tar.gz (49kB)
    100% |████████████████████████████████| 51kB 1.9MB/s 
Requirement already satisfied: numpy in ./anaconda3/lib/python3.5/site-packages (from word2vec)
Requirement already satisfied: cython in ./anaconda3/lib/python3.5/site-packages (from word2vec)
Building wheels for collected packages: word2vec
  Running setup.py bdist_wheel for word2vec ... done
  Stored in directory: /home/segavvy/.cache/pip/wheels/f9/fa/6a/4cdbfefd2835490548505e4136b8f41f063d8f3c4639bf0f53
Successfully built word2vec
Installing collected packages: word2vec
Successfully installed word2vec-0.9.1
```

これで「import word2vec」できればインストールは完了です。

```console
segavvy@ubuntu:~$ python
Python 3.5.2 |Anaconda 4.1.1 (64-bit)| (default, Jul  2 2016, 17:53:06) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import word2vec
>>> 
```

### word2vecの使い方

pythonのインタプリタで`help(word2vec.word2vec)`とするとパラメータの説明が表示されます。英語ですが、基本的にはword2vec本体のコマンドラインオプションと同じのようです。

```console:pythonインタプリタでのhelp(word2vec.word2vec)の結果
Help on function word2vec in module word2vec.scripts_interface:

word2vec(train, output, size=100, window=5, sample='1e-3', hs=0, negative=5, threads=12, iter_=5, min_count=5, alpha=0.025, debug=2, binary=1, cbow=1, save_vocab=None, read_vocab=None, verbose=False)
    word2vec execution
    
    Parameters for training:
        train <file>
            Use text data from <file> to train the model
        output <file>
            Use <file> to save the resulting word vectors / word clusters
        size <int>
            Set size of word vectors; default is 100
        window <int>
            Set max skip length between words; default is 5
        sample <float>
            Set threshold for occurrence of words. Those that appear with
            higher frequency in the training data will be randomly
            down-sampled; default is 0 (off), useful value is 1e-5
        hs <int>
            Use Hierarchical Softmax; default is 1 (0 = not used)
        negative <int>
            Number of negative examples; default is 0, common values are 5 - 10
            (0 = not used)
        threads <int>
            Use <int> threads (default 1)
        min_count <int>
            This will discard words that appear less than <int> times; default
            is 5
        alpha <float>
            Set the starting learning rate; default is 0.025
        debug <int>
            Set the debug mode (default = 2 = more info during training)
        binary <int>
            Save the resulting vectors in binary moded; default is 0 (off)
        cbow <int>
            Use the continuous back of words model; default is 1 (skip-gram
            model)
        save_vocab <file>
            The vocabulary will be saved to <file>
        read_vocab <file>
            The vocabulary will be read from <file>, not constructed from the
            training data
```

詳細は割愛しますが、「train」で入力ファイル名、「output」で結果の出力ファイル名、「size」で次元数、「threads」で処理に使うスレッド数が指定できます。
次元数はこれまでの問題と合わせるために「300」を指定しました。また、実行後にword2vecの結果を[問題85](http://qiita.com/segavvy/items/f1a7f3200c3b771e8568)と同じフォーマットへ変換する必要があるため、結果が読み込みやすいように「binary=0」を指定して、結果をテキスト形式にしています。

### word2vecの結果ファイルの解析

word2vecのテキスト形式の結果ファイルは、1行目に用語数と次元数（=300）が空白区切りで出力されています。2行目以降は1行1用語になっていて、各行には用語と対応する各次元の値300個が空白区切りで出力されています。

なお、C言語が使える方はword2vec本体のソースを見るとわかるのですが、各次元の値を出力するループの中で値＋空白を出力しているので、行末にも余分な空白が付きます。読み込む際は注意が必要です。

```c:word2vec.cの結果出力部分のソース抜粋
    // Save the word vectors
    fprintf(fo, "%lld %lld\n", vocab_size, layer1_size);
    for (a = 0; a < vocab_size; a++) {
      fprintf(fo, "%s ", vocab[a].word);
      if (binary) for (b = 0; b < layer1_size; b++) fwrite(&syn0[a * layer1_size + b], sizeof(real), 1, fo);
      else for (b = 0; b < layer1_size; b++) fprintf(fo, "%lf ", syn0[a * layer1_size + b]);
      fprintf(fo, "\n");
    }
```

今回のプログラムでは、まずword2vecで「vectors.txt」という結果ファイルを作り、それを読み込んで「matrix_x300.mat」と「dict_index_t」に変換する流れで実装しました。

### word2vecの方が結果ファイルのサイズが小さくなる
同じデータで同じ次元数を指定しているのですが、word2vecの方が結果ファイルのサイズが小さくなります。これは、低頻出語を除去する機能が働いているためです。「min_count」で調整できますが、結果ファイルのサイズが小さいほど処理が早くなるので、今回はデフォルトの状態（出現頻度が5未満の用語は除去）のままにしました。

なお、word2vecには高頻出語をランダムに除去する機能も搭載されています。こちらは「sample」で調整できます。

　
91本目のノックは以上です。誤りなどありましたら、ご指摘いただけますと幸いです。
<hr>

*実行結果には、[100本ノックで用いるコーパス・データ](http://www.cl.ecei.tohoku.ac.jp/nlp100/#data)で配布されているデータの一部が含まれます。この第10章で用いているデータのライセンスは[クリエイティブ・コモンズ 表示-継承 3.0 非移植](https://creativecommons.org/licenses/by-sa/3.0/legalcode)（[日本語訳](https://creativecommons.org/licenses/by-sa/3.0/deed.ja)）です。*