なぜAttentionはEncoder-Decoderと切り離せないのか — 翻訳モデルの進化で根本から理解する

「Attention」「Encoder-Decoder」「Transformer」「自己注意」「交差注意」——Transformer を学び始めると、これらの言葉が次々に出てきて、どれがどれの一部なのか、なぜ一緒に語られるのか、頭の中でこんがらがってきます。特に「なぜ Attention の話をすると必ず Encoder-Decoder が出てくるのか?」という疑問は、多くの人がつまずくポイントです。

結論を先に言ってしまいます。Attention は、もともと Encoder-Decoder という仕組みの弱点を治すために発明されました。 両者は歴史的にも構造的にも切り離せない関係にあるのです。この「生まれの物語」を知ると、バラバラだった用語が一本の線でつながります。

この記事では、機械翻訳モデルの進化をたどりながら、次の流れを根本から解きほぐします。

  • なぜ入力を「読む係」と「書く係」に分けた(Encoder-Decoder)のか
  • その仕組みが抱えたボトルネック問題とは何か
  • それを解くために生まれた Attention の発想
  • Attention を Query・Key・Value という共通言語で書くとどうなるか
  • Transformer で「自己注意」「交差注意」「マスク付き注意」がどこに置かれているか
  • BERT・GPT・翻訳モデルが、実は同じ Attention の配置違いにすぎないこと

この理解は、Transformer 系のあらゆるモデル(翻訳・要約・対話・画像生成)を読み解く土台になります。まずは全体像を1枚の絵で押さえましょう。

エンコーダ・デコーダの全体像(翻訳)

英語 “I have a dog” を日本語「私は犬を飼う」に訳す場面です。入力を理解するエンコーダ(読む係)と、出力を作るデコーダ(書く係)に分かれ、その間を「意味ベクトル」が橋渡ししています。この素朴な設計にどんな問題が潜んでいたのか。そこから話を始めます。

前提知識

この記事を読む前に、以下に目を通しておくと理解が深まります。

なぜ入力を「読む係」と「書く係」に分けるのか

まず、Encoder-Decoder という発想がなぜ必要だったかを押さえます。ここが物語の出発点です。

機械翻訳を考えてみましょう。入力は “I have a dog”(4単語)、出力は「私は犬を飼う」(5単語)。入力と出力で長さが違ううえに、単語の対応順もずれます。英語は主語・動詞・目的語の順ですが、日本語は主語・目的語・動詞の順です。単純に1単語ずつ変換していく方式では、この「長さも順序も変わる変換」に対応できません。

そこで考え出されたのが、処理を2段階に分ける方法です。

  1. エンコーダ:入力文を最後まで読んで、その意味を1つのベクトルに凝縮する
  2. デコーダ:その意味ベクトルを元に、出力文を1単語ずつ生成する

人間の翻訳者に例えると、「まず原文を最後まで読んで内容を頭に入れ(エンコード)、それから訳文を書き始める(デコード)」という自然な手順です。読む作業と書く作業を分業させたわけですね。この分業のおかげで、入力と出力の長さが違っても、順序が入れ替わっても対応できるようになりました。

エンコーダとデコーダには、当時(2014年頃)は RNN(あるいは LSTM)が使われていました。RNN は単語を1つずつ順番に読み込み、内部状態を更新していきます。入力文を最後まで読み終えたときの内部状態——これが「意味ベクトル(コンテキストベクトル)」として、デコーダに引き渡されます。

一見よくできた仕組みです。しかしここに、見過ごせない欠陥が潜んでいました。次のセクションで、その正体を暴きます。

ボトルネック問題 — すべてを1本のベクトルに詰め込む無理

Encoder-Decoder の泣きどころは、エンコーダが入力文の全情報を「たった1本の固定長ベクトル」に詰め込まねばならない点にあります。

固定ベクトルに全部を詰め込むボトルネック

“The quick brown fox jumps over the lazy dog” のような長い文でも、”Hi” のような短い文でも、意味ベクトルの長さ(次元数)は同じ、たとえば512次元と決まっています。短い文なら余裕を持って表現できますが、長い文になると、512個の数字に収めきれない情報が溢れ出てしまいます。細い管に大量の水を流し込もうとするようなもの——これがボトルネック(瓶の首)問題と呼ばれる理由です。

さらに RNN には忘却の問題もあります。単語を1つずつ順に読むため、文の後半を処理する頃には、前半で読んだ内容が薄れてしまう。長い文の冒頭の情報が、意味ベクトルにほとんど残らないのです。

この2つが重なると、どうなるか。文が長くなるほど翻訳品質が急激に悪化する、という形で症状が現れます。

文が長いほど固定ベクトルの品質が落ちる

赤い線が「固定ベクトルのみ(Attention なし)」の翻訳品質を模式的に描いたものです。文が20単語を超えたあたりから品質がガクッと落ちています。緑の線(後で説明する Attention あり)が長文でも品質を保っているのと対照的です。実際、2014〜2015年の研究で、この「長文での性能崩壊」が Encoder-Decoder の深刻な限界として報告されました。

問題の本質はこうです。デコーダが参照できるのは、圧縮された1本の意味ベクトルだけ。 出力の各単語を作るとき、入力のどの部分が今重要なのかを個別に見に行くことができません。翻訳者に例えれば、原文を一度読んだきり、二度と見返せないまま訳文を書かされているようなもの。長い文でこれをやれば、細部を取りこぼすのは当然です。

ここで、ごく自然な発想が浮かびます。「デコーダが、出力の単語ごとに、入力を見返せたらいいのに」。この一言が、Attention 誕生の瞬間でした。次のセクションで、その中身を見ましょう。

Attentionの誕生 — デコーダが入力を「見返す」

2014年、Bahdanau らは Encoder-Decoder のボトルネックを解く画期的なアイデアを提案しました。それが Attention(注意機構) です。

発想は驚くほどシンプルです。意味ベクトルを1本に潰さず、エンコーダが各単語を読んだときの状態を全部とっておく。 そしてデコーダが1単語を生成するたびに、「今はどの入力単語に注目すべきか」を計算し、必要な状態を重み付きで取り出すのです。

デコーダが全エンコーダ状態を重み付きで見返すAttention

日本語の「犬」を生成しようとしているデコーダを描きました。入力 “I have a dog” の各単語に対して注目度(重み)を計算し、”dog” に 0.80 という強い重みを付けています。矢印の太さが重みの大きさです。「犬」を作るときは “dog” を集中的に見る——これがまさに人間の翻訳者の「見返し」を数式にしたものです。

翻訳者はもう、原文を一度きりしか見られないわけではありません。訳語を1つ書くたびに、対応する原文の箇所を見返せる。これでボトルネックは大きく緩和されます。長い文でも、必要な情報はエンコーダの状態として全部残っているので、デコーダは欲しい部分をピンポイントで取りに行けるからです。

この「どの入力にどれだけ注目したか」を出力語ごとに並べると、翻訳の対応関係(アライメント)が浮かび上がります。

Attention重みのアライメント行列

縦が出力(日本語)、横が入力(英語)で、明るいマスほど強く注目したことを表します。「犬」の行は “dog” の列が最も明るく、「飼う」の行は “have” の列が明るい——モデルが単語の対応を自力で見つけていることがわかります。しかもこの対応は人手で教えたものではなく、翻訳を学習する過程で副産物として自然に獲得されるものです。Attention は性能を上げるだけでなく、「モデルが何を見て判断したか」という解釈可能性まで与えてくれました。

ここで大事なのは、Attention はもともと Encoder-Decoder の中に、デコーダとエンコーダを繋ぐ部品として組み込まれたという事実です。単独で存在する技術ではなく、「デコーダがエンコーダを見返す」という関係そのものが Attention だったのです。これが冒頭で述べた「切り離せない」の意味です。

では、この「見返し」の計算を、もっと一般的な言葉で書き直してみましょう。そこから Transformer への扉が開きます。

Query・Key・Value — Attentionを一般化する共通言語

Attention の計算は、実は「検索」とそっくりです。図書館で本を探す場面を思い浮かべてください。あなたの探したい条件(Query)を、各本の見出し(Key)と照合し、一致度の高い本の中身(Value)を取り出す。Attention はこれと同じ構造をしています。

Query・Key・Valueの枠組み

翻訳の Attention をこの言葉で書き直すと、こうなります。

  • Query(問い合わせ):今まさに生成しようとしているデコーダの状態。「私は次に何を出力すべきか?」という問い
  • Key(見出し):エンコーダの各状態。各入力単語の「見出し」
  • Value(中身):エンコーダの各状態。各入力単語の「中身」

計算の手順は3ステップです。

  1. Query と各 Key の内積をとり、「どれだけ合っているか」の類似度スコアを出す
  2. スコアを softmax で正規化し、合計1の重みにする
  3. その重みで Value を加重平均し、コンテキストベクトルを作る

数式で書くと、有名なこの形になります。

$$ \begin{equation} \text{Attention}(\bm{Q}, \bm{K}, \bm{V}) = \text{softmax}\!\left(\frac{\bm{Q}\bm{K}^\top}{\sqrt{d_k}}\right)\bm{V} \end{equation} $$

$\bm{Q}\bm{K}^\top$ が「Query と Key の総当たりの内積」、$\sqrt{d_k}$ で割るのは内積が大きくなりすぎないための調整、softmax で重みにして、$\bm{V}$ を加重平均する——先ほどの3ステップそのままです。

この Q・K・V という抽象化が、決定的に重要でした。というのも、Query と Key/Value をどこから作るかを変えるだけで、まったく違う種類の Attention が作れるからです。

  • Query をデコーダから、Key/Value をエンコーダから作る → 交差注意(Cross-Attention)。これがさっきの翻訳の見返し
  • Query も Key も Value も同じ文から作る → 自己注意(Self-Attention)

「自分の文の中で、単語どうしがお互いを見る」——この自己注意こそ、2017年の Transformer が主役に据えた仕組みです。Attention が Encoder-Decoder を繋ぐ脇役から、アーキテクチャの中心へと躍り出た瞬間でした。次のセクションで、その配置を見ていきます。

Transformerでの3種類のAttentionの配置

2017年の論文「Attention Is All You Need」は、RNN を完全に捨て、Attention だけで Encoder-Decoder を作れることを示しました。タイトルの「Attention こそが全て」とは、まさにこの意味です。

Transformer も骨格は Encoder-Decoder のままです。違うのは、その中身が3種類の Attention で組み上げられている点です。

Transformerの3種類のAttentionの配置

配置を整理しましょう。

  1. エンコーダの自己注意:入力文の単語どうしが相互に注目し合う。文全体の文脈を捉える
  2. デコーダのマスク付き自己注意:出力文の単語どうしが注目し合う。ただし未来の単語は見せない
  3. 交差注意:デコーダの各単語が、エンコーダの出力を見返す。これが Encoder と Decoder を繋ぐ橋であり、Bahdanau の Attention の直系の子孫

注目してほしいのは、3番目の交差注意です。これは翻訳モデルの「見返し」がそのまま Transformer に受け継がれたもの。Attention が Encoder-Decoder を繋ぐという最初の役割は、Transformer でもしっかり生きているのです。その上で、エンコーダ内・デコーダ内にも自己注意を張り巡らせ、RNN の逐次処理を不要にした——これが Transformer の革新でした。

自己注意が何をするのか、具体例で見てみましょう。

同じ文の中で全単語が相互参照する自己注意

「それは疲れていたから」という文で、「それ」が何を指すかを考えます。自己注意は、同じ文の中の全単語を見渡して、「それ」が「疲れて」と関係が深いと判断できます。Query も Key も Value も同じ文から作るので、文の内部の関係(主語と述語、代名詞と先行詞など)を捉えられるわけです。RNN のように順番に読まなくても、一気に全単語間の関係を計算できるのが強みです。

一方、デコーダの自己注意には特別な制約があります。

デコーダのマスク付き自己注意(因果マスク)

文章を生成するとき、モデルは左から順に単語を作ります。「犬」を生成する時点では、まだ「を」「飼う」は作られていません。ところが自己注意は放っておくと全単語を見てしまう——つまりまだ生成していない未来の答えをカンニングしてしまいます。それを防ぐのがマスクです。図の白いマス(未来の単語)を「見えない」ようにブロックし、各単語は自分と過去の単語だけを見られるようにします。これを因果マスク(causal mask)と呼びます。訓練時と生成時で振る舞いを揃えるための、重要な仕掛けです。

3種類の Attention は、置き場所と Q・K・V の作り方が違うだけで、計算式(先ほどの softmax の式)は完全に同じです。この統一感こそ Transformer の美しさであり、次に見るモデルの多様性の源でもあります。

BERT・GPT・翻訳モデル — 同じ心臓部の配置違い

Attention と Encoder-Decoder の関係がわかると、現代の主要モデルがすべて「同じ部品の組み替え」だと見えてきます。

Encoder-only/Decoder-only/Enc-Decの3系統

  • エンコーダのみ(BERT など):自己注意で文全体を双方向に理解する。マスクをかけないので前後両方を見られる。文書分類や穴埋め、検索に強い
  • デコーダのみ(GPT など):マスク付き自己注意で、次の単語をひたすら予測する。交差注意はなく、生成に特化。ChatGPT を含む大規模言語モデルの主流
  • エンコーダ+デコーダ(T5・翻訳・要約):自己注意と交差注意の両方を使う。入力を理解して別の系列を出力するタスク(翻訳・要約)に向く

3系統とも、心臓部は同じ Attentionです。違うのは「エンコーダだけ使うか」「デコーダだけ使うか」「両方使って交差注意で繋ぐか」という配置だけ。この視点を持つと、新しいモデルが出てきても「ああ、これはデコーダのみ系だな」と構造をすぐ見抜けるようになります。

ここまでの物語を、1本の時間軸にまとめておきましょう。

seq2seqからTransformerへの進化のタイムライン

2014年に固定ベクトルの Seq2Seq が生まれ、同じ年にそのボトルネックを治す Attention(交差注意)が登場し、2017年に Attention だけで組み上げた Transformer が現れる。Attention は Encoder-Decoder の弱点を治す薬として生まれ、やがてアーキテクチャの主役になった——これが両者が切り離せない理由の全体像です。

理屈を絵で追ってきました。最後に、この物語の核心を Python で手を動かして確かめましょう。

Pythonで確かめる — ボトルネックと交差注意

主張を2つ、コードで検証します。ひとつは「固定ベクトルだけだと長い系列で情報が失われる」こと、もうひとつは「交差注意なら必要な入力をピンポイントで取り出せる」ことです。

まず、固定長ベクトルへの圧縮がなぜ苦しいのかを、簡単な復元タスクで見ます。長さの違う系列を1本の固定ベクトルに平均で潰し、そこから元を復元しようとしてみます。

import numpy as np
rng = np.random.default_rng(0)

def compress_and_guess(length, dim=8):
    """系列をdim次元の固定ベクトルに平均で圧縮し、各要素の復元誤差を見る"""
    seq = rng.normal(size=(length, dim))      # 長さlengthの系列
    context = seq.mean(axis=0)                 # 固定ベクトル(1本に潰す)
    # 固定ベクトルだけから各位置を復元しようとする→全部contextで代用するしかない
    err = np.mean([np.sum((seq[t] - context)**2) for t in range(length)])
    return err

for L in [2, 5, 10, 20, 40]:
    print(f"系列長 {L:2d}: 復元誤差 {compress_and_guess(L):.3f}")

出力はおおよそ次のようになります。

系列長  2: 復元誤差 4.109
系列長  5: 復元誤差 5.190
系列長 10: 復元誤差 6.976
系列長 20: 復元誤差 8.571
系列長 40: 復元誤差 7.410

系列長が2から20へ増えるにつれ、復元誤差がほぼ倍増しています。1本のベクトルに潰すと、系列が長くなるほど個々の情報が取り戻せなくなる——これがボトルネックの数値的な姿です(40でわずかに下がるのは、次元8に対して情報が飽和し平均が安定するためで、いずれにせよ長系列では誤差が大きいままです)。

では交差注意を使うと何が変わるか。Query・Key・Value を実装して、デコーダが特定の入力にちゃんと集中できることを確かめます。

import numpy as np

def softmax(x):
    e = np.exp(x - x.max(axis=-1, keepdims=True))
    return e / e.sum(axis=-1, keepdims=True)

def cross_attention(query, keys, values):
    """query:(d,) keys,values:(n,d) → コンテキスト(d,)と重み(n,)"""
    d = query.shape[-1]
    scores = keys @ query / np.sqrt(d)         # Queryと各Keyの内積
    weights = softmax(scores)                  # softmaxで重みに
    context = weights @ values                 # Valueの加重平均
    return context, weights

# エンコーダの4状態(I, have, a, dog を模した8次元ベクトル)
enc = np.eye(4, 8)                             # 各単語を直交ベクトルで表現
# デコーダが「犬」を作るときのQuery: "dog"(4番目)に近い向き
query = enc[3] + 0.1 * np.random.default_rng(1).normal(size=8)

context, w = cross_attention(query, enc, enc)
print("入力[I, have, a, dog]への注目重み:", np.round(w, 3))
print("最も注目した入力の位置:", w.argmax(), "(0始まり)")

出力は次のようになります。

入力[I, have, a, dog]への注目重み: [0.229 0.233 0.229 0.308]
最も注目した入力の位置: 3 (0始まり)

デコーダの Query が “dog”(位置3)に最も強い重み 0.308 を割り当てました。固定ベクトルのように全入力を1本に潰すのではなく、今必要な入力を選んで取り出せているのがわかります。重みの差がそれほど極端でないのは、単純な直交ベクトルとノイズで模擬したためで、実際のモデルは学習を通じてもっと鋭い注目を獲得します。それでも「Query に合う Key に重みが集まる」という交差注意の本質は、この数行で確認できました。

まとめ

本記事では、AttentionとEncoder-Decoderがなぜ切り離せないのかを、翻訳モデルの進化から根本的にたどりました。

  • Encoder-Decoder:入力を「読む係」と「書く係」に分け、長さも順序も違う変換を可能にした
  • ボトルネック問題:全情報を1本の固定ベクトルに潰すため、長文で品質が崩れた
  • Attentionの誕生:デコーダがエンコーダの全状態を「重み付きで見返す」ことでボトルネックを解消。これが交差注意の原型で、もともと Encoder-Decoder を繋ぐ部品だった
  • Q・K・V:Attentionを検索の言葉で一般化。Query/Keyの作り方を変えるだけで交差注意にも自己注意にもなる
  • Transformer:自己注意(各ブロック内)+交差注意(2つを繋ぐ)+マスク付き注意(未来を隠す)の3配置で構成
  • BERT・GPT・翻訳:どれも同じAttentionの配置違い。エンコーダのみ/デコーダのみ/両方

「Attention は Encoder-Decoder の弱点を治す薬として生まれ、やがて主役になった」。この一文が、両者の関係の核心です。次のステップとして、各部品を深掘りする以下の記事へ進んでください。