たにしきんぐダム

プログラミングやったりゲームしてます

Coursera / Sequence Models (week2) 受講メモ (Natural Language Processing & Word Embeddings)

Deep Learning Specialization 5つ目のコース www.coursera.org

今回はみんな大好き word embedding


Coursera 以外の参考資料

one-hot encoding

  • 特徴
    • ある単語を機械学習モデルで扱うためにはそれをベクトル化する必要がある。そのためのシンプルな方法がone-hot encoding
    • one-hot encode された単語はボキャブラリーサイズ次元のベクトルとなり、その単語を表すindexの要素だけ1、それ以外は0となる
  • one-hot encoding の問題点
    • ベクトル間の演算で意味のある結果が得られない
    • 次元の数がボキャブラリサイズだけどんどん大きくなる

Word Embedding とは

  • one-hot encode した単語ベクトルと違い、単語を低次元実数値で表す
  • 似たような(似たコンテキストで出現する単語)単語ベクトル間の距離が近いなどの特徴があるので、単語ベクトル間の演算に意味がある
  • 得られる単語ベクトルの次元はボキャブラリ数とは関係なく好きに決められる
  • 最近のembedding手法では分布仮説に基づき、似たような文脈で現れる単語が似たようなベクトルを持つように計算される
    • 分布仮説 : 同じような文脈で出現する単語は似たような意味を持つだろうという仮説
    • 統計的意味論
      • 語の統計的な性質から語の意味を考える方法。だいたい語の出現する文脈を統計情報として利用する
    • 昔は違ったんか?

Word2Vec(skip-gram)

word2vecはword embedding手法のうち、"文脈"には周辺に出現する語を利用し、アーキテクチャにはNNを使って目的関数を最小にしていくことで最適なembeddingを得る手法。

CBOW (Continuous Bag-of-Words)と、skip-gram モデルが提案されているが、とりあえずよく使われている(???) skip-gram だけ勉強しておく。

skip-gram

word2vec では context として注目する語の周辺に出現する単語群をcontextとして利用する。例えば window_size = 2 とすると、注目している語の前後2単語をcontextとして利用することになる。

  • I want a glass of orange to go along with my cereal. について
    • orange に注目していて
    • window_size = 2 の場合
    • context は orange の周辺語である ['glass', 'of', 'to', 'go']

algorithm

word2vec で embedding を得るために、2層NNによる最適化問題を解き、最終的な隠れ層への重み行列(embedding matrix)の各列が各語のembedded vectorとなる。

  • 入力>隠れ層
    • $ e_t $: 注目している語のone-hot vector (vocab_size, 1) (入力ベクトル)
    • E: embedding matrix (embedding_dims, vocab_size) 行列で、各列が各単語のembedding vectorを表している
    • $ b_e $: 入力から隠れ層へのバイアス項 (embedding_dims, 1)
    • $ \theta $ sigmoid 函数 (何故か sigma が mathjax で描画されない)
    • $ h $ 隠れ層からの出力 (embedding_dims, 1)
  • 隠れ層>出力層
    • W: 重み行列 (vocab_size, embedding_dims)
    • $ b_w $: バイアス項 (vocab_size, 1)
    • $ \hat{y} $ 出力層からの出力 (vocab_size, 1)

このとき

$$ h = \theta (E e_t + b_e) $$

$$ \hat{y} = softmax (W h + b_w) $$

(インターネット眺めてたら出力層がcontextの数だけ複数あるような画像がよく見られ、その結果それぞれの出力層について異なる重みかパラメータか何かがあるのか???とずっと混乱してたけど隠れ層->出力層の重みは同じなので、入力に対して各contextに対する予測はすべて同じはずだよね????)

  • $ e_{t-1} $ は注目している単語の左隣りの単語の one-hot vector とすると
  • m: window_size

とすると、最終的なコスト関数は

f:id:tanishiking24:20201027170116p:plain

(全体で見ると、これを i ごとに vocab_size だけ足し合わせた値になる)。

コスト関数が定まったので後はこれを back prop で最適化していき、最終的に得られた embedding matrix E を使って、vocab 内の単語を低次元ベクトルに変換できるようになる。

Negative sampling

  • ここまでで紹介した素朴なword2vecでは、出力層でサラッとsoftmaxを利用している。
  • softmax では分母の計算のために vocab_size の数だけ exponential を計算して足し合わせる必要がある
  • vocab_size は一般的に非常に大きい (ボキャブラリの数だからね...)ため、計算量が非常に大きくなり、学習が遅くなる大きな原因となる。

negative sampling はある target word に対して、すべてのボキャブラリがcontextに出現する確率を(softmaxを使って)計算して、隠れ層から出力層への重み行列Wをまるごと更新する代わりに

正解サンプル(contextに含まれるword)とランダムにピックした少数の非正解サンプル(contextに含まれないword)に対する出力だけを使ってコスト関数を求め、Wの更新も一部の行に対してだけ少しずつ行っていくという方法。

先程の出力とコスト関数

$$ \hat{y} = softmax (W h + b_w) $$

f:id:tanishiking24:20201027170116p:plain
素朴なコスト関数

の代わりに、以下のコスト関数を利用する。

  • K ピックする非正解サンプルの数
    • k: k番目の非正解サンプル (k=1...K)
  • idx(k): k番目の非正解サンプルのindex (idx(k)=1...vocab_size)
  • $ W_i $: 重み行列Wのi行目の横ベクトル (1, embedding_size)

f:id:tanishiking24:20201027184617p:plain

これを微分して更新式を求めるが、コスト関数にはWのうちの一部の行しか含まれていないので、Wは一部の行が少しずつ更新されていく形になる。

ネガティブサンプリング時にピックする非正解wordは、コーパス中の単語の出現頻度で重み付けしたランダムピックを利用する。各wordの出現回数に 3/4 乗とかすることでワードごとのピック確率をスムージングしたりすることもあるらしい。

GloVe

  • word2vec は高速で結構良い感じの結果が得られる代わりに、ローカルな情報(ある文内の注目単語の周辺語彙)によってのみ重みの更新が決まっており、コーパス全体の語の共起情報などが利用できていない。
  • 一方 SVD などのような embedding 手法ではコーパス全体での語の共起情報が利用できるが、計算コストが重いという欠点がある

GloVe はそれら2つの良いとこ取りをするアルゴリズム(早くて、グローバルな共起情報を利用できる)、embedding のアイデアは基本的に word2vec と同じでありつつ、以下のようなコスト関数を利用する。

f:id:tanishiking24:20201028231435p:plain

  • $ X_{ij} $ は j番目の単語の"文脈で"単語iが出現する頻度、コーパス全体から計算される。"文脈" は例えば word2vec だと注目単語の前後window_size語に含まれている数
  • $ u_i $ と $ v_j $ はそれぞれi番目とj番目の単語のembedding vector(何でuとv異なるアルファベット使われるんだろね???)
  • $ b_i $ と $ b_j $ は最適化対象のバイアス。何でここにバイアス項が必要なのか?論文読んでもよく分からなかった、誰か教えてくれ〜〜〜
    • id:syou6162 さんに気持ちを教えていただきました。どう書くか悩んだけどそのまま引用させていただくことに
    • バイアス項はなくてもよくて、1のような定数項でもよいですが、GloVeに限らず基本的に入っていることが多いし、性能が高いことも多いです。

    • GloVeは文脈givenの単語の生起確率を考えるいわゆる「確率的言語モデル」と言われるやつの一種です。バイアス項は文脈によらない値なので、「どんな単語でもこれくらいは生起しうる」という意味があると考えていいと思います。一昔前の言語モデルでいうとadditive smoothingとやっていることは気持ちとしては同じです。

    • GloVeに限らず、二値分類のようなものを考えるとより分かりやすいかも、クラスA or Bに分類する問題で、Aが99.9%、Bが0.1%の発生確率というようなクラス分布がめっちゃ片よった問題の場合、入力が何であるかに関わらず、基本的にクラスAと答えたほうが精度は高くなります。そういった「入力に依らないクラスの事前確率」のようなものをエンコードしたものがバイアスと考えるとよいです

    • GloVeも語彙数V分の多値分類問題を解いているようなもの、と考えると二値分類の一般化でバイアス項の存在も捉えることができるかな

    • feed forward NN のコスト関数と違って、コスト関数の中にバイアス項がそのままでてくるので面食らってしまったけど、GloVe の u_iT v_j - log X_ij とかはそれ自体が共起する確率なので、そこにバイアス項が出てくるのは他のモデルでバイアス項いれて「どんな単語でもこれくらいは生起しうる」みたいなのを表現しているのと基本的には変わらないんですね。
  • $ |V| $ は語彙数
  • f は以下のような関数で negative sampling と同じようにスムージングを行いつつ、高頻度で出現する単語(the とかみたいな stop words など)を $ x_{max} $ でキャップする。

f:id:tanishiking24:20201028232122p:plain

$ X_{ij} $ に log がかかってる理由なんかは論文を読むとそれっぽいことが書かれてる

Debiasing word embedding

arxiv.org

word2vec などは周辺語彙からある単語のベクトルを計算する。そのため、学習データそのものに例えば gender bias が入り込んでいると、学習した単語ベクトルにも gender bias が含まれてしまう。

どういうことかというと、例えば

  • computer programmer から man - woman なる単語ベクトルで引くと homemaker が一番近い語となったり
    • (本来 computer programmer に gender bias は含まれていないはずなので、computer programmer から男女の情報を反転させても computer programmer 担ってほしいよね)
  • doctor という語も本来男女差はあるべきではないが、woman より man のほうが近くなったりする

このような本来あるべきでないbiasを含んだembeddingを利用して、文書分類器を作るとbiasを含んだモデルができるし、文章生成器を作るとbiasのある文章が生成されてしまう。

このような問題を軽減するために、生成された embedding vector から意図しない bias を排除するアルゴリズム

やるべきことは3ステップ

Identify bias direction

  • 例えば語の gender bias を he - she や male - female などの対となる語を(手動で)集めて
  • それらの平均を取る (論文では集めた語に対してPCAで bias direction を計算している)
  • man - woman など単一の対を利用すると、例えば man は oh man! みたいな genderとは無関係な文脈で利用されることもあるので、計算が安定しない

Neurtralize

  • すべての definitional でない語について bias direction に対して射影
  • definitinal とは、例えば gender bias なら motherboy など、それ自体に gender 的な意味が込められているもの

以下のように変数を定めると

  • g: 前のステップでidentifyした bias direction
  • e: neutralize しようとしている単語のembeddingベクトル

まずeに含まれるbias directionの成分を抽出

$$ e_{bias-component} = \frac{e \cdot g}{|| g|| ^2 } * g $$

元のベクトルからbias directionを差し引く

$$ e' = e - e_{bias-component} $$

Equalize pairs

  • gender bias の含まれる語を neutralize しただけでは一歩足りない
  • neutralize は射影した方向のベクトルを引いているだけ
    • そのため、neutralizeしたgrandmotherとgrandfatherでは、grandmotherのほうがbabysittingに近いなどのgender biasがまだ含まれうる。
    • bias direction について単語ペアが対称になっているのが理想
  • これを実現するためにequalizingではペアの単語ベクトルをそれぞれ対象になるようにずらしてやる

以下のように定式化する

  • $ e_1, e_2 $: 単語1, 2の neutralize前のembeddingベクトル
  • $ g $: bias direction
  • $ e'_1 $ 単語1 の neutralize後のembeddingベクトル
  • $ e'_2 $ 単語2 の neutralize後のembeddingベクトル

まずは 単語1と単語2のベクトルの平均をとって、そのベクトルをneutralizeする

$$ \mu = \frac{e_1 + e_2}{2} $$

$$ \mu_b = \frac{\mu \cdot g}{|| g|| ^2 } * g $$

$$ \mu_{\perp} = \mu - \mu_b $$

これを使ってそれぞれのneutralized vectorが正反対の方向を向くように調整

f:id:tanishiking24:20201029022532p:plain
e'_1 の correction

e2 についても同じように計算し、最終的に以下の値がequalizeした単語ベクトル対となる

f:id:tanishiking24:20201029023012p:plain
最終的なe1

f:id:tanishiking24:20201029023030p:plain
最終的なe2

Application

padding

  • 同一バッチ内の文章はそれぞれ同じ長さのベクトル列になってないといけない。
  • しかし入力文章は必ずしも都合よく同じ長さにはなっていない。
  • 長過ぎる文章はtruncateしたり、ある固定長を定めてそれに足りない文章はpaddingを行う
    • paddingは0埋め

www.tensorflow.org