のんびりしているエンジニアの日記

ソフトウェアなどのエンジニア的な何かを書きます。

Word Embedding using GloVe

Sponsored Links

GloVeについて調べてみた。
皆さんこんにちは。お元気ですか。

先日、EMNLP勉強会@PFIに行ってきました。
専門とは異なるので、普段聞けない話が聞けてよかったですね。
個人的にはRNN,LSTMがどう使われているのか、
Word Embeddingが流行していたそうだといったことを知りました。

さて、ここからが本題です。Word Embeddingが流行していたということで
どんなツールがあるのか、GloVeを使っているそうなので、実際に実行してみたいと思います。

GloVe

GloVeとは?

GloVeとは、Global Vectors for Word Representationの略です。その名の通り、ワードを表現する大域的な特徴ベクトルを計算します。単語をD次元ベクトルに変換することができ、言葉と言葉の距離の計算を可能とします。

nlp.stanford.edu

GloVeのプロジェクトページには次の説明がされています。

GloVe is an unsupervised learning algorithm for obtaining vector representations for words. Training is performed on aggregated global word-word co-occurrence statistics from a corpus, and the resulting representations showcase interesting linear substructures of the word vector space.

Gloveはワードのベクトル表現を得るための教師なし学習です。トレーニングはコーパスからワードとワードの共起関係を取得し、結果の表現はベクトル空間の線形土台を見せています。
(本当に日本語あってるのだろうか・・・・)

これを実行してみたいと思います。

GloVeの実行

GloVeのデモをまずは、実行してみましょう。
プロジェクトサイトから簡単に落とすことができます。

unzip GloVe-1.2.zip
cd GloVe-1.2
make
./demo.sh

終了するとvector.txtを見てみましょう。

the -0.758453 -2.523403 0.299992 0.267743 0.381706 -0.376821 -0.974731 1.306025 -0.138829 -0.722273 -0.064311 0.263958 -0.415959 -0.430698 -0.819925 -0.364475 0.494827 -1.504928 0.091005 0.121923 -0.587493 -0.639555 0.027899 1.459869 0.194540 1.657798 -0.669957 0.468008 0.482504 -0.060323 -1.217621 -0.640420 0.272116 1.721379 0.893092 -0.104101 -0.950922 -0.640129 -0.382053 -1.482101 -0.333742 1.246693 -0.296496 -0.167184 -1.364057 -0.701479 -0.516832 -0.189075 0.307086 0.544367
of 0.355966 -2.764249 -0.784797 -0.430014 0.015815 0.251706 -1.172660 1.231570 -0.088100 -1.096690 0.078604 1.046581 -0.801276 0.113112 -1.217713 -0.899042 -0.017204 -1.573786 0.101672 0.326505 -0.707793 0.030542 -0.207718 1.729472 -0.178380 -0.001650 -0.427250 -0.346689 0.199061 -0.588670 -1.971376 0.028140 0.746297 1.487038 0.615223 0.112806 -0.722166 -0.693157 0.178179 -1.518002 -1.013502 1.250527 -0.574263 -0.745865 -0.373828 -0.834977 -0.528751 0.019232 0.265046 0.936585
and 0.446098 -1.811615 -0.353268 -0.051443 0.040332 0.812978 -0.593563 0.966398 -0.566344 -0.592895 0.433528 0.727498 -0.504422 0.213339 -0.685778 -0.574884 -0.004847 -2.175045 0.767085 0.031597 -0.495819 -0.758598 -0.668815 1.427099 0.330896 0.562528 -0.295899 -0.172762 1.050802 -0.102661 -1.193188 -0.329468 0.016036 0.888725 -0.679774 -0.276835 -1.037455 -0.431113 -0.929244 -1.424251 -1.184382 1.838362 -0.227819 0.078359 -0.694604 -1.115786 -0.071506 -0.096441 0.877182 0.179364
one 0.364596 -1.943173 -0.537504 0.814123 0.322067 0.037298 -1.122736 0.683297 -0.299700 0.147351 -0.605290 0.741196 -0.732275 -0.546695 -0.146561 -0.368056 1.428013 -1.519689 1.156027 0.482965 -0.345150 -0.855212 -0.040511 1.594894 -0.104000 0.609003 0.996303 -0.257099 0.953962 0.439300 -0.464679 -0.903534 0.584461 1.348905 -0.046460 -0.118805 -1.794130 -0.269246 -0.409564 -0.853598 -1.710586 1.450581 -0.366107 -0.853765 -0.863046 -1.150611 -2.109000 0.271317 0.416582 0.961476
in 0.117041 -2.358588 -0.262088 0.040849 0.866629 0.069522 -0.929005 0.808353 0.790301 -0.885243 0.006898 0.871831 -0.322603 -0.160780 0.137975 -0.963780 -0.066032 -1.770403 0.863770 0.071457 -0.856015 -1.212066 0.589558 0.851039 0.334259 0.810518 -0.589247 -0.813436 1.001811 0.535292 -0.706110 -0.480132 0.626726 1.318615 -0.010253 0.218757 -0.832403 -0.566802 -0.332122 -1.551412 -1.288447 0.964842 -0.192673 -0.095291 -1.511273 -1.180612 -0.978853 -0.141687 1.096499 0.238801
a -0.335620 -1.886768 -0.652837 -0.144978 1.066821 0.735378 -1.055512 0.568546 0.503230 -0.714855 -0.036076 -0.018779 -0.794496 0.060566 -1.061533 0.097322 -0.058681 -0.876165 0.367434 0.616753 -1.049416 0.382041 0.679451 1.160343 0.255098 1.475383 0.489245 -0.628017 -0.158506 1.294099 -0.938892 0.055180 1.305304 1.872185 -0.590877 -0.267683 -1.529724 -0.891699 -0.547773 -1.606850 -0.391448 1.650338 0.661356 0.234639 -0.681136 -0.903211 -1.194046 -0.139752 0.390438 0.544274
to -0.106022 -2.133875 -0.085214 -0.202362 -0.348531 0.806785 -1.294710 1.821011 0.172374 -0.270730 0.419197 0.873153 -0.664268 -0.490616 -0.700092 0.072916 0.410310 -1.283809 1.787798 -0.402262 -0.024345 0.418891 1.083327 1.232473 0.408190 0.307005 -0.271150 -0.045721 0.400825 1.136887 -1.321607 -1.374747 0.644898 1.460335 -0.024055 -0.741743 -0.998496 -2.074960 -0.454168 -1.572479 -0.867492 0.984265 -0.432214 -0.115469 -1.851609 -0.607529 0.038281 0.280623 0.956921 -0.229746
zero -0.024830 -2.210507 -0.668605 0.943509 -0.093947 -0.457017 -1.242542 1.435944 0.216868 1.130904 0.100059 -0.164164 -0.724447 -1.303184 -0.715328 -1.243692 0.887440 -1.583009 1.293877 0.076488 -0.604136 -0.447726 0.243468 0.982450 -0.088473 0.741850 0.882386 -0.280220 0.542730 0.735642 -0.373908 -0.489380 1.063772 0.739898 -0.213539 -0.064189 -1.240139 -0.527681 -0.123412 -1.258443 -1.626607 1.587301 0.001785 -1.572981 -0.329720 -0.980483 -1.924067 0.065859 0.704996 0.204892

ワードとベクトルの組み合わせがテキストファイルに書き込まれています。.shを確認すると

$BUILDDIR/vocab_count -min-count $VOCAB_MIN_COUNT -verbose $VERBOSE < $CORPUS > $VOCAB_FILE
$BUILDDIR/cooccur -memory $MEMORY -vocab-file $VOCAB_FILE -verbose $VERBOSE -window-size $WINDOW_SIZE < $CORPUS > $COOCCURRENCE_FILE
$BUILDDIR/shuffle -memory $MEMORY -verbose $VERBOSE < $COOCCURRENCE_FILE > $COOCCURRENCE_SHUF_FILE
$BUILDDIR/glove -save-file $SAVE_FILE -threads $NUM_THREADS -input-file $COOCCURRENCE_SHUF_FILE -x-max $X_MAX -iter $MAX_ITER -vector-size $VECTOR_SIZE -binary $BINARY -vocab-file $VOCAB_FILE -verbose $VERBOSE

この順番で動作させると、手動でファイル実行することができるそうです。
変数は適宜置き換えてみてください。CORPUSは通常のテキストファイルなので、文字が入っていれば問題ないと思います。(日本語だと形態素解析などをしてsplitする必要があると思います。)

一行目のみ表示してみます。

 anarchism originated as a term of abuse first used against early working class radicals including the diggers of the english revolution and the sans culottes of the french revolution whilst the term is still used in a pejorative way to describe any act that used violent means to destroy the organization of society it has also been taken up as a positive label by self defined anarchists the word anarchism is derived from the greek without archons rule

Python Binding

僕はPythonが好きです。そこでPythonのBindingを探してみました。
Python BIndingがあったので使ってみようと思います。

github.com

インストール方法

git clone https://github.com/maciejkula/glove-python.git
cd glove-python/
brew install gcc49
sudo python setup.py install

Exampleの実行

さて、Exampleを元に作成してみます。
面倒なのでtext8で実行してみます。

python example.py -c text8 -t 10
Python 2.7.8 (default, Aug 17 2015, 21:21:04)
Type "copyright", "credits" or "license" for more information.

IPython 4.0.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.
Pre-processing corpus
Dict size: 253855
Collocations: 27126482
Training the GloVe model
Performing 10 training epochs with 1 threads
Epoch 0
Epoch 1
Epoch 2
Epoch 3
Epoch 4
Epoch 5
Epoch 6
Epoch 7
Epoch 8
Epoch 9

In [1]: glove.most_similar('physics')
Out[1]:
[('chemistry', 0.94415466526568825),
 ('mathematics', 0.91902354333821079),
 ('mathematical', 0.897438750317059),
 ('quantum', 0.89535388695725449)]

In [2]: glove.most_similar('a')
Out[2]:
[('goldfinches', 0.90560441332694197),
 ('tiny', 0.89832854729840927),
 ('shield', 0.89193666825792484),
 ('loose', 0.89111940986604166)]

In [3]: glove.most_similar('big')
Out[3]:
[('man', 0.88264078976629123),
 ('star', 0.87335378017955623),
 ('girl', 0.84603632780899607),
 ('song', 0.84302185664132367)]

ソースコード解説

基本は普段どおり、データ準備→学習です。
exampleを改良し、必要な箇所を使って改変すると以下の通りになります。

from __future__ import print_function
import argparse
import pprint

from glove import Glove, Corpus

def read_corpus(filename):
    data = []
    with open(filename, 'r') as datafile:
        for line in datafile:
            data.append(line.lower().split(' '))
    return data

if __name__ == '__main__':
    print('Pre-processing corpus')

    corpus_model = Corpus()
    corpus_model.fit(read_corpus("text8"), window=10)
    corpus_model.save('corpus.model')

    print('Dict size: %s' % len(corpus_model.dictionary))
    print('Collocations: %s' % corpus_model.matrix.nnz)
    print('Training the GloVe model')

    glove = Glove(no_components=100, learning_rate=0.05)
    glove.fit(corpus_model.matrix, epochs=int(10),
              no_threads=2, verbose=True)
    glove.add_dictionary(corpus_model.dictionary)

    glove.save('glove.model')
    print(glove.most_similar("queen"))
    print(glove.most_similar("king"))
    print(glove.most_similar("physis"))

生成の段階は2つに分かれます。
①コーパス生成
②Gloveでの学習

コーパス生成

コーパス生成は以下のコードの部分です。

def read_corpus(filename):
    data = []
    with open(filename, 'r') as datafile:
        for line in datafile:
            data.append(line.lower().split(' '))
    return data

    corpus_model = Corpus()
    corpus_model.fit(read_corpus("text8"), window=10)
    corpus_model.save('corpus.model')

ファイルについて、1行1行に分割し、単語ごとにSplitをしています。(英語なので1単語は空白でSplitすればよいですね。)
それにもとづいてwindow幅のパラメータを設定してコーパスモデルを作成します。

Gloveでの学習

Gloveでの学習は以下の箇所が該当します。

    glove = Glove(no_components=100, learning_rate=0.05)
    glove.fit(corpus_model.matrix, epochs=int(10),
              no_threads=2, verbose=True)
    glove.add_dictionary(corpus_model.dictionary)

    glove.save('glove.model')

Gloveインスタンスの生成時にパラメータを設定します。学習率とかベクトルの次元ですね。
後はfitにパラメータを入力し、辞書を設定します。この辞書は学習したベクトルとの対応関係ですね。
最後にモデルをセーブして終わりです。
後はmost_similarを使うことで、ベクトル同士の計算を行う、類似ワードを出力することができます。