KZKY memo

自分用メモ.

TensorFlow: Vector Representations of Words

word2vec modelの例.word embeddingsていう単語のベクトル表現に使用するembedding matrixを学習で求めるモデル.

Highlights

  • WordをVectorとして表現したい動機
  • modelの直感的解釈とどうやって訓練するか
  • TFでどうやってやるか
  • Scaleさせるやり方

Motivation: Why Learn Word Embeddings?

Image, Audioデータはデンスで,人間でも生のデータからこれらの領域のタスクを取り扱える.Textはスパースで取り扱いづらい.よくidで表現するけれど,この生のidデータ自身は待ったく意味を持たない.NLPの分野ではこの問題に昔から取り組んできた.Vector Space Models (VSMs)はwordを連続な空間で取り扱う,意味的に近い単語は,この空間で近くにマップされるという考え方. Distributional Hypothesisっていう仮定があって,同じコンテキストで出てくる単語は意味を共有している.

2つのアプローチがあって,count-based (e.g., Latent Semantic Analysis)とpredictive model (neural probabilistic language models)がある.

count-basedは大きいコーパスから近くに出てくる単語の統計値をとって,このmapを使って,小さいデンスなベクトルにする.predictive modelは,ある単語の近くにある複数の単語から,学習された小さいデンスなエンベッドベクトルを使って,ある単語を予測する.

Word2vecっていうのは,生のテキストから学習されるword embedding計算量的にイケているpredictive model.Continuous Bag-of-Words model (CBOW) と the Skip-Gram modelでいう2ついい点がある.Continuous Bag-of-Words model (CBOW)は,ソースとなる複数の単語(context words)から次の単語を予測する.Skip-Gramはこれの逆変換で,単語から複数の単語(context words)を予測する.統計的には,CBOWは,多くの分布情報を平滑化するので,小さいデータセットで役立つ.Skip-Gramは,context, targetのペアを新しい観測として取り扱うので,大きいデータセットでうまく機能する.

Scaling up with Noise-Contrastive Training

普通,目的関数はLog-Likelihoodを使い,likelihoodには,softmax(socre(w_t, h))を使う.例えば,w_tは時刻tのおける次のword, hはコンテキストベクターを想像しておけば良い.何も考えずにsoftmaxを使うと,NLPのword predictionのようにクラスが非常に多い場合には,softmaxの母数のノーマライズでVocabularyの中にあるすべての単語に関してsocreをとる操作が非常に計算コストが高い.

なので,Vocab中のすべての単語ではなくて,nosise wordsを考えて,実際に予測したい単語 vs noise wordsのbinary logistic regressionを考える.

{ \displaystyle
\log Q_{\theta} (D = 1 | w_t, h) + k \mathbb{E}_{w^{\tilde} \propto P_{noise} } Q_{\theta}( D = 0 | w^{\tilde}, h) 
}

これを見ると,プラクティカルには,第1項は,実際に予測したい単語のscore(w_t, h)にlogistic funtionをとって対数をつけた項で,第2項は,nosie wordsの分布で測った対数尤度の期待値,kはnosie wordsの数, Q\_{\theta} ( D = 0 | w^{\tilde}, h)  1 - \sigma( score ( w^{\tilde}, h ) ) を考えておけば良い.これはNegative Samplingとも呼ばれる.

TFだと,

tf.nn.nce_loss()

を使う.他にも

tf.nn.sampled_softmax_loss()

が代替としてある.

The Skip-gram Model

例として,こんな文がある.

the quick brown fox jumped over the lazy dog

この文からdatasetを作る場合は,ある単語をtargetとしてその周辺単語(e.g., 周辺1つ)をコンテキストとして,(context ,target)-pairをつくると

([the, brown], quick), ([quick, fox], brown), ([brown, jumped], fox), ...

な感じ.skip-gramモデルだと,targetからcontextを予想するので,この場合は,

(quick, the), (quick, brown), (brown, quick), (brown, fox), ...

こういう(input, output)-pairのデータセットを想定すればいい.

学習されたword embeddingsでwordをvector spaceにプロジェクトしてから, t-SNEで,2-dへ次元圧縮して可視化すると構造論とか意味論的に面白い結果が得られる.特に,vector spaceでの方向に関して興味深い結果が得られる.例えば,king - queen = man - womanで,male - femaleという意味の結果になる.詳しくはMikolov et al., 2013

これは,4項類推とかにつかえる.例えば,

king is to queen as father is to ?

の質問に対して,vec(king) - vec(queen) = vec(father) - vec(x)の方程式を代替満たすxを答えにすればいい.

Building the Graph

TFで実際にどうやってWord2Vecをやるかの説明.

word2vec_basic.pyに書いてある内容の抜粋

embeddingsはvocabulary_size x embedding_sizeのデッカイ行列

embeddings = tf.Variable(
    tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))

embeddings layerしか考えないなら,いきなりlogistの計算をするためのW, bを作る.

nce_weights = tf.Variable(
  tf.truncated_normal([vocabulary_size, embedding_size],
                      stddev=1.0 / math.sqrt(embedding_size)))
nce_biases = tf.Variable(tf.zeros([vocabulary_size]))

単語は初めにinteger化しておくこと.するとinput/outputは

# Placeholders for inputs
train_inputs = tf.placeholder(tf.int32, shape=[batch_size])
train_labels = tf.placeholder(tf.int32, shape=[batch_size, 1])

なる.word2vecのvectorのlook-upは関数が用意されていて,

embed = tf.nn.embedding_lookup(embeddings, train_inputs)

word2vec_basic.pyをみると,

# Ops and variables pinned to the CPU because of missing GPU implementation

て書いてあるけれど,APIみても,embeddeding_looup/nce_lossのどちらかだかわからない.

nce lossを定義して,

# Compute the NCE loss, using a sample of the negative labels each time.
loss = tf.reduce_mean(
  tf.nn.nce_loss(nce_weights, nce_biases, embed, train_labels,
                 num_sampled, vocabulary_size))

train_labelsが入っているのに注目.

最後に,optを定義する

# We use the SGD optimizer.
optimizer = tf.train.GradientDescentOptimizer(learning_rate=1.0).minimize(loss)

Training the Model

sess.runする.

for inputs, labels in generate_batch(...):
  feed_dict = {training_inputs: inputs, training_labels: labels}
  _, cur_loss = session.run([optimizer, loss], feed_dict=feed_dict)

Visualizing the Learned Embeddings

word2vec.pyがサンプル.t-SNEを使って結果を2次元にmapしてくれる.

Evaluating Embeddings: Analogical Reasoning

4項類推での評価方法.データセットここにあって,word2vec.pyに評価のサンプルがある.

Optimizing the Implementation

tf.nn.nce_lossの代わりにtf.nn.sampled_softmax_lossもある.I/O boundとおもったら,カスタムデータリーダーを実装すれば良い.例はword2vec.pyにある.I/O boundでなくて,もっと計算速度的なパフォーマンスが欲しかったらカスタムopを追加すればいい.例は,word2vec_optimized.pyにある.

Conclusion

ここではWord2Vecの例を学んだ.