ゼロつく2のAttentionからTransformerへ

#1.はじめに
　**ゼロから作るDeep Learning2 自然言語処理編**を勉強しました。8章に **Attention** があるのですが、現在主流である **BERT** の基本になっている **Transformer** に割かれているのは僅か4ページでした。

　実はこれは無理からぬことで、ゼロつく2が発刊されたのは2018年7月で、Transformerが2017年12月、BERTが2018年10月というタイミングだったためです。

　今回は、**ゼロつく2のAttention**から、現在の自然言語処理の基本とも言える **Transformer** の論文 **Attention Is All You Need** へと迫ってみたいと思います。

#2.ゼロつく2のAttentionとは？
![スクリーンショット 2020-09-04 19.00.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/73cbc95b-d8e2-3748-a244-b879b9fb13e9.png)
　ゼロつく2の8章Attentionでは、**Seq2Seqの翻訳モデル**の性能を上げるために、**Decoderで「I」を翻訳する時にEncoderの「吾輩」に注意を向ける**ような**Attention**を実装します。

![スクリーンショット 2020-09-05 11.59.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/10f8f28c-f6da-dbf4-cf53-309b76b96662.png)

　まず、Attention Weightで、Decoderの「I」のベクトルとEncoderの各単語ベクトルの類似度を求めるために**内積**を計算し、その結果に**Softmax**を掛けて**重みa**を求めます。但し、分かり易さを優先し、内積は**掛け算＋H軸SUM**で代替計算をしています。

　そして、Attention Sumで、重みaからEncoderの各単語ベクトルの**重み付き和**を計算し、**Context Vector** を求めます。この Context Vector は「吾輩」の単語ベクトルを色濃く反映したものになっていて、「I」が注意を向けるべきなのは「吾輩」であることを示しています。

#3.ゼロつく2からTransformerへ
　ゼロつく2の処理は分かり易さを優先し、わざと**内積**を使わず**掛け算＋軸SUM**で計算しています。では、ちゃんと内積を使って計算するとどうなるかと言うと、

![スクリーンショット 2020-09-04 19.28.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/f8f50a88-1288-d2ec-7819-508b332dc2f9.png)

　重みの計算、重み付け和の計算とも、内積の形にすると、こんな風にスッキリします。こちらの方が合理的ですよね。

　さて、一般的にAttentionでは、計算するターゲットを**Query**、類似度の計算に使う単語ベクトルの集まりを**Key**、重み付け和計算に使うベクトルの集まりを**Value**と言います。これらを反映して図を見直すと、

![スクリーンショット 2020-09-04 19.33.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/58aea5f9-602c-4c18-33da-13487bbba20b.png)

　計算するターゲット**「I』はQuery**, 類似度の計算に使う単語ベクトルの集まり**hsはKey**, 重み付け和計算に使うベクトルの集まり**hsはValue** となるわけです。ゼロつく2では、keyとValueは同じですが、この2つを独立させることでより表現力が向上します。

　※　Attentionの解説には、KeyとValueをまとめてMemoryと言い、Keyで指定されたValueを引き出す辞書のようなものだと説明しているケースもありますが、これは1つの例えです(実際にdictが使われているわけではありません)。

　もう１つ、一般的にAttentionには、入力が何処から来るかによって区分があります。

![スクリーンショット 2020-09-04 19.51.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/7f04542b-5d19-b76c-7951-737ecf4b1c3b.png)
　 QureryとKey・Valueが別の場所から来る場合を **Sorce Taget Attention** と言い、Quety,Key,Valueの3つが全て同じ場所 (Self) から来る場合を **Self Attention**と言います。ゼロつくのAttentionはSorce Taget Attentionということになります。

　さて、書き方を少し変え、**Self Attention** の形式にして項を1つ追加します。

![スクリーンショット 2020-09-04 19.54.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/68cb794b-92b2-9c4a-55e6-b4696545efd7.png)

　追加したのは**ルートdkで割る**項です。なぜルートdkで割るかと言うと、内積計算で大き過ぎる値があると**Softmax**を掛けたとき、それ以外の値が0になり**勾配消失**してしまうため、それを防止するためです。dkは単語分散表現の次元数で論文では512です。

　さて、Transformer は逐次処理が必要なRNNは一切使わず並列処理が可能なAttentionのみで構成されています。従って、Queryを1つづつ計算する必要はなく、**全てのQueryに対して一気に計算が出来る**ので下記の様に表せます。

　![スクリーンショット 2020-09-04 23.07.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/ebf59620-7432-2f75-6714-4e37375c7af9.png)

　Attention Is All You Need の論文では、これを **Scaled dot-product Attention** と呼んでいて、下記の式で表現されています。これが Transformer の肝です。

![スクリーンショット 2020-09-05 12.06.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/a5e1185c-a447-b0fe-6d03-cade1f86b4ed.png)

#4.Attention Is All You Need
　さて、2017年12月に発表された論文 **Attention Is All You Need** をザックリ理解するために、論文に出て来る図の説明をします。
![スクリーンショット 2020-09-05 12.13.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/144a439c-1381-3b41-8318-3259a5015ded.png)

　まず、Scaled Dot-product Attention の図です。**Matmul** は内積、**Scale** はルートdkで割ることを表しています。**Mask(opt)**は、入力する単語数がシーケンス長より短いときにpad処理した箇所にをマスクを掛けるという意味です。Attention(Q,K,V)の式と符号することが分かると思います。

![スクリーンショット 2020-09-05 12.17.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/be3a1f18-ad3d-2f57-0776-841fa3a5d2a4.png)

　先程のScaled Dot-product Attention を含んだ、**Multi-Head Attention** です。前段の1つの出力が3つの**Linear(全結合層)**を経てQ,K,Vとして入力されます。言い換えれば、Q,K,Vは前段の出力にそれぞれ重みWq,Wk,Wvを掛けたもので、これらの重みは学習によって獲得されます。

　1組のQuery,Key,Valueを**ヘッド**(Head)といいます。大きなヘッドを1つ持つより、小さいヘッドを複数個持ち、それぞれで潜在表現を計算し最後にConcatする方がパフォーマンスが上がります。そのためにMulti-Headにしているわけです。論文では、前段の出力が512次元で、それを64次元のヘッドで8つに分割しています。

![スクリーンショット 2020-09-05 12.29.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/ba25d1ef-d419-758f-594c-6be7543d6d24.png)


　全体構成は、**Encoder-Decoderの翻訳モデル**です。狙いは、逐次計算のRNNはやめて並列計算ができるAttentionのみで構成し処理を高速化すること。Nxは同じ構成がN回繰り返されるという意味で、論文ではN=6です。

**① Input Embedding**
　Inputsは(バッチ数, 単語ID列数)。事前訓練済みの単語分散表現を使ってベクトルに変換したものを出力(バッチ数, 単語ID列数, 次元数)します。論文の次元数は512です。

**② Positional Encoding**
　重み和を計算すると**単語の順番情報が失われる**(「私は彼女を好き」と「彼女は私を好き」の区別がつかない) ので、分散表現に単語の位置情報(sin関数とcos関数のパターン)を加算し、**相対的な単語位置を学習し易く**しています。つまり、同じ単語でも違う場所にあれば異なるベクトルになります。論文に載っている位置情報を表す式は、以下の様です。
![スクリーンショット 2020-09-05 12.39.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/ce7bfff5-6f97-79e9-73ad-5c980cd9493a.png)

**③ Add & Norm**
　**Skipコネクション**で、**LayerNormalization**による正規化, Dropoutによる正則化を行います。LayerNormalizationはバッチ単位ではなくバッチ内の各データ単位（センテンス)で正規化を行います。

　
**④ Feed Foward**
　Attention層からの出力を2層の全結合層で特徴量を変換します。

**⑤ Masked Multi-Head Attention**
![スクリーンショット 2020-09-05 11.48.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/a81182da-a2cc-e095-b826-13697b91abf0.png)
　「I」のAtentionを計算するとき、「am」,「a」,「cat」を対象に入れると予測すべき単語をカンニングすることになるので、Keyにある先の単語を見えなくするためにマスクを掛けます。

#5.Transformer その先へ
　Transformer は翻訳モデルとして提案されましたが、その後研究が進むと **Self Attentionによる文の意味抽出力**が相当強力であることが分かって来て、様々な展開がされていますので、簡単に触れておきます。
![スクリーンショット 2020-09-04 23.23.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/2a72df13-3d1a-eb2f-8de4-68576aef2862.png)

　2018年6月。TransformerのEncoder側のみを使い、**事前学習＋ファインチューニング**の2段階学習で性能が向上しました。事前学習は、**次に来る単語は何かを当てる**というもので、未来の情報が分かるとまずいので、後ろの文にはマスクをかけています。

　構造上、後ろの文脈が利用できないという欠点があり、後ろの文脈を活用できればさらなる性能の向上が図れることは周知の事実でした。

![スクリーンショット 2020-09-04 23.24.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/bdbd52f8-1971-7718-ee32-fa2e2760590d.png)

　2018年2月。なんとかして後ろの文脈も利用するために、Attentionによる並列処理のメリットは一旦捨てて、多層化した双方向LSTMを使って後ろの文脈も利用してみました。そうすることで、性能が向上することが分かりました。

![スクリーンショット 2020-09-04 23.25.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/937ee94d-e76a-8a2d-4bf0-ddfaf1e0b140.png)

　2018年10月。自然言語処理における革命的な手法**BERT**が発表されました。ザックリ言うと、GPTの事前学習を下記の2つのタスクに置き換えることで、前後の文脈を利用し、かつ並列処理を実現しました。いわば双方向Transformerの誕生です。

　**①Masked Language Model**：穴埋め問題を解かせる。15％の単語を選び80％をマスク、10％を他の単語に置き換え、10％をそのままにする。
　**②Next Sentence Prediction**：2つの文の文脈が繋がっているかを判定する。