前回、Encoder-Decoderモデルでは入力を固定長ベクトルに圧縮するため、長い入力で情報が失われるという話を書きました。Attention(注意機構)は、この問題を「入力の全ステップの情報を保持しておき、必要なときに参照する」という方法で解消します。
Encoderの全状態を保持する
従来のEncoder-DecoderではEncoderの最後の隠れ状態だけをDecoderに渡していましたが、Attentionを導入したモデルではEncoderの全ステップの隠れ状態を捨てずに保持します。
Decoderは出力を1トークン生成するたびに、Encoderの隠れ状態のリストを見て「今の自分にとってどの入力が重要か」をスコアとして計算します。
\[ a_{ts} = \frac{\exp(\text{score}(h_t, \bar{h}_s))}{\sum_{s'} \exp(\text{score}(h_t, \bar{h}_{s'}))} \]\( a_{ts} \) が注意の重みです。Decoderの現在の状態 \( h_t \) とEncoderの各状態 \( \bar{h}_s \) の相性をスコアリングし、ソフトマックスで正規化して確率分布にします。
重み付き和でコンテキストを作る
計算された重みを使って、Encoderの全状態の重み付き和を取ります。
\[ c_t = \sum_{s} a_{ts} \bar{h}_s \]これが新しいコンテキストベクトルです。固定長ベクトル1つにすべてを押し込むのではなく、生成する単語ごとに異なるブレンドを作る。入力系列のどの位置にも直接アクセスできるので、100単語前の情報でも参照できます。
イメージとしては、要約メモだけを頼りに話すのをやめて、原文を開いたまま、必要な箇所を指で差しながら訳すような感じです。
Query, Key, Valueという一般化
Attentionの仕組みをさらに一般化すると、Query(何を探しているか)、Key(各情報のインデックス)、Value(情報の中身)という3つの役割に分けられます。
先ほどの例だとDecoderの状態がQuery、Encoderの各状態がKeyとValueを兼ねています。この3つを明示的に分離することで、Attentionは翻訳に限らず「あるデータ群の中から、今の文脈に関連する情報を選んで集約する」汎用的な操作として使えるようになりました。
このQuery-Key-Valueの枠組みが、次回扱うTransformerの基盤になっています。
まとめ
Attentionは、出力を生成するたびに入力の全位置を参照して、文脈に応じた重み付き和を計算する仕組みです。固定長ベクトルのボトルネックが解消され、長い系列でも情報が失われにくくなりました。
Attentionで入力のどの位置にも直接アクセスできるなら、そもそもRNNで順番に処理する必要はあるのか。2017年のTransformerの論文はまさにこの問いを出発点にしています。次回はTransformerについて書きます。