Transformer完全解剖 — 埋め込みから出力まで一気通貫
「今日は」と打ってから「いい天気」が返るまで、たった1つのトークンが何を持たされ、どこで書き換えられ、最後にどうやって言葉に戻るのか。分かち書き・埋め込み・位置・注意・フィードフォワード・残差・出力ヘッドまで、部品を並べるのではなく1本の旅として通しで歩く地図。
Attention Is All You Need
一次資料 — この記事の根拠
論文の発表 2017-06-12→この解説の公開 2026-08-269年2か月後
Attention Is All You NeedAshish Vaswani, Noam Shazeer, Niki Parmar ほか · 2017-06-12 · v7arXiv:1706.03762論文ページ·PDF原文の要旨(Abstract)を読む
The dominant sequence transduction models are based on complex recurrent or convolutional neural networks in an encoder-decoder configuration. The best performing models also connect the encoder and decoder through an attention mechanism. We propose a new simple network architecture, the Transformer, based solely on attention mechanisms, dispensing with recurrence and convolutions entirely. Experiments on two machine translation tasks show these models to be superior in quality while being more parallelizable and requiring significantly less time to train. Our model achieves 28.4 BLEU on the WMT 2014 English-to-German translation task, improving over the existing best results, including ensembles by over 2 BLEU. On the WMT 2014 English-to-French translation task, our model establishes a new single-model state-of-the-art BLEU score of 41.8 after training for 3.5 days on eight GPUs, a small fraction of the training costs of the best models from the literature. We show that the Transformer generalizes well to other tasks by applying it successfully to English constituency parsing both with large and limited training data.
この記事の読み方 — 1トークンの旅として歩く
Transformerの解説は、部品ごとに分かれていることがほとんどです。注意機構の記事、位置エンコーディングの記事、正規化の記事。ひとつひとつは分かるのに、「で、結局この部品はどこに刺さっているのか」が最後まで見えない。組み立て説明書なしでネジの説明書だけ読まされている状態です。
この記事は逆をやります。たった1つのトークンの視点に立って、入口から出口までを通しで歩きます。
比喩として、トークンを「1冊のノートを持った旅人」だと思ってください。ノートには数百個の数字が書かれています。旅人はいくつもの部屋を通り、部屋ごとにノートを少しずつ書き換えます。部屋を出るときのノートの大きさは、入ったときとまったく同じです。だから同じ形の部屋をいくらでも連結できる。そして最後の部屋を出たとき、そのノートは「次に来るべき単語」を指し示すものに変わっている。
これがTransformerの全体像です。あとは各部屋で何が起きるかを順に見ていきます。
停留所0: 文字列を切り分ける
モデルは文字を読みません。読むのはトークンIDという整数の列です。
「今日はいい天気」という文字列は、まず語彙表(vocabulary)と照らし合わされ、[1284, 402, 9931] のような数字の列に変換されます。この切り方を決めるのがトークナイザで、単語でも文字でもない中間の単位(サブワード)を使うのが普通です。
ここで押さえるべきは1点だけです。この時点でトークンはただの背番号であり、意味を一切持っていません。 背番号1284と背番号1285が似た意味だという保証はどこにもない。切り方そのものの理屈はトークナイザを1からで扱っています。
停留所1: 番号を「意味の座標」に変える
背番号に意味を与える最初の部屋が埋め込み層(embedding)です。
やっていることは、巨大な表の行を1行取り出すだけです。表のサイズは「語彙数 × モデル次元」。語彙が5万語、モデル次元が768なら、5万行×768列の表があり、背番号1284番のトークンは1284行目の768個の数字を受け取ります。
この768個の数字が、冒頭の比喩で言う「ノート」です。旅の最初から最後まで、旅人が持ち歩くのはこの1冊だけで、部屋を通るたびに中身が書き換わっていきます。この列(正確には層をまたいで持ち回られるベクトル)を残差ストリームと呼ぶこともあります。
重要なのは、この表が固定の辞書ではなく学習で決まるという点です。訓練が進むと、似た文脈で使われる語のベクトルは近い向きに寄っていきます。ここまでは文脈を見ていないので、bank は「銀行」と「土手」の両方の意味が混ざった1つのベクトルとして始まります。この曖昧さを解くのが、この先の仕事です。
停留所2: 順番を書き加える
素のTransformerには、致命的に見える性質があります。入力の順番を知らない。
注意機構は「全トークンが全トークンを見る」仕組みなので、トークンを並べ替えても計算結果の対応関係は入れ替わるだけで、「どちらが先に来たか」という情報がどこにもありません。「犬が人を噛んだ」と「人が犬を噛んだ」を区別できないのでは、言語モデルとして話になりません。
そこで、位置の情報を明示的に注入します。原論文はsin/cosで作った位置ベクトルを埋め込みに足し算しました。現代のLLMの多くは、足すのではなく注意計算の直前にQueryとKeyを回転させるRoPEという方式を使います。どちらも狙いは同じで、「何番目か」あるいは「どれだけ離れているか」をベクトルに刻むことです。詳しくは位置エンコーディングを1から理解するを参照してください。
ここまでで旅の準備は完了です。旅人のノートには「意味の初期値」と「自分が何番目にいるか」が書き込まれました。
停留所3: ブロックに入る — 部屋は2つで1組
Transformerブロックは、中に小部屋を2つ持っています。周りを見る部屋と、自分で考える部屋です。この2つで1セット、というのがTransformerの設計の核心です。
前半: 自己注意 — 周りを見る
自己注意は、各トークンが文中の全トークンを見渡して、「自分の意味を決めるのにどれが重要か」の割合を決め、その割合で他トークンの情報を混ぜ込む仕組みです。bank が「銀行」に確定するのは、この部屋で account の情報を強く受け取ったときです。
各トークンは自分のノートから3つの見え方を作ります。何を探しているかを表すQuery、何を持っているかを表すKey、実際に渡す中身であるValue。QueryとKeyの内積が大きいペアほど強く結ばれ、その強さで全員のValueを混ぜます。
つまり「全員の質問と全員の名札を突き合わせて点数表を作り、点数を割合に直して、その割合で全員の中身を混ぜる」と言っているだけです( はKeyの次元数で、 で割るのは点数が大きくなりすぎるのを防ぐため)。この式の導出と、マルチヘッド・因果マスクの意味はAttention機構を1から理解するで詳しく追っています。
この部屋の性質を1行で言うと、トークンどうしの情報を混ぜる唯一の場所です。逆に言えば、他のすべての部屋はトークンを独立に処理しています。
後半: フィードフォワード — 自分で考える
注意を出たノートは、次にフィードフォワード層(FFN)に入ります。ここでは他のトークンを一切見ません。1トークンのノートを、1トークンのまま変換します。
要するに「いったん広い空間に引き伸ばして、非線形関数 で折り曲げて、また元の幅に畳み直す」だけです( は学習される行列、 はバイアス、 はReLUなどの活性化関数)。引き伸ばす幅はモデル次元の4倍が定番で、原論文では512次元を2048次元に広げてから戻しています。
地味に見えますが、ここはブロック内のパラメータの大半を占める部屋です。モデル次元を とすると、注意側の重み(Q・K・V・出力の4枚)は合計で 個、FFN側は の行列が2枚で 個。ブロックの重みのおよそ3分の2がFFNにあります。「知識が詰まっているのはFFN」と言われるのはこの体積の話でもあります。
部屋を出るときの作法: 残差と正規化
各小部屋の出力は、そのまま次に渡されるのではありません。入ってきたノートに足し戻され、正規化を挟みます。
つまり式(2)は「部屋は元のノートを置き換えるのではなく、差分を書き足すだけ」と言っています。この足し算(残差接続)があるおかげで、層を深くしても勾配が最下層まで届き、学習が破綻しません。 は数値の大きさを揃える処理で、原論文は部屋の後ろに置いていましたが、深いモデルでは前に置く形(Pre-LN)が安定するため現在はこちらが主流です。
同じ形の部屋を何十回も
ここが一番だまされやすいところです。Transformerブロックは1種類しかありません。それを何十回も積み重ねているだけです。
入口と出口のノートの大きさが同じ(どちらもモデル次元)なので、ブロックはいくらでも連結できます。ただし重みは共有されません。1層目のFFNと2層目のFFNは別の行列です。同じ間取りの部屋が並んでいるだけで、部屋の中の家具は全部違う、と思ってください。
層が進むにつれて、ノートの内容は「その単語自体の意味」から「文脈の中でその位置が担う役割」へと性質を変えていきます。
終点: ベクトルを言葉に戻す
最後のブロックを出たノートは、まだ数百個の数字です。これを語彙に戻すのが出力ヘッドです。
やることは埋め込みのちょうど逆で、「モデル次元 × 語彙数」の行列を掛けて、語彙の全単語ぶんのスコア(ロジット)を作ります。この行列に埋め込み表を転置して使い回す実装(重み共有)もあります。そのスコアをsoftmaxで確率に直します。
つまり「最終層のノート を語彙のスコア に変換し、温度 で割ってから割合に直す」です。 を小さくすると最大スコアの語がほぼ確実に選ばれ、大きくすると分布がなだらかになって珍しい語も出てきます。APIの temperature はこの そのものです。
生成モデルでは、この確率から1トークンを選び、それを入力の末尾に足して、また停留所0から旅をやり直します。1トークンぶんの旅が、出力の1文字ぶんに相当するわけです。
全体を1つのコードに
ここまでを擬似コードにすると、驚くほど短くなります。
def transformer(ids):
h = embed[ids] + pos(len(ids)) # 停留所1・2
for blk in blocks: # 同じ形の部屋を N 回
h = h + attn(norm1(h), blk) # 前半: 周りを見る
h = h + ffn(norm2(h), blk) # 後半: 自分で考える
return norm_f(h) @ W_u.T # 終点: 語彙のロジット
5行です。この5行に対して、部品ごとの記事が何本も書かれている。逆に言えば、Transformerの難しさは構造ではなく各部屋の中身にあるということでもあります。
形の勘定表
デバッグ時に効くのは、どこで何次元になるかの感覚です。文長を 、モデル次元を 、語彙数を として並べます。
| 場所 | 形 |
|---|---|
| トークンID | |
| 埋め込み後 | |
| 注意のスコア表 | (ヘッドごと) |
| FFNの中間 | |
| ブロック出力 | |
| ロジット |
太っているのは2か所です。注意のスコア表は文長の2乗で膨らみ、ロジットは語彙数で膨らむ。長文が高価な理由と、語彙を増やすと出力層が重くなる理由が、この表から直接読めます。
計算量も同じ表から出ます。注意は に比例し、FFNは に比例する。文が短いうちはFFNが支配的で、文が より十分長くなると注意が支配的になります。「長文コンテキストで急に遅くなる」現象の正体はこの交代です。
現場ではこう使う
設定ファイルを読むとき。 Hugging Faceのモデルを触ると必ず config.json を見ることになりますが、そこに並ぶ値はこの記事の停留所とそのまま対応します。vocab_size は停留所0の語彙表、hidden_size はノートの厚さ 、num_hidden_layers は部屋を何回通るか、num_attention_heads は注意の視点の数、intermediate_size はFFNが引き伸ばす幅(多くは hidden_size の4倍前後)、max_position_embeddings は停留所2が想定する最大の文長です。この6つが読めれば、モデルの重さとメモリ消費はおおよそ見積もれます。
ファインチューニングの対象を選ぶとき。 LoRAで target_modules を指定する場面では、この地図がそのまま選択肢になります。q_proj k_proj v_proj o_proj は注意側の4枚、gate_proj up_proj down_proj はFFN側です。注意側だけに当てるのが軽量な定番ですが、前述のとおりパラメータの体積はFFN側にあるので、知識そのものを動かしたいならFFNを含める判断もあります。どちらが効くかはタスク次第なので、両方試して比べるのが実務の答えです。
推論を調整するとき。 temperature は終点の 、top_p や top_k はsoftmax後の候補を切り詰める処理です。ここで頻出の事故が、temperature=0 にしたのに出力が揺れる、という報告です。多くの実装で温度0は「最大確率の語を選ぶ」に置き換えられますが、それでも並列計算の丸め順序で同点付近の順位が入れ替わることがあり、完全な再現性は保証されません。再現性が要件なら、シードと実装の固定まで含めて検証してください。
知らないと事故になる落とし穴を3つ。 ①max_position_embeddings を超える入力は、位置の扱いが訓練時の範囲外になり、エラーにならないまま品質だけ静かに落ちることがあります。長文を入れる前に必ずこの値を確認してください。②語彙を拡張したときは、停留所1の埋め込み表と終点の出力行列の両方を広げる必要があります。重み共有している実装では片方を触れば済みますが、共有していない実装で片側だけ広げると次元が合わずに落ちます。③文の意味ベクトルが欲しいとき、最終層の出力をそのまま使うのは早計です。最終層は「次のトークンを当てる」ために最適化された場所であり、文全体の意味を代表する場所とは限りません。中間層や別の集約方法と比べて選ぶべきところです。
面接や設計レビューで問われる形。 「文長を2倍にすると計算量は何倍か」という問いには、この記事の勘定表がそのまま答えになります。注意部分は4倍、FFN部分は2倍、全体としてどちらに寄るかは と の比で決まる——ここまで言えれば、KVキャッシュやFlashAttentionといった高速化がどこを削りに行っているのかも、同じ地図の上で説明できます。
まとめ
- トークンは「1冊のノート」であり、埋め込みで初期値を得て、位置を書き足され、ブロックを通るたびに差分を書き加えられる
- ブロックの中身は2部屋だけ。注意=トークン間で混ぜる、FFN=トークン内で変換する。混ぜる場所は注意しかない
- 残差接続のおかげで各部屋は「置き換え」ではなく「書き足し」になり、深く積める
- 出口では語彙サイズのスコアに戻り、温度付きsoftmaxで確率になる
- 太る場所は2つ、注意のスコア表(文長の2乗)と出力ロジット(語彙数)
各部屋の中身は、この地図の停留所ごとに1本ずつ記事があります。全体像を持ったうえで戻って読むと、部品の記事の見え方が変わるはずです。
コメント
コメントにはログインが必要です