ブログ一覧へ
Transformer完全解説

Transformer完全解説

「Attention Is All You Need」(Vaswani et al., 2017)を一から丁寧に解説します。

読了目安: 約20分

1. Transformerとは?──なぜ生まれたか

2017年、Googleの研究者たちが「Attention Is All You Need」という論文を発表し、Transformerが誕生しました。

それ以前、自然言語処理(NLP)の主役はRNNとそれをベースにしたエンコーダ-デコーダモデルでした。 しかしRNNには、根本的な欠点がありました。

RNNの問題点

逐次処理しかできない。 単語を1つずつ順番に処理するため、

  1. 文が長いほど訓練が遅くなる(GPUの並列計算をほとんど使えない)
  2. 先頭付近の情報が薄れていく長距離依存問題が生じる

たとえば100単語の文で先頭の主語を参照しようとすると、RNNは100ステップ分の情報を引き継がなければなりません。

そこでTransformerは再帰も畳み込みも使わず、Attentionだけで系列変換を行うという大胆な設計を採用しました。 これにより入力を一度に並列処理でき、学習速度と精度が飛躍的に向上しました。

結果として翻訳タスクで当時の最高精度(BLEU 28.4)を大幅に更新しただけでなく、 BERT・GPT・T5・ViTなど現代のほぼすべての大型モデルの基盤となっています。

Transformerを理解することは、現代AIの基礎を理解することに等しいのです。

2. 全体アーキテクチャ──大きな流れを掴む

TransformerはEncoder-Decoder構造を採用しています。 まず全体の流れを掴んでから、各モジュールを詳しく見ていきましょう。

ここでは「Yo tengo gatos(スペイン語)→ I have cats(英語)」の翻訳を例にとります。

Transformerのアーキテクチャ

  • Encoder:原文を受け取り、「文脈を考慮した単語ベクトル」に変換する
  • Decoder:Encoderの出力を参照しながら、翻訳文を1単語ずつ生成する

各ブロックが6層重なっており、層を通るほど抽象度の高い表現になっていきます。 では、入力の前処理から順に中身を見ていきましょう。

3. 入力の準備──単語をベクトルに変換する

3.1 単語埋め込み(Word Embedding)

ニューラルネットが扱えるのは数値だけです。

まず「単語」という離散的な記号を、数値のベクトルに変換する必要があります。

最もシンプルな方法は、語彙数分の次元を用意してその単語だけ1にするone-hotエンコーディングです。 しかしこれでは「cat」と「dog」の距離も「cat」と「Tokyo」の距離も同じになってしまい、単語間の意味的な近さが表現できません。

そこで使われるのが単語埋め込みです。

各単語をdmodel=512d_{\text{model}} = 512次元の実数ベクトルにマッピングします。

💡 なぜベクトルで意味が表現できるのか

訓練を通じて似た文脈で登場する単語には似たベクトルが割り当てられるようになります。 つまり、ベクトルの差や和が意味的な関係を反映します。 "512次元のベクトル"は、各単語が持つ意味・品詞・使われ方などの情報を、512個の実数で圧縮して表現したものです。

この単語埋め込みの重み行列WeRV×dmodelW_e \in \mathbb{R}^{|\mathcal{V}| \times d_{\text{model}}}は、 訓練を通じて学習されます(V|\mathcal{V}|は語彙サイズ)。

3.2 位置エンコーディング(Positional Encoding)

ここで問題があります。Attentionは単語を「集合」として扱うため、単語の順序を考慮しません。 "I love cats" と "cats love I" が区別できず、同じ入力として扱われてしまいます。

これを解決するため、各単語ベクトルに位置情報のベクトル(Positional Encoding)を加算します。

入力pos=Embedding(wordpos)+PEpos\text{入力}{\text{pos}} = \text{Embedding}(\text{word}{\text{pos}}) + \text{PE}_{\text{pos}}

位置エンコーディングの計算式

PE(pos,;2i)=sin!(pos100002i/dmodel)\text{PE}{(\text{pos},;2i)}=\sin!\left(\frac{\text{pos}}{10000^{2i/d{\text{model}}}}\right)

PE(pos,;2i+1)=cos!(pos100002i/dmodel)\text{PE}{(\text{pos},;2i+1)}=\cos!\left(\frac{\text{pos}}{10000^{2i/d{\text{model}}}}\right)

変数の意味を確認しましょう。

  • pos\text{pos}:文中の位置。最初の単語が0、次が1、…と続く
  • ii:512次元のうち何番目の次元か(0〜255)
  • dmodeld_{\text{model}}:ベクトルの次元数(= 512)

phase1-vec

ポイントは次元ごとに異なる周期のsin/cos波を割り当てることです。 iiが大きくなるほど分母100002i/dmodel10000^{2i/d_{\text{model}}}が大きくなり、波の周期が長くなります。

i = 0 → 周期が非常に短い波(隣の位置で大きく変化)
i = 128 → 周期が非常に長い波(隣の位置ではほとんど変化しない)

これは時計のアナログ表示に似ています。 秒針(i = 0)は1分で一周する短い周期、 分針(i = 1)は60分で一周する長い周期—— 複数の針を組み合わせることで、どの時刻かを一意に特定できます。 位置エンコーディングも同じ発想で、短い周期から長い周期まで複数の波を重ねることで、各位置に一意なベクトルを生成します。

💡 なぜsin/cosの組み合わせなのか

sin/cosには重要な数学的性質があります。 任意の相対位置kkに対して、 PEpos+k\text{PE}_{\text{pos}+k}PEpos\text{PE}_{\text{pos}}線形変換で表せます。

PE(pos+k)=MkPE(pos)\text{PE}{(\text{pos}+k)}=M_k \cdot \text{PE}{(\text{pos})}

これは三角関数の和積の公式から導かれます。

A=pos/100002i/dA = \text{pos}/10000^{2i/d}B=k/100002i/dB = k/10000^{2i/d}とおくと、 PE(pos+k)の各次元はPE(pos)の対応する2次元(sinとcos)の線形結合になっています。

このおかげで、Attentionが「2単語間の距離kk」を学習しやすくなります。 また、sinとcosの値域は常に[1,1][-1, 1]に収まるため、 訓練時より長い文章が来ても値が爆発せず、外挿(未見の長さへの適用)も可能です。

最終的に、単語埋め込みと位置エンコーディングを足し合わせたベクトルが Encoderへの入力として使われます。

4. Encoder──文脈を考慮した表現を作る

Encoderブロックは以下の3つのコンポーネントで構成されています。

  1. Multi-Head Self-Attention
  2. Position-wise Feed-Forward Network
  3. 残差接続(Add)& Layer Normalization(Norm)

phase4-encoder

順番に見ていきましょう。

4.1 Attentionメカニズム──単語間の「関連度」を計算する

Attentionの動機から始めます。

The animal didn't cross the street because it was too tired.

この文で"it"が何を指すか理解するには、"animal"に注目する必要があります。 RNNはこれを「情報を順番に引き継ぐ」ことで解決しようとしましたが、 文が長いと情報が薄れてしまいます。

Attentionは発想を変えます。すべての単語ペアの関連度を一度に計算してしまおうという考え方です。

Query・Key・Value(Q・K・V)

AttentionはQuery(Q)・Key(K)・Value(V)という3種類のベクトルで計算されます。 まず「なぜ3種類も必要なのか」という動機から始めましょう。

素朴なAttentionの問題点

最もシンプルな「単語間の関連度」の測り方は、 単語ベクトル同士の内積をそのまま使うことです。

score(it,animal)=vec(it)vec(animal)\text{score}(\text{it}, \text{animal}) = \text{vec}(\text{it}) \cdot \text{vec}(\text{animal})

しかしこれでは、「関連度の計算に使う側面」と「実際に渡す情報」が同じベクトルになってしまいます。

「"it"が何を指すか調べたい」(検索の目的)という側面と、「"it"という単語そのものが持つ情報」(渡す内容)は、本来別々に最適化されるべきです。

そこでQ・K・Vを分離します。

💡 Q・K・Vの役割

Query(Q):「私は何を探しているか」── 検索する側の表現

Key(K):「私はどんな情報を持っているか」── 検索される側の表現

Value(V):「実際に渡す情報の中身」── 検索がヒットしたときに渡すデータ

qkv

Q・K・Vの生成

入力埋め込み行列XRn×dmodelX \in \mathbb{R}^{n \times d_{\text{model}}}nnは単語数)に対して、 学習可能な重み行列をかけることでQ・K・Vを生成します。

Q=XWQ,K=XWK,V=XWVQ = XW^Q,\quad K = XW^K,\quad V = XW^V

ここで各重み行列の形はWQ,WK,WVRdmodel×dkW^Q, W^K, W^V \in \mathbb{R}^{d_{\text{model}} \times d_k}です(dk=64d_k = 64)。

この線形変換が重要な意味を持ちます。 同じ入力ベクトルxitx_{\text{it}}("it"のベクトル)から出発しても、

  • WQW^Qをかけると「itが何かを探すときの表現」に変換される
  • WKW^Kをかけると「itが検索されたときにヒットしやすい表現」に変換される
  • WVW^Vをかけると「itが実際に渡す情報の表現」に変換される

3つの重み行列が「同じ単語の3つの異なる役割」を学習するというわけです。

"I have cats"(3単語)の場合、行列の形を追うと:

X : (3, 512) ← 3単語 × 512次元
W^Q : (512, 64)
W^K : (512, 64)
W^V : (512, 64)
─────────────────────────────
Q = XW^Q : (3, 64) ← 3単語分のQueryベクトル
K = XW^K : (3, 64) ← 3単語分のKeyベクトル
V = XW^V : (3, 64) ← 3単語分のValueベクトル

Scaled Dot-Product Attention

Q・K・VからAttentionを計算する式は以下のとおりです。

Attention(Q,K,V)=softmax!(QKdk)V\text{Attention}(Q,K,V)=\text{softmax}!\left(\frac{QK^\top}{\sqrt{d_k}}\right)V

行列の形を追いながら、4ステップで読み解きましょう。

phase2-attention

Step 1:スコア行列の計算 QKQK^\top

S=QKRn×nS = QK^\top \quad \in \mathbb{R}^{n \times n}

Q : (3, 64)
K^T: (64, 3)
──────────────
S : (3, 3) ← 全単語ペアのスコアが一度に得られる

SijS_{ij}は「単語iiが単語jjにどれだけ関連しているか」を表すスコアです。 "I have cats" なら:

I have cats
I [ 24.1, 3.2, 0.8 ]
have [ 2.1, 22.4, 3.8 ]
cats [ 0.9, 4.1, 23.7 ]

(値は説明のためのイメージです)

Step 2:スケーリング /dk/\sqrt{d_k}

dk=64d_k = 64次元の内積は、次元数が大きいほど値が大きくなる傾向があります。 (各次元の寄与が足し合わされるため)

値が大きすぎるとsoftmaxへの入力が極端になり、出力が「0か1か」のような分布に飽和します。 すると勾配がほぼ0になり、学習が止まってしまいます。

dk\sqrt{d_k}で割ることで値のスケールを揃えます。直感的には「内積の期待値の標準偏差で割る」操作です。

S' = \frac{QK^\top}{\sqrt{64}} = \frac{QK^\top}{8}

Step 3:Softmaxで注意重みへ変換

A = \text{softmax}(S') \quad \in \mathbb{R}^{n \times n}

に対してsoftmaxを適用します。これにより各行の和が1になり、「確率分布」として解釈できます。

I have cats
I [ 0.88, 0.10, 0.02 ] ← "I" はほぼ自分自身に注目
have [ 0.08, 0.80, 0.12 ] ← "have" は自分と "cats" に注目
cats [ 0.03, 0.14, 0.83 ] ← "cats" はほぼ自分自身に注目

この行列AAがAttention重み(Attention Map)です。 各行が「その単語がどの単語にどれだけ注目しているか」を表します。

Step 4:Valueの加重和 V\cdot V

Output=AVRn×dk\text{Output} = AV \quad \in \mathbb{R}^{n \times d_k}

A : (3, 3)
V : (3, 64)
────────────────
Output : (3, 64) ← 各単語の「文脈を考慮した新しい表現」

たとえば "have" の出力ベクトルは:

Output["have"] = 0.08 × V["I"] + 0.80 × V["have"] + 0.12 × V["cats"]

"have" は自分自身のValueを80%、"cats"のValueを12%取り込んだ表現になります。 **「"have"の意味は"cats"との関係によって少し変わる」**という文脈情報が、この加重和に埋め込まれているわけです。

Multi-Head Attention

前節のScaled Dot-Product Attentionは1組のQ,K,VQ,K,Vで計算します。 これはつまり、Attentionの重み行列がひとつの視点でしか表現できないことを意味します。

しかし、自然言語の単語間には、同時に複数の関係が存在します。

"The animal didn't cross the street because it was too tired."

"it"と他の単語の間には、少なくとも以下の関係が同時に存在します。

  • 照応関係:it → animal (照応先)
  • 構文関係:it → was (主語-述語)
  • 意味関係:it → tired (状態の主体)

1ヘッドでは、これら全てを1枚のAttentionマップに押し込めなければならず、 どれかの関係が犠牲になります。

Multi-Headの仕組み

解決策は単純です。 Attention計算をhh個並列に走らせ、それぞれ独立したQ・K・Vを持たせることです。

論文ではh=8h = 8ヘッドを使用します。 ここで重要な工夫があります。各ヘッドの次元を縮小して合計の計算コストを保ちます。

dk=dv=dmodelh=5128=64d_k = d_v = \frac{d_{\text{model}}}{h} = \frac{512}{8} = 64

1ヘッドを512次元で動かす代わりに、64次元のヘッドを8個並列に走らせます。 計算量はほぼ同じでありながら、8つの独立した視点が得られます。

phase3-multihead

各ヘッドの計算:

headi=Attention(QWiQ,;KWiK,;VWiV)\text{head}_i = \text{Attention}(QW_i^Q,; KW_i^K,; VW_i^V)

ここでWiQ,WiK,WiVRdmodel×dkW_i^Q, W_i^K, W_i^V \in \mathbb{R}^{d_{\text{model}} \times d_k}は、 ヘッドごとに独立した重み行列です。同じ入力XXから出発しても、 異なる重み行列をかけることで「異なる側面を切り出す射影」が学習されます。

Concatと射影

8つのヘッドの出力を次元方向に結合(Concat)します。

Concat(head1,...,head8):(3,64×8)=(3,512)\text{Concat}(\text{head}_1,...,\text{head}_8):(3,64 \times 8)=(3,512)

ここで512次元に戻りましたが、これは単なる「くっつけた」状態です。 ヘッド間の情報を混合するために、重み行列WOR512×512W^O \in \mathbb{R}^{512 \times 512}をかけて射影します。

MultiHead(Q,K,V)=Concat(head1,,head8),WO\text{MultiHead}(Q,K,V) = \text{Concat}(\text{head}_1, \ldots, \text{head}_8),W^O

(3,512)×(512,512)=(3,512)(3,512) \times (512,512)=(3,512)

WOW^Oの役割は「8つのヘッドが独立して学習した情報を、 うまく混ぜ合わせて統合する」ことです。 最終的な出力は入力と同じ(3,512)(3, 512)の形になり、次の層へと渡されます。

💡 各ヘッドは実際に違う関係を学習するのか

論文の著者らが学習済みモデルのAttentionマップを可視化したところ、 ヘッドごとに明確に異なるパターンが現れました。

  • あるヘッドは照応関係("it" → "animal")に強く反応
  • あるヘッドは構文的な隣接関係に集中
  • あるヘッドは文末の区切り記号に注目

これは設計で強制したわけでなく、訓練を通じて自然に分化したものです。

4.2 Position-wise Feed-Forward Network

なぜAttentionの後にFFNが必要か

Multi-Head Attentionの出力は「どの単語の情報をどれだけ混ぜるか」を計算したものです。 しかしこの操作は本質的に線形です(加重和にすぎません)。

線形変換をいくら積み重ねても、表現できる関数のクラスは変わりません。 深いネットワークの恩恵を受けるには、非線形変換を挟む必要があります。

また、Attentionは「単語間の情報のやり取り」に特化しています。 Attentionで得た「文脈を取り込んだ表現」を、さらに各単語の内部で変換・整理する層が必要です。 これがFFNの役割です。

FFNの計算

FFN(x)=max(0,;xW1+b1),W2+b2\text{FFN}(x) = \max(0,; xW_1 + b_1),W_2 + b_2

2層の全結合層で、max(0,)\max(0, \cdot)はReLU活性化関数です。

重み行列W1,W2W_1, W_2は全単語で共有されますが、 適用は完全に独立です。Attentionが「単語間の情報交換」なら、 FFNは「各単語内での情報整理」と理解できます。

4.3 残差接続(Add)& Layer Normalization(Norm)

残差接続(Add)──「変化分だけ学習する」

6層のブロックを積み重ねると、勾配消失が深刻な問題になります。 誤差逆伝播では、勾配が層を遡るたびに掛け算されるため、 深い層ほど勾配が指数的に小さくなります(1未満の数を何度もかけるため)。

残差接続はこれを次の式で解決します。

output=x+Sublayer(x)\text{output} = x + \text{Sublayer}(x)

通常のネットワークがF(x)F(x)を学習するのに対し、 残差接続ではF(x)=Sublayer(x)F(x) = \text{Sublayer}(x)が「入力からの変化分」を学習します。

重要な性質として、逆伝播の際に勾配がxxのパスを通って そのまま(掛け算なしで)前の層まで届きます。 どれだけ層が深くても勾配が消えにくくなり、6層のブロックが安定して訓練できます。

Layer Normalization(Norm)──「スケールを揃える」

学習が進むにつれて、各層の出力の分布がずれていく「内部共変量シフト」が起きます。 これにより学習が不安定になったり、収束が遅くなったりします。

Layer Normalizationはこれを防ぐため、各サンプルの全特徴量次元にわたって正規化を行います。

LayerNorm(x)=γxμσ+ϵ+β\text{LayerNorm}(x) = \gamma \cdot \frac{x - \mu}{\sigma + \epsilon} + \beta

  • μ,σ\mu, \sigma:そのサンプルの512次元にわたる平均・標準偏差
  • γ,β\gamma, \beta:スケールとシフトの学習パラメータ
  • ϵ\epsilon:ゼロ除算防止の小さな定数

5. Decoder──翻訳文を1単語ずつ生成する

DecoderはEncoderと同様のブロック(Self-Attention → FFN → Add&Norm)を持ちますが、 2つの重要な追加要素があります。まず全体像を確認しましょう。

phase5-decoder

推論時のDecoderの動作は次のとおりです。

Step1: <start> → "I"
Step2: <start> → "I" → "have"
Step3: <start> → "I" → "have" → "cats"
Step4: <start> → "I" → "have" → "cats" → <end>

1単語ずつ生成し、生成した単語を次のステップの入力に加えながら繰り返します。 この自己回帰的な生成は直感的ですが、訓練時には問題があります。

5.1 Masked Multi-Head Self-Attention

訓練時の工夫:Teacher Forcing

推論時と同じように「1単語ずつ生成→次の入力に追加」を繰り返すと、 訓練が非常に遅くなります。誤った単語を生成するたびにその後の計算が全て狂うためです。

そこで訓練時はTeacher Forcingという手法を使います。 「モデルが生成した単語」ではなく、正解の翻訳文をまとめてDecoderに渡し、 全ての位置を並列に予測させます。

Encoder入力 : "Yo tengo gatos"
Decoder入力 : <start>, "I", "have", "cats" ← 正解をまとめて渡す
Decoder出力 : "I", "have", "cats", <end> ← 全位置を一度に予測

しかしこれには問題があります。 "have"を予測するときに正解の"cats"が見えてしまいます。これはカンニングです。

マスキングの仕組み

これを防ぐのがマスキングです。 スコア行列S=QK/dkS = QK^\top / \sqrt{d_k}を計算した後、 「現在の位置より右」の要素を-\inftyに置き換えます。

"I have cats"(4トークン)のスコア行列の変化を追いましょう。

マスク適用前:

<start> I have cats
<start> [ 3.2, 1.1, 0.8, 2.4 ]
I [ 2.1, 4.5, 1.3, 0.9 ]
have [ 0.8, 2.3, 3.7, 1.2 ]
cats [ 1.4, 0.6, 2.1, 4.8 ]

マスク適用後(右上を-\inftyに):

<start> I have cats
<start> [ 3.2, -∞, -∞, -∞ ]
I [ 2.1, 4.5, -∞, -∞ ]
have [ 0.8, 2.3, 3.7, -∞ ]
cats [ 1.4, 0.6, 2.1, 4.8 ]

Softmaxを適用すると-\inftyの部分はe=0e^{-\infty} = 0になるため、 未来の単語へのAttention重みが完全に0になります。

softmax後:
<start> I have cats
<start> [ 1.00, 0.00, 0.00, 0.00 ] ← <start>しか参照できない
I [ 0.19, 0.81, 0.00, 0.00 ] ← <start>とIだけ参照
have [ 0.08, 0.32, 0.60, 0.00 ] ← haveまで参照
cats [ 0.05, 0.07, 0.20, 0.68 ] ← 全て参照可

これにより訓練時でも「未来の単語は見えない」という推論時と同じ制約が実現され、 かつ全位置を並列に計算できます。

5.2 Cross-Attention──EncoderとDecoderをつなぐ

なぜCross-Attentionが必要か

Masked Self-Attentionが終わった段階で、Decoderは 「これまでに生成した訳語の文脈表現」を持っています。 しかしこれだけでは、原文のどの部分に対応する翻訳を生成すべきかがわかりません。

Cross-Attention(Encoder-Decoder Attention)がその橋渡しをします。

Q・K・Vの出所

💡 Cross-AttentionのQ・K・V
  • Query(Q):Decoderの前層出力(「今、何を生成しようとしているか」)
  • Key(K):Encoderの最終出力(「原文の各単語がどんな内容を持っているか」)
  • Value(V):Encoderの最終出力(「原文の各単語から実際に取り出す情報」)

QだけDecoderから、K・VはEncoderから来ます。 Self-Attentionとの違いはここです。

行列の形で確認しましょう。"Yo tengo gatos"(3単語)→ "I have cats"(3単語)の例:

Encoder最終出力 : (3, 512) ← "Yo", "tengo", "gatos" の文脈表現
Decoder前層出力 : (3, 512) ← "I", "have", "cats" の途中表現
Q = Decoder × W^Q : (3, 64) ← 「何を生成しようとしているか」
K = Encoder × W^K : (3, 64) ← 「原文の各単語のキー」
V = Encoder × W^V : (3, 64) ← 「原文の各単語の内容」
Attention(Q, K, V) : (3, 64) ← 「各訳語が原文のどこを参照したか」

Attentionスコア行列のイメージ:

Yo tengo gatos
I [ 0.85, 0.10, 0.05 ] ← "I"は"Yo"を強く参照
have [ 0.07, 0.88, 0.05 ] ← "have"は"tengo"を強く参照
cats [ 0.04, 0.08, 0.88 ] ← "cats"は"gatos"を強く参照

「cats」を生成しようとするDecoderのQueryが、 Encoderの「gatos」のKeyと強く一致することで、 「gatos」のValueから情報を多く引き出す——これが翻訳のアライメントです。

なおCross-AttentionにはMaskはありません。 訳語を生成するとき、原文全体を参照するのは正当だからです。

EncoderとDecoderのデータフロー(まとめ)

原文: "Yo tengo gatos"
↓ ↓ ↓
[Encoder × 6層]
↓ ↓ ↓
Encoder出力: (3, 512) ──────────────────────┐
↓(K, Vとして)
訳文: <start>, "I", "have" [Cross-Attention]
↓ ↓ ↓ ↑(Qとして)
[Masked Self-Attention] │
↓ ↓ ↓ ───────────────────┘
[Decoder × 6層]
↓ ↓ ↓
"I", "have", "cats"

5.3 最終出力

phase6-predict

Decoderの最終層の出力Rn×dmodel\in \mathbb{R}^{n \times d_{\text{model}}}を、 Linear層で語彙サイズに射影しSoftmaxで確率分布に変換します。

y^t=softmax(htWout+b)\hat{y}t = \text{softmax}(h_t W{\text{out}} + b)

Decoder最終出力 : (1, 512) ← 1時刻分
× W_out : (512, |V|) ← |V| = 語彙サイズ(約37,000)
─────────────────────────────
ロジット : (1, 37000)
softmax後 : (1, 37000) ← 各単語の確率

確率が最も高い単語をその時刻の出力とし(greedy decoding)、 end\langle\text{end}\rangleが出るまで繰り返します。

💡 重みの共有

出力層の重み行列WoutRdmodel×VW_{\text{out}} \in \mathbb{R}^{d_{\text{model}} \times |\mathcal{V}|}は、 入力の単語埋め込み行列WeRV×dmodelW_e \in \mathbb{R}^{|\mathcal{V}| \times d_{\text{model}}}の転置と共有されます。

語彙サイズV|\mathcal{V}|が数万規模になると、この重み行列だけで 数千万パラメータを占めます。共有することでパラメータ数を削減しつつ、 「似た意味の単語は似たベクトルを持つ」という埋め込みの性質を出力層でも活用できます。

6. なぜSelf-Attentionなのか──RNN・CNNとの比較

Transformerの設計選択の核心は「なぜSelf-Attentionなのか」です。 論文は計算量・並列性・長距離依存の3軸でRNN・CNNと比較しています。

6.1 計算量の比較

観点Self-AttentionRNNCNN(カーネル幅kk
1層あたりの計算量O(n2d)O(n^2 d)O(nd2)O(nd^2)O(knd2)O(knd^2)
並列計算完全に可能不可(逐次)可能
長距離の経路長O(1)O(1)O(n)O(n)O(logkn)O(\log_k n)O(n/k)O(n/k)

nn:文の長さ、dd:次元数(512)、kk:CNNのカーネルサイズ

計算量がそれぞれになる理由

  • Self-Attention:O(n2d)O(n^2 d) QKQK^\topの計算が(n×d)×(d×n)(n \times d) \times (d \times n)の行列積であるため。 単語数nnが増えると二乗で計算量が増加します。

  • RNN:O(nd2)O(nd^2) 各ステップで隠れ状態(d,)(d,)に重み行列(d×d)(d \times d)をかけ、 それをnnステップ繰り返すため。

  • CNN:O(knd2)O(knd^2) カーネル幅kkの畳み込みをnn位置に適用し、 各位置でdd次元の変換をするため。

NLPの典型的な設定(n100n \approx 100d=512d = 512)ではndn \ll dのため、 O(n2d)O(10000×512)O(n^2 d) \approx O(10000 \times 512)O(nd2)O(100×262144)O(nd^2) \approx O(100 \times 262144)を比べると Self-Attentionのほうが実質的に小さくなります。

6.2 長距離依存の学習しやすさ

最も重要な優位性は長距離の経路長です。

任意の2単語が「互いの情報をやり取りする」までに何層かかるかを考えます。

文: "The cat that the dog chased ran away"
[0] [1] [2] [3] [4] [5] [6] [7]
"cat"(位置1)と"ran"(位置6)の関係を学習したい場合:
Self-Attention : 1ステップで直接接続
RNN : 5ステップ分の情報を順次引き継ぐ必要がある
CNN(k=3) : log_3(5) ≈ 3層必要(各層でk=3の範囲しか参照できないため)

RNNは情報が層を伝わるたびに変換・圧縮されるため、距離が離れるほど情報が劣化します。 Self-Attentionでは距離に関係なく常に1ステップで直接参照できるため、 長距離の依存関係を損失なく学習できます。

これがTransformerが長文理解に強い根本的な理由です。

7. まとめ

Transformerが革命的だった理由を一言で言えば:

「RNNもCNNも使わず、Attentionだけで、より速く・より精度高く・より汎用的に系列変換を解いた」

ことです。要点を整理すると:

  • Multi-Head Self-Attentionで単語間の関係を並列に・多角的に捉える
  • 位置エンコーディングでAttentionの「順序盲点」を補う
  • 残差接続+Layer Normで深いネットワークを安定訓練
  • 任意の2単語間がO(1)O(1)ステップで繋がり、長距離依存を学習しやすい

この設計のシンプルさと汎用性が、NLPを超えて画像・音声・マルチモーダルへと広がった理由です。 BERT・GPT・T5・ViT——現代AIのすべての起点がここにあります。

関連記事