私がTransformerとBERTを理解するまで

この記事は、くふうカンパニー Advent Calendar 2018 - Qiita の19日目の記事です。

今回は界隈を騒がせたアレを理解するためのブログを書こうと思います。

そう、BERTです。


BERT?

(Bidirectional Encoder Representations from Transformer)
BERTは、言語表現を事前学習する手法のひとつ。
これまでもword2vecや、ELMo、OpenAI GPT などがありました。
今回のBERTがなぜここまで話題になっているかというと、先のツイートでも触れられているように、一つの言語モデルで8つのタスクでSOTA祭りというのが大きいです。

一度事前学習させたモデルを用意しておけば、それを利用して様々なタスクに利用可能という、私が2年前くらいに夢に見た汎用言語表現がほぼ実現したと言えます。

How?

ではどのように事前学習することで、様々なタスクで高い精度を実現する表現を獲得したのでしょうか。

言語モデルの変遷

そもそも言語モデルとは、言語の意味的な情報をモデル化したものです。
私たちはその言語をキーとして必要な情報を脳から引き出し、文章を理解します。
そのため、言語は単体では離散的な情報であり、コンピュータにとってはトークンの集合体としてしか認識することができません。その点が画像のような連続的な情報の認識と異なり、言語理解を困難にしている要因といえます。

古典的な言語モデルにbag of wordsがあります。
これは文章中の単語の出現頻度で文章を表現する手法です。
また、n gram言語モデルもこれまでスタンダードな手法でした。
これはn個前の単語の次に現在の単語が出現する確率で表現します。

しかしこれらのモデルは表現空間が制限されているため表現力が弱く、タスクの精度も頭打ちでした。そこで登場したのが分散表現によるモデルです。


ニューラル言語モデル


分散表現では組み合わせで特徴を表現します。
word2vecを筆頭に、ニューラルネットを利用して分散表現を獲得するニューラル言語モデルが登場しました。
そこから単語の前後関係を考慮して文章ベクトルを生成するため、LSTMやGRUなどのRNNを利用したencoder-decoder modelが登場しました。


RNNからAttentionへ


それから割と長い間?自然言語機械学習といえばRNN必須だろ、というながれでしたが、ここで「え、再帰的なブロックとか使わなくてもAttentionだけでSOTAできるぜ?」という論文「Attention is All You Need」でTransformerというモデルがが提案されます。
これによってAttentionすげーやん、という流れができ種々のモデルが提案され今に至るという感じ(だと思って)います。

構造

BERTは、ざっくり言うと双方向のTransformerです。(Transformerについても後で触れます)

f:id:YasuKe:20181220174043p:plain
BERT Architecture
構造自体は非常にシンプルです。

しかしこれだけでは何がどうなっているのか理解したことにはならないので、次にTransformerの構造を見ていきます。というかむしろこちらを理解することの方が大変。

Transformer

f:id:YasuKe:20181220175146p:plain
Transformer Architecture

たくさんブロックがでてきていますが、逆に言えばこれらを理解すれば大方理解可能ということですね。
これを一つずつ解説していくのか…、と一瞬絶望しかけましたが、BERTのtrmブロックはmulti head attentionらしいということに気づきました。
ということで、ここではmulti head attention の説明にとどめます。

Attention

そもそもattentionとは、入力の特徴量のなかで着目すべき部分とそれに対応する知識を同時に学習しようというものでした。
私たちが言語を理解するとき、その入力クエリ(例えば読んだり聞いたりした文章)の一語一句全てをちゃんと理解している訳ではなく、ある特定の単語からそれに関する情報を想起し、関係を把握し、解釈することになります。attentionではそうした構造をモデル化して(いると私は解釈している)います。
端的に言うとattentionは、入力ベクトルによって重みを計算し、その重みに基づいてメモリから任意のベクトルを出力するモデルです。
ここで登場する変数は3つ。


入力クエリ Q
キー K
メモリ V


そしてattentionは次式のように表される。


{ \displaystyle
Attention(Q, K, V) = softmax(Q K^T)V
}

(softmaxはベクトルを正規化する関数である。大小関係を保ったまま全ての次元の総和が1になるようなベクトルにしてくれる。)
QK^T の部分でクエリとキーの類似度を計算し、そのクエリに近いVを持つ次元の値を求めると言うことですね。それで求めた値を正規化して重みとし、Vと積をとることで任意の情報を引き出すような操作になるというわけです。クエリに関連する情報に注目する、まさにattentionというわけですね。


この重みとなるクエリとキーの類似度の計算方法は主に2種類あり、先に説明した内積で求めるものをDot-Product Attention(https://arxiv.org/abs/1508.04025)、Feed Forward Networkで求めるものをAdditive Attention(https://arxiv.org/abs/1409.0473)と呼びます。
TransformerやBERTではDot-Product Attentionのほうを使っています。
また、Transformerでは単純なDot-Product AttentionではなくScaled Dot-Product Attentionというものを利用しています。

Scaled Dot-Product Attention


f:id:YasuKe:20181223045016p:plain
こちらはDot-Product Attentionの内積の後にスケールを調整する処理が入ったもので、次式のように表されます。

{ \displaystyle
Attention(Q, K, V) = softmax(\frac{Q K^T}{\sqrt{d_k}})V
}
通常のDot-Product Attentionでは次元数dが大きい場合、内積が巨大になり、重みが効果的に計算できないという問題がありました。それを解消するためにスケーリング因子\frac{1}{\sqrt{d_k}}を導入したということのようです。

Multi-Head Attention


f:id:YasuKe:20181223051251p:plain

Multi-Head Attentionでは、Scaled Dot-Product AttentionをひとつのHeadと見做して各Headを並列で計算し、concatして最後に一層線形変換を通すものです。


 \displaystyle 
\begin{eqnarray}
MultiHead(Q, K, V)&=&Concat(head_1,...,head_h)W^o\\\\
where \quad head_i&=&Attention(QW_i^Q, KW_i^K, VW_i^V)
\end{eqnarray}

ここで、入力時の線形変換の各隠れ層W^Q, W^K, W^Vd_{model} \times \frac{d_{model}}{h}の行列です。d_{model}はモデルの出力ベクトルの次元数。つまり、Single Scaled Dot-Product Attention とパラメータ数はほぼ同じということになります。パラメータ数が同じでも分割して学習した方が効率が良いらしいと主張してます。

また、最後の線形変換の隠れ層はW^o \in \mathcal{R}^{d_{model} \times d_{model}}です。

BERTの入力の表現ベクトル

BERTでは、入力の表現ベクトルは単純な埋め込み行列だけではありません。
以下に説明する3つのベクトルの和を入力としています。


f:id:YasuKe:20181223182646p:plain

Position Embeddings

Transformer や BERTではAttentionを用いるため、それだけでは単語の位置に関する情報を考慮できません。そのため、Attentionに入力する前にPosition Embeddingsと呼ばれている操作を挟みます。

イメージはわかったが、具体的な説明が見当たらない…。

Segment Embeddings

BERTでは、事前学習時の入力に複数のセンテンスを用いる場合がある。それぞれのセンテンスの間には特殊なトークン[CLS]を挟んで入力するが、モデルが複数のセンテンスを識別できるようにSegment Emgeddingsと呼ばれるものを利用する。具体的には、センテンスごとに別の埋め込み行列を利用するというもので、一つしかセンテンスがない場合は最初の埋め込み行列のみを利用する。

Token Embeddings

こちらはいつもの埋め込み行列によるベクトルです。
説明は割愛します。

BERT構造 Again

ここでもう一度BERTの構造を見てみます。


f:id:YasuKe:20181225184834p:plain
この図ではtransformerブロックが単語数x2個あるように見えますが実際はこの図で言えば2層のtransformer層を持つモデルとなります。BERTの実験では12層のモデルと24層のモデルを利用しています。

そしてBERTのtransformerブロックはすべてencoderらしいのですが、どうやってencoderだけからsequenceな出力を得ているのかまだ理解できてません…。

事前学習

BERTの特徴は双方向のTransformerであるという点にあります。OpenAI GPTは単方向のTransformerでした。双方向である方が直感的に良さそうに見えますが、なぜOpenAI GPTでは単方向だったのでしょうか。これはOpenAI GPTの事前学習に、次の単語を予測するタスクを利用したことに起因します。BERTのように双方向のTransformerを用いてしまうと、最初から文章全体を入力するため、そのタスクの答え(次の単語)の情報も入力に含まれてしまい(カンニングになってしまうため)、うまく学習ません。そこでBERTでは、事前学習に用いるタスクをくふうすることで双方向のTransformerを実現しました。

BERTで利用する事前学習タスクは2つあります。

Masked LM

こちらは穴埋め問題です。英語の授業でよく、マスクされている箇所の単語を埋めて文章を完成させるような問題があったと思いますが、それをそのままモデルにやらせるイメージです。人間と同じように勉強をしている感じが興味深いですね

具体的には、入力する文章の単語の一部を特殊なトークン[MASK]に置き換えて、その置換された部分の単語のみを予測します。文章全体を予測するわけではないため学習に時間がかかるらしい。

また、fine tuning 時にま[MASK]トークンが一切出現しないというギャップを埋めるため、学習データの10%はそのままの文章を入力、もう10%はマスクする箇所を別の単語に置換したものを入力するようにしているようです。加えて1つの系列でマスクするのは単語の15%にしています。

Next Sentence Prediction

これは2つの系列を入力にとり、それらが連続する系列かどうかを予測するタスクです。(ここでSegment Emgeddingsが活きる)
このタスクでは文章間の関係を学習します。ここで得られた特徴がQuestion Answeringなどのタスクに活かされるわけですね。

まとめ

今回はGoogleの言語表現モデルであるBERTとそれに利用されているTransformerの構造をみてきました。
その中で、個人的にまだ理解できていない点が明らかにできました。
(あと本当は何か動かしたかった。)

疑問点
  • Position Encodeingsの詳細
  • 系列の出力をどう生成しているのか
まだ説明できていない点
  • Scaled Dot-Product AttentionのMask層
  • Transformerのresidualとnorm層
  • Source Target AttentionとSelf Attention

これらを明らかにできたらちょくちょく編集していこうと思います。

くふうカンパニー Advent Calendar 2018、お次はkazumalabのFlutterを使ってみる(WIP) - KazumaLab.です。