そもそも
最近?巷で流行っているword2vecですが、
(word2vecで何ができるの?という方は[あんちべさんのブログ] (http://antibayesian.hateblo.jp/entry/2014/03/10/001532)をみてみるとイメージしやすいかと思います。
ただ、中身のロジックであるC-BOWとskip-gramが全然わからないので
コードから何をやっているんだろうというのを理解しようとしてみました。
その時のメモです。(ほぼCBOWについてしか書いてないです)
リポジトリの中身
実際に使ってみたことのある方はわかるかと思いますが、
- distance.c
- word2analogy.c
- word2vec.c
- word2phrase.c
と、これらに似たような名前の.shファイルが見つかります。
.shの中身は大体以下の内容です。
学習データの取得
word2vecによる学習
各種デモの実行(distance/word2analogy/word2phrase)
demo-word.sh の場合こんなかんじ。
1. 学習データの取得
2. word2vecによる学習
******ここからdistance.cの内容*****
3. 学習済みデータ(ベクトル化済みの単語辞書)の読込
4. ユーザ入力の受け取り
5. コサイン類似度でk-NN(割とnaiveな実装でした)を実行しk-Bestを出力
6. 4に戻る
というわけでword2vec.cを読んでみる
行数は全部で700行程度と思ったより少なめ。
DeepLearningの文脈で出てくることもありますが、内部ではNNの重みを学習する過程で単語のベクトル化を行い、NNの重みは捨ててベクトル化された単語を保存します。
20弱の関数がありますが、
TrainModelThread()
CreateBinaryTree()
InitNet()
このあたりが重要かなと思います。
ただし、変数の使い回しがあったりで結構読みづらいです。
というわけでこのあたりを見てみる。
InitNet
一番簡単そうなものから。
ネットワークと単語ベクトルの初期化を行う
CreateBinaryTree
単語をひと通りなめる時に頻度もカウントしておいて
huffman-codingをする。それに応じて2分木を作っておく
TrainModelTHread
メインの学習スレッド
TrainModel()から呼ばれる
引数に応じてCBOWかSkipgramかだったり、
- hierarchical softmax
- negative sampling
をつけるか、などもここで利用する。
今回はそのうちのメインの1つCBOWについて簡単に説明する。
CBOWはひとことで言うと前後N単語それぞれのベクトルの平均値を求めるもの。
そんなことして何になるんだと思われる方もいると思いますが
「今日」「私は」「?」「に」「行くんだ」って文を見たら
「?」には場所がはいるんだろうなと予測ができますよね?
これを機械に行わせるために大量に集めてこれらの語の平均の方向へ「?」に入りそうな場所を表す語は上の様な例文の文脈でよくでてくるから同じようなベクトルを足しそうですよね。
多分これが直感的なWord2vecの話かと思います。
CBOWはそのなかでも、単語をベクトル化したものを「順序を無視して」単純に足したというところが重要だということです。
(明日京都に行きますと京都に明日行きますは文意は同じですが順序を利用してしまうと別の意味になってしまう)
本当はhuffman-codingによる効果やnegative samplingによる効果も書きたいのですが全然理解ができてないので理解ができたらまた勉強がてらまとめます。
あと、これコードが短くてCBOWだけなら書けそうなのでゆとりができればMapReduceできるようにしようと思います。