「ゼロから作るDeep Learning ❷ ―自然言語処理編」のまとめ 前半
なんとなく自然言語処理に興味を持ったので読んでみた。
ゼロから作るDeep Learning ❷ ―自然言語処理編
BERTが理解できるようになりたさ。
この本の構成は、スタンフォードのCS224d: Deep Learning for Natural Language Processingを基にしているそう。
1章 ニューラルネットワークの復習
そのまま。NNの基本的なことについての復習。Softmaxとかクロスエントロピーとかアフィン変換とかBackpropとか。前巻読んだ人はスルーで大丈夫。
ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装
2章 自然言語と単語の分散表現
自然言語処理は、プログラミング言語とは違い、「柔らかい」言語を処理する。
コンピュータに単語の意味を理解させるためには
・カウントベース
・推論ベース(word2vec)
による手法がある。
まずシソーラス(thesaurus)とは、人手によって単語の意味を定義した辞書のこと。
辞書と言っても、一般的な広辞苑みたいなのではなく、同じ意味の単語がまとまった「類語辞書」らしい。
こんな感じ
WordNetという1985年に発表されたシソーラスがあり、それを使うと単語の意味やネットワークが求められる。付録に使用方法も記載されている。
しかし、シソーラスは、
・時代の変化に対応するのが難しい
・人の作業コストが高い
・単語の細かなニュアンスが表現できない
など問題が多い。
そこで、カウントベースの手法が出てくる。
これはコーパス(corpus)を用いて、単語の分散表現を獲得する手法。
分散表現とは、単語の意味をベクトルで表したもの。
それに当たって、分布仮説というのを仮定する。
分布仮説とは、「単語の意味は、周囲の単語によって形成される」というもの。
周囲の単語を「コンテキスト」、どれだけ周囲の単語を見るかの幅を「ウィンドウサイズ」という
"You say goodbye and I say hello"
という文(ビートルズの曲が流れてきそうw)のgoodbyeを見ると、ウインドウサイズを1にしたとき、say, andがコンテキスト。
sayだと2回出てきているので、You, goodbye, I, hello がコンテキスト。
ここから共起行列を作る。
これは単語ごとに、共起するかを0,1で表したものを行列にしたもの。
これで単語のベクトル化ができる。
このベクトルをコサイン類似度(ベクトルを内積して正規化する)を用いて、類似度を測れる。
これだけだと、例えばthe と car という単語の類似度が高くなってしまうが、それは直感に合わない。
そこで相互情報量(Pointwise Mutual Information)を使う。
これを使うと、文章中にその単語が出てくる頻度を考慮に入れて、単語同士の関連性が見られる。
共起する回数をC(・)で表すと
で求められる。
扱うときは正にしたいので、
PPMI(x,y)=max( 0, PMI(x,y) )
にする。
このPPMI行列のままだと疎な行列なので、次元削減をすることが多い。
特異値分解(Singular Value Decomposition:SVD)を用いるとUに単語の意味を表わせる。Dの特異値が小さいものは重要度が低いので、Uの先頭数列を取り出すと、次元削減した表現として利用できる。
これで密なベクトルになる。
以上がカウントベースの手法。
3章 word2vec
カウントベースの手法だと、SVDの計算にO(n^3)かかるので、巨大なデータを扱うのに現実的でない。
そこで推論ベースでは、データの一部を使って逐次的に学習していく。
ここでword2vecが出てくる。
例えば
"You ? goodbye and I say hello"
の?をコンテキストから推論できるような学習をする。
この?をターゲットという。
つまり、コンテキストを入力してターゲットの?を推論する隠れ層1層のNNを作る。
その学習結果として、NNの重み行列が単語の意味を表現している、という寸法。
入力には単語のone-hotベクトル([1,0,0, ... 0,0])を用いる。
重みは入力のときと出力のときのものがあるが、入力側のほうが単語の意味が良く表現されているらしい。
word2vecにはCBOWモデルとskip-gramモデルがある。
CBOWは上記のような推論をする。
skip-gramは
"? say ? and I say hello"
のように、sayから2つの?を推論する。
skip-gramの方が難易度が高い処理をしているためか、こちらの方が精度が高いらしい。
カウントベースと推論ベースのどちらが良いか、は一概に言えない。
単語の分散表現を更新したい場合は、カウントベースだとSVDを一から計算しないといけないので面倒。推論ベースなら、それまでの重みを利用して再学習ができるので楽。
カウントベースだと単語の類似性が表現できる。
推論ベースだと、単語の類似性の上、「king - man +woman = queen」などのような類推問題も解ける。
skip-gramとNegative Samplingモデルは、共起行列にある特殊な行列分解をしたものと同じらしく、関連性が有る。
また、2つを融合させたGloVeという手法もある。
4章 word2vecの高速化
単語のone-hotベクトルを中間表現にする際に行列積を行うが、実際は重み行列の行成分を取り出しているだけ。
この計算はもったいないので、Embeddingレイヤを作り、置き換えることで計算量を減らせる。
このレイヤの逆伝播は、前の勾配をそのまま伝える(行が重複する場合は加算する)。
次に計算が重いのは
・出力の重みをかけるとき
・Softmax層
ここでNegative samplingという手法を用いる。
これは、多クラス分類を二値分類で近似できないか?(できる)という手法。
上記のCBOWの場合だと、「youとgoodbyeからsayを正答できるかどうか?」を二値で扱う。
これでは負例も二値分類するから結局計算量は変わらないのでは?
となるが、負例に対しては、corpus中の出現回数に応じた確率分布を用いて5個とか10個をサンプリングする。
これがNegative samplingということ。
こうすることで、出力層のニューロンは1つになり、計算量が減る。
このニューロンは、Embeddingで抜き出した行と、重み行列の列の内積となる。
これにSigmoidとクロスエントロピー誤差を使ってLossが求まる。
結果、得られた単語の分散表現では、ベクトルの加算で類推問題が解ける。
また、分散表現が有用なのは、Wikipediaなどで学習したものを使った、転移学習がしやすい、という面がある。
キリがいい(次からRNN)のでいったんここまで。
ここまで読んで、まぁSVDが使えたり、word2vecの仕組み頭いいなーとか思ったりとかもあるんですが、一番気になったのが、分布仮説の「単語の意味は、周囲の単語によって形成される」ってこと(え)。
よく言えないけど、再帰的な感じがして気になる。
某SF作家の博士論文が「 自己参照関数方程式 : 自然言語の理解へ向けて 」ってタイトルなのは知ってたんですけど、そこらへん少し関係してるのかな、と思ったり(s○i-hub使ってもwebで読めない)。
あとは、単語を分散表現で表すのって、ソシュールの「差異の体系」が基になってそうですよね、たぶん(読んだこと無いけど)。
このあたり詳しい人がいたら、ぜひ本とか教えてください。