論文について
タイトル
GloVe: Global Vectors for Word Representation
著者
Jeffrey Pennington, Richard Socher, Christopher D. Manning
所属
Stanford NLP
3行まとめ
“global matrix factorization methods”と”local context window methods”のいいとこ取りをしていい感じのword embeddingを獲得する手法を提案するよ。GloVeは単語-単語のスパースな共起行列全体や超巨大なコーパス上の個々のlocal context windowを使うことなく、共起行列の非ゼロな要素だけを使って効率的に学習できるよ。
補足:前者の”global matrix factorization”はいわゆるカウントベースの手法で、単語-単語の共起行列をmatrxi factorizeするモデルで、後者の”local contet windows methods”というのは対象語(target)を周辺語(local context)から予測するというモデル(e.g. CBOWやその他言語モデル)である。
関連研究
global matrix factorization methods
local context window methods
Skip-gram、CBOW
このような手法は、アナロジータスクに強いが、統計情報は(コーパス全体の共起行列ではなくlocalなcontext windowを使っているため)活用できていない。
手法
まず、単語-単語の共起のカウントを格納した行列(共起行列)をX X X と表す。このとき、
X i j X_{ij} X i j
とは、文脈語i i i の文脈で単語j j j の出現回数を表す。さらに、
X i = ∑ k X i k X_i = \sum_{k} X_{ik} X i = k ∑ X i k
はi i i が文脈語として出現した合計回数になる。また、
P i j = P ( j ∣ i ) = X i j X i P_{ij} = P(j | i) = \frac{X_{ij}}{X_i} P i j = P ( j ∣ i ) = X i X i j
は単語j j j が文脈語i i i の文脈で出現する確率とする。
熱力学を考えたときにi = ice i=\text{ice} i = ice でj = steam j=\text{steam} j = steam で考えてみる。
k = solid k=\text{solid} k = solid のとき、solidはsteamではなく、iceに関連があるためP i k P j k \frac{P_{ik}}{P_{jk}} P j k P i k の値は大きくなると考えられる。
逆にk = gas k=\text{gas} k = gas のとき、gasはiceではなく、steamに関連があるためP i k P j k \frac{P_{ik}}{P_{jk}} P j k P i k の値は小さくなると考えられる。
k = water or fashion k = \text{water or fashion} k = water or fashion のときは、k k k はiceとsteamの両方に関係しているか、どちらにも関係していないので、P i k P j k \frac{P_{ik}}{P_{jk}} P j k P i k の値は1に近づくはず。
上記の議論は、単語ベクトル学習のための適切な出発点は、確率それ自体よりもむしろ共起確率の比率にあるべきであることを示唆している。これを定式化すると、
F ( w i , w j , w ~ k ) = P i k P j k ( 1 ) F \left( w _ { i } , w _ { j } , \tilde { w } _ { k } \right) = \frac { P _ { i k } } { P _ { j k } } ~ ~ ~ ~ ~ ~ ~ (1) F ( w i , w j , w ~ k ) = P j k P i k ( 1 )
ここで、w ∈ R d w \in \mathbb{R}^d w ∈ R d は単語ベクトル、w ~ ∈ R d \tilde{w} \in \mathbb{R}^d w ~ ∈ R d は文脈単語ベクトルである。
最初に、単語ベクトル空間においてP i k P j k \frac{P_{ik}}{P_{jk}} P j k P i k を表す情報に変換するF F F を考える。ベクトル空間は線形構造なので、ベクトルの差を使って
F ( w i − w j , w ~ k ) = P i k P j k . ( 2 ) F \left( w _ { i } - w _ { j } , \tilde { w } _ { k } \right) = \frac { P _ { i k } } { P _ { j k } }. ~ ~ ~ ~ ~ ~ ~ (2) F ( w i − w j , w ~ k ) = P j k P i k . ( 2 )
左辺の引数はベクトル、右辺はスカラである。
F F F は、例えばニューラルネットワークによってパラメータ化された複雑な関数であると見なすことができるが、そうしてしまうと前提にある線形構造をobfuscateしてしまうので、F F F の引数同士のdot productとしてしてあげると、
F ( ( w i − w j ) T w ~ k ) = P i k P j k . ( 3 ) F \left( \left(w _ { i } - w _ { j }\right)^{\mathrm{T}}\tilde { w } _ { k } \right) = \frac { P _ { i k } } { P _ { j k } }. ~ ~ ~ ~ ~ ~ ~ (3) F ( ( w i − w j ) T w ~ k ) = P j k P i k . ( 3 )
次に、単語-単語の共起行列の場合、単語と文脈語の区別は任意であり、2つの役割を自由に交換できます。つまり、w ↔ w ~ w \leftrightarrow \tilde { w } w ↔ w ~ だけでなく、X ↔ X T X \leftrightarrow X ^ { T } X ↔ X T も交換可能でないといけないが、( 3 ) (3) ( 3 ) は満たしていない。
まずはじめに、F F F について( R , + ) ( \mathbb { R } , + ) ( R , + ) と( R > 0 , × ) \left( \mathbb { R } _ { > 0 } , \times \right) ( R > 0 , × ) の間で準同型(homomorphism)を仮定すると、
F ( ( w i − w j ) T w ~ k ) = F ( w i T w ~ k ) F ( w j T w ~ k ) . ( 4 ) F \left( \left( w _ { i } - w _ { j } \right) ^ { T } \tilde { w } _ { k } \right) = \frac { F \left( w _ { i } ^ { T } \tilde { w } _ { k } \right) } { F \left( w _ { j } ^ { T } \tilde { w } _ { k } \right) }. ~ ~ ~ ~ ~ ~ ~ (4) F ( ( w i − w j ) T w ~ k ) = F ( w j T w ~ k ) F ( w i T w ~ k ) . ( 4 )
補足:F F F について加法群( R , + ) (\mathbb{R, +}) ( R , + ) から乗法群( R > 0 , × ) \left( \mathbb { R } _ { > 0 } , \times \right) ( R > 0 , × ) への準同型であるとは、
F ( a + b ) = F ( a ) F ( b ) F(a+b) = F(a)F(b) F ( a + b ) = F ( a ) F ( b )
を満たすことを言う。
( 3 ) ( 4 ) (3)(4) ( 3 ) ( 4 ) より、
F ( w i T w ~ k ) = P i k = X i k X i . ( 5 ) F \left( w _ { i } ^ { T } \tilde { w } _ { k } \right) = P _ { i k } = \frac { X _ { i k } } { X _ { i } }. ~ ~ ~ ~ ~ ~ ~ (5) F ( w i T w ~ k ) = P i k = X i X i k . ( 5 )
F = exp F = \exp F = exp とおくと、
w i T w ~ k = log ( P i k ) = log ( X i k ) − log ( X i ) . ( 6 ) w _ { i } ^ { T } \tilde { w } _ { k } = \log \left( P _ { i k } \right) = \log \left( X _ { i k } \right) - \log \left( X _ { i } \right) . ~ ~ ~ ~ ~ ~ ~ (6) w i T w ~ k = log ( P i k ) = log ( X i k ) − log ( X i ) . ( 6 )
( 6 ) (6) ( 6 ) のlog ( X i ) \log \left( X _ { i } \right) log ( X i ) がなければ、交換対称性があるのに。。。。
補足:w ↔ w ~ w \leftrightarrow \tilde { w } w ↔ w ~ 、X ↔ X T X \leftrightarrow X ^ { T } X ↔ X T としたときに(6)は
w ~ i T w k = log ( X i k T ) − log ( X i T ) ( ∗ ) \tilde{w} _ { i } ^ { T } w _ { k } = \log \left( X ^ {\mathrm{T}} _ { i k } \right) - \log \left( X ^ {\mathrm{T}} _ { i } \right) ~ ~ ~ ~ ~ ~ ~ (\ast) w ~ i T w k = log ( X i k T ) − log ( X i T ) ( ∗ )
となる。一方i ↔ k i\leftrightarrow k i ↔ k としたときに、
w k w ~ i T = log ( X k i ) − log ( X k ) w _ { k } \tilde{w} _ { i } ^ { T } = \log \left( X _ { ki } \right) - \log \left( X _ { k } \right) w k w ~ i T = log ( X k i ) − log ( X k )
⇔ w ~ i T w k = log ( X i k T ) − log ( X k ) ( ∗ ∗ ) \Leftrightarrow \tilde{w} _ { i } ^ { T } w _ { k } = \log \left( X ^ {\mathrm{T}} _ { i k } \right) - \log \left( X _ { k } \right) ~ ~ ~ ~ ~ ~ ~ (\ast \ast) ⇔ w ~ i T w k = log ( X i k T ) − log ( X k ) ( ∗ ∗ )
となり、( ∗ ) (\ast) ( ∗ ) と( ∗ ∗ ) (\ast \ast) ( ∗ ∗ ) を比較したときに矛盾する。(∵ log ( X i T ) ≠ log ( X k ) \because \log \left(X_i^\mathrm{T} \right) \neq \log \left(X_k \right) ∵ log ( X i T ) = log ( X k ) )
そこで、( 6 ) (6) ( 6 ) について、この項はk k k とは関係がないので(w i w_i w i に対する)バイアスb i b_i b i で置き換えることを考え、さらに(w ~ k \tilde{w}_k w ~ k に対する)バイアスb ~ k \tilde{b}_k b ~ k を付け加えることで、対称性を持った
w i T w ~ k + b i + b ~ k = log ( X i k ) w _ { i } ^ { T } \tilde { w } _ { k } + b _ { i } + \tilde { b } _ { k } = \log \left( X _ { i k } \right) w i T w ~ k + b i + b ~ k = log ( X i k )
を得る。これの2乗誤差に重み関数f f f で重み付けを行ったものを損失関数とする。
J = ∑ i , j = 1 V f ( X i j ) ( w i T w ~ j + b i + b ~ j − log X i j ) 2 J = \sum _ { i , j = 1 } ^ { V } f \left( X _ { i j } \right) \left( w _ { i } ^ { T } \tilde { w } _ { j } + b _ { i } + \tilde { b } _ { j } - \log X _ { i j } \right) ^ { 2 } J = i , j = 1 ∑ V f ( X i j ) ( w i T w ~ j + b i + b ~ j − log X i j ) 2
ここでV V V は語彙数、重み関数f f f は
f ( x ) = { ( x / x max ) α if x < x max 1 otherwise f ( x ) = \left\{ \begin{array} { c c } { \left( x / x _ { \max } \right) ^ { \alpha } } & { \text { if } x < x _ { \max } } \\ { 1 } & { \text { otherwise } } \end{array} \right. f ( x ) = { ( x / x m a x ) α 1 if x < x m a x otherwise
x max x _ { \max } x m a x は100 100 1 0 0 、α \alpha α は3 / 4 3/4 3 / 4 のときに良い結果が得られた。negative samplingのときと同じ値だ。。。
f ( 0 ) = 0 f(0) = 0 f ( 0 ) = 0 なのでそもそもX X X の非ゼロな要素だけ見れば良いので嬉しい。(語彙やコーパスサイズにもよるが、X X X の75–95%はゼロな要素らしい。。。)
実験
タスク
学習につかったコーパス
2010 Wikipedia dump with 1 billion tokens
2014 Wikipedia dump with 1.6 billion tokens
Gigaword 5 which has 4.3 billion tokens
2と3の足し合わせ 6 billion tokens
42 billion tokens of web data, from Common Crawl
Stanford tokenizerでトークナイズ、 最頻出の40万語を語彙として使用。
※5.については200万語を語彙として使用。
比較手法
結果
Word analogies
概して、GloVeが良かった。特に、42Bものバカでかいコーパスでも簡単に学習することができた。SVD-Lはコーパスサイズが大きくなると逆に精度が悪くなったが、GloVeは良くなった。(GloVeは重み関数f f f が効いている?)
Word similarity
概してGloVeが良い。SVD-Lとの比較は先程と同じだが、より大きいコーパスで学習したCBOWよりもGloVeの方が良かった。
NER
概してGloVeが良い。SVD-Lとの比較は先程と同じだが、より大きいコーパスで学習したCBOWよりもGloVeの方が良かった。
考察
Embeddingの次元と文脈の長さについて
(a) だいたい200次元くらいでaccuracyが頭打ちになる。
(b)(c) Syntacticについては語順に強く依存するためAsynmmetric context(contextが注目語の左のみ)の方が良く、window sizeを大きくすると悪化する。Semanticについてはlocalではなく、window sizeが大きければ大きいほどよい結果となった。
コーパスサイズについて
Syntacticについてはコーパスのサイズが大きければ大きいほど良い傾向にあるが、Semnticについては必ずしもその限りではなく、コーパスの性質によって変わってくる。例えばアナロジータスクでは地名に関する設問があったが、Wiki2014の方ではそれをカバーできてきたと考えれる。
学習時間について
40万語彙、60億語のコーパスでWindow size = 10で共起行列X X X をつくるのに85分かかったらしい。学習には32コアすべてを使って1イテレーション14分しかかからなかった。(Figure 4参照)
Skip-gram、CBOWとの比較
単純な比較はパラメータが多すぎるのでむずかしい。学習時間で比較したい。GloVeの学習時間がiterationで表されるのに対し、Skip-gram、CBOWはepochである。しかし、残念ながら現状の(Skip-gram、CBOWの)コードがsingle epochしか学習しないように実装されているため、ひとまずnegative samplingの個数が訓練に使いデータ量に関連があるので、これと比較する。CBOWに至ってはnegative samplingの個数を増やすとかえって精度が悪くなった。これは、negative samplingが対象となる確率分布をうまく近似できていないからなのかも知れない。
NNablaでの実装
import nnabla as nn
import nnabla. functions as F
import nnabla. parametric_functions as PF
x_central = nn. Variable( ( batch_size, ) )
x_context = nn. Variable( ( batch_size, ) )
with nn. parameter_scope( 'central_embedding' ) :
central_embedding = PF. embed( x_central, vocab_size, embedding_size)
with nn. parameter_scope( 'context_embedding' ) :
context_embedding = PF. embed( x_context, vocab_size, embedding_size)
with nn. parameter_scope( 'central_bias' ) :
central_bias = PF. embed( x_central, vocab_size, 1 )
with nn. parameter_scope( 'context_bias' ) :
context_bias = PF. embed( x_context, vocab_size, 1 )
dot_product = F. reshape(
F. batch_matmul(
F. reshape( central_embedding, shape= ( batch_size, 1 , embedding_size) ) ,
F. reshape( context_embedding, shape= ( batch_size, embedding_size, 1 ) )
) ,
shape= ( batch_size, 1 )
)
prediction = dot_product + central_bias + context_bias
t = nn. Variable( ( batch_size, 1 ) )
zero = F. constant( 0 , shape= ( batch_size, 1 ) )
one = F. constant( 1 , shape= ( batch_size, 1 ) )
weight = F. clip_by_value( t / 100 , zero, one) ** 0.75
loss = F. sum ( weight * ( ( prediction - F. log( t+ 1 ) ) ** 2 ) )