ゼロから作る DeepLearning 2 格闘日誌5/6 3章

に公開

ゼロから作るDeep Learning 2を友達と読んでいます.
自分用のまとめかつ,補充知識として重要なことを書いています.
読者対象はゼロから作るDeep Learning 実装が終わったくらいの方,Deep Learning 2を読んでいる方です.

3章

2章に引き続き単語の分散表現を得る方法を見ていく.
推論ベースの方法:ニューラルネットワーク,word2vec
シンプルで分かりやすい/処理効率は犠牲

3.1 推論ベースの手法とニューラルネットワーク

推論ベースも分布仮説に基づく
本章ではカウントベースの問題点を指摘,それに代わる推論ベースの利点を説明

3.1.1 カウントベースの手法の問題点

大規模なコーパスを扱えない( O(N^3) )現実的にSVDは不可能
カウントベースの手法は学習データを一気にまとめて処理.
推論ベースの手法は学習データの一部を使って逐次的に学習.
→語彙数が大きいコーパスにおいてカウントベースでは計算量が問題になる場合でも,ニューラルネットワークではデータを小分けにして学習できる.
さらに:複数マシン/GPUによる並列計算も可能,全体の学習を高速化.

3.1.2 推論ベースの手法の概要

コンテキストに対して単語の出現パターンを学習したモデルが各単語の確率分布を出力.
その副産物として,単語の分散表現のベクトルが得られる

3.1.3 ニューラルネットにおける単語の処理方法

単語をone-hot-表現に変換
(ある意味ベクトルは単語数次元になる...?)
例) You say goodbye and I say hello.

"you" (1,0,0,0,0,0,0)
"say" (0,1,0,0,0,0,0)
...
"." (0,0,0,0,0,0,1)

このように,単語IDの該当する箇所を1に,残りは全て0に設定する.
入力層:語彙数次元のニューロン
本章での全結合層におけるバイアスは省略.

3.2 シンプルなword2vec

word2vecには2種類ある.
CBOW: continuous-bag-of-words
skip-gram

3.2.1 CBOWモデルの推論処理

CBOWモデル:コンテキストからターゲットを推測することを目的としたNN.
入力:単語のコンテキスト.今回のモデルはCBOWモデルなので,外側から真ん中を予測する(skip-gramは内側から外側.)
入力層から中間層への変換行列がベクトル表現となる.
中間層のノード数を減らすことで,単語予測のために必要な情報をコンパクトに納めることで,結果的に密なベクトル表現が得られる.

3_2_1cbow.py
import sys
sys.path.append('..')
import numpy as np
from common.layers import Matmul

c0 = np.array([[1,0,0,0,0,0,0]])
c1 = np.array([[0,0,1,0,0,0,0]])

W_in = np.random.randn(7,3)
W_out = np.random.randn(3,7)

in_layer0 = Matmul(W_in)
in_layer1 = Matmul(W_in)
out_layer = Matmul(W_out)

h0 = in_layer0.forward(c0)
h1 = in_layer1.forward(c0)
h = 0.5 * (h0 + h1)
s =out_layer.forward(h)
print(s)

3.2.3 CBOWモデルの学習

W_{in}(3×7)とW_{out}(7×3)いずれにも単語の意味がエンコードされている.

  • 入力側だけ
  • 出力側だけ
  • 2つの重みを使う(足し合わせるなど)

3.3 学習データの準備

"You say goodbye and I say hello."をコーパスとして使用

3.3.1 コンテキストとターゲット

contexts:word2vecで用いるニューラルネットワークの入力
target:出力で予測してほしい単語

you say goodbye → [you, goodbye] [say]

3_3_1context_target.py
import sys
sys.path.append('..')
from common.utils import preprocess, create_context_target
text ='You say goodbye and I say hello.'
corpus, word_to_id, id_to_word = preprocess(text)
print("corpus:",corpus)
contexts, target = create_context_target(corpus)
print("contexts:")
print(contexts)
print("target:",target)
実行結果
corpus: [0 1 2 3 4 1 5 6]
contexts:
[[0 2]
 [1 3]
 [2 4]
 [3 1]
 [4 5]
 [1 6]]
target: [1 2 3 4 1 5]

3.3.2 one-hot表現への変換

context, targetをone-hot表現に変換.
(実装はせず,本書の提供による.)

vocab_size = len(word_to_id)
contexts = convert_one_hot(contexts, vocab_size)
target = convert_one_hot(target,vocab_size)

以上より,one-hot表現のcontextsとtargetを得られた.

3.4 CBOWモデルの実装

3.4.1 学習コードの実装

本書のtrainer, optimizerを使用.

3_4_1simple_cbow.py
import sys
sys.path.append('..')
from common.trainer import Trainer
from common.optimizer import Adam
from simple_cbow import SimpleCBOW
from common.utils import preprocess, create_context_target, convert_one_hot

window_size = 1
hidden_size = 5
batch_size = 3
max_epoch = 1000

text ='You say goodbye and I say hello.'
corpus, word_to_id, id_to_word = preprocess(text)

contexts, target = create_context_target(corpus)

vocab_size = len(word_to_id)

contexts = convert_one_hot(contexts, vocab_size)
target = convert_one_hot(target,vocab_size)

model = SimpleCBOW(vocab_size,hidden_size)
optimizer =Adam()
trainer = Trainer(model, optimizer)
trainer.fit(contexts,target,max_epoch,batch_size)
trainer.plot()

you [ 0.78739786  1.2351654  -1.3133503  -1.306601    1.0372504 ]
say [-1.265859   -0.18141685  1.291368    0.06596346 -0.35498974]
goodbye [ 1.1238313   0.5965251  -0.44927633 -0.47516468  0.99999875]
and [-1.1147761  1.4734994  1.1048938 -1.4662195 -1.3940052]
i [ 1.1328448   0.6149101  -0.41845414 -0.50209147  0.96877456]
hello [ 0.77423674  1.2310126  -1.308908   -1.3032073   1.0586238 ]
. [-1.097413  -1.2244037  1.0699775  1.2419415  1.425111 ]

単語の分散表現の学習が行えていることが分かる.以上より,CBOWモデルに基づいたword2vecによる学習モデルの実装が完了した.

3.5 word2vecに関する補足

確率の視点からみたCBOWモデル/skip-gramモデルについて考える.

3.5.1 CBOWモデルと確率

コンテクストw_{t-1},w_{t+1}が与えられたとき,ターゲットがw_tとなる確率は

P(w_t|w_{t-1},w_{t+1})

単なるマルコフ過程を実装するのであればこの確率に基づいて次の単語を予測するが,私たちの目的はこれを用いて目的関数L(具体的には負の対数尤度)を最小化すること.
教師ラベルt_kとして,(例:t_0=[1,0,0,...,0]) L

L = - \sum_{k} \log t_k P(w_{t+k}|w_{t+k-1},w_{t+k+1}) = -\log P(w_t|w_{t-1},w_{t+1})

コーパス全体に拡張すれば

L_{sum} = - \sum_{t=1}^T \log P(w_t|w_{t-1},w_{t+1})

3.5.2 skip-gramモデル v.s. CBOWモデル

skip-gram:CBOWモデルのコンテクストとターゲットを逆転したモデル

ターゲットw_tが与えられたとき,コンテクストw_{t-1},w_{t+1}である確率は

P(w_{t-1},w_{t+1}|w_t)

ここで,コンテキストw_{t-1}w_{t+1}の単語の間の条件付き独立を仮定して,

P(w_{t-1},w_{t+1}|w_t) = P(w_{t-1}|w_t)P(w_{t+1}|w_t)

損失関数L

L = -\log P(w_{t-1},w_{t+1}|w_t)

コーパス全体に拡張した損失関数L_{sum}は,

L_{sum} = - \sum_{t=1}^T \log P(w_{t-1},w_{t+1}|w_t)

単語の分散表現の精度の点でskip-gramのほうが良い結果を得られる(低頻出の単語,類推問題の性能の点でskip-gramが優秀)
分散表現の評価方法については次章4.4.2 単語ベクトルの評価方法にて解説.

3.5.3 カウントベース v.s. 推論ベース

カウントベースと推論ベースには関連性.skip-gramとnegative-samplingを利用したモデルは,コーパス全体の共起行列に対する特殊な行列分解しているのと同じ(Levy & Goldberg (2014))

該当論文: https://proceedings.neurips.cc/paper_files/paper/2014/file/b78666971ceae55a8e87efb7cbfd9ad4-Paper.pdf

他の論文はこんなのを見つけた:
https://openreview.net/pdf?id=rJfJiR5ooX

3.6 まとめ

本章で学んだこと

  • 推論ベースの手法では,周囲の単語情報から単語を推測する過程を通して単語の分散表現を得る
  • word2vecは推論ベースの手法であり,シンプルな2層NNで構成される
  • skip-gramとCBOW
  • word2vecなどの推論ベースの手法は重みの再学習が可能な為,分散表現の更新,追加が効率的

次章で

  • word2vecの転移学習
  • CBOWモデルの効率の改善
  • Negative Sampling

Discussion