LLMは文章をどう数値に変え、次のトークンを選ぶのか――トークン化・Attention・KVキャッシュの流れ

LLMへ文章を渡してから、次の一トークンが選ばれるまでには、種類の違う数値が何段階も登場します。トークンID、埋め込み、各層の表現、Attentionの重み、logit、確率は、すべて数値ですが役割は同じではありません。

この記事では、自己回帰型Transformerの推論の流れを追います。文字列をID列へ変え、各位置の表現を更新し、処理済みのKとVを再利用して、次のIDを選ぶまでを説明します。モデルごとの差がある箇所は、共通する範囲に絞ります。

対応するショート「LLMのトークンIDは、意味の近さを表す番号ではない」では、数値を混同しないための入口を短く整理しました。この記事は、同じ流れを図と架空例まで広げて説明します。

まず全体像

入力文字列から次のトークンを選ぶまでの流れ 入力文字列をトークンID列へ変換し、埋め込みをTransformerの層で更新する。処理済みの位置では各層のKとVをキャッシュから再利用する。最後に語彙全体のlogitを生成設定で処理して次のIDを選び、列へ追加して処理を繰り返す 入力文字列 「雨が降る」 トークンID列 語彙表の参照番号 初期ベクトル 埋め込みを取得 Transformerの各層 Attention・FFN 残差接続・正規化 logit 語彙全体 各層のKVキャッシュ 処理済み位置のK・V 次の計算で再利用 次のIDを選ぶ greedy または sampling 選んだIDを列へ追加し、停止条件まで繰り返す
IDは語彙表の項目を指し、ベクトルは層の計算で変化します。最後に出るlogitを生成設定で処理して次のIDを選びます。図は横へスクロールできます。

文章はモデル固有のID列になる

トークナイザーは文字列をモデルが扱う単位へ分け、各トークンを語彙表のIDへ変換します。トークンは単語一つとは限りません。単語の一部、記号、バイト列、文頭・文末などを示す特殊トークンも含まれます。

たとえば、次の対応は仕組みを説明するための架空例です。

段階 説明用の値
入力文字列 雨が降る
トークン列 [雨] [が] [降] [る]
ID列 [81, 204, 57, 19]

実際の分割とIDは、使用するモデルとトークナイザーによって変わります。この例のIDは実在するモデルの出力ではありません。

IDの役割は、語彙表のどの項目かを指定することです。ID 81と82が近い番号でも、二つのトークンの意味が近いとは限りません。モデルはIDを使って埋め込み層を参照し、トークンごとの初期ベクトルを得ます。

ここでいうトークン埋め込みと、意味検索で使う文章埋め込みも別物です。前者はモデル内部の処理を始める各位置の表現です。後者は、文章や段落を一つのベクトルとして比較する用途があります。

同じ「埋め込み」という名前だけで、数値の単位や目的を同一視できません。

参考:Tokenizer — Hugging Face Transformers、Semantic Textual Similarity — Sentence Transformers

各トークンのベクトルは層ごとに更新される

モデルは位置情報を計算のどこかへ組み込みます。埋め込みへ加算する方式もあれば、Attention内のQとKへ位置情報を組み込む方式もあります。

Transformerのブロックは、Attentionだけで構成されるわけではありません。代表的には、次の処理を組み合わせます。

  • Self-Attention:その位置自身を含む、参照可能な位置から情報を集める
  • Feed Forward Network:各位置の表現を個別に変換する
  • 残差接続:ブロックへ入った表現と処理結果を組み合わせる
  • 正規化:層を重ねた計算を扱いやすくする

これらの処理を通るたび、各位置のベクトルは周囲の文脈を反映した表現へ更新されます。正規化をAttentionの前後どちらへ置くか、どのAttention方式を使うかなど、細かな構成はモデルごとに確認が必要です。

したがって、一つのトークンに対応する数値を追うときも、「埋め込み層から取り出した直後」なのか「何層目かを通った後」なのかを区別します。同じ位置を表すベクトルでも、層が違えば中身は変わります。

参考:Attention Is All You Need、RoFormer: Enhanced Transformer with Rotary Position Embedding

AttentionはQとKで重みを作り、Vを混ぜる

Self-Attentionは、トークンIDの大小を比べて参照先を決めているわけではありません。各位置の現在の表現を別々の重みで変換し、Query(Q)、Key(K)、Value(V)を作ります。

Scaled Dot-Product Attentionの基本形は、次の順序です。

  1. 各位置のQと、参照候補のKの内積を計算する
  2. ベクトルの次元に応じてスコアを調整する
  3. 参照できない位置をマスクする
  4. softmaxで参照先ごとの重みへ変換する
  5. その重みでVを混ぜる

QとKは、どの位置をどれだけ参照するかを決める側です。Vは、参照先から取り込む情報を担います。

自己回帰型の生成では、未来の位置を参照しないように因果マスクを使います。その位置自身は参照できます。

実際のLLMでは、この計算を複数のヘッドに分けるほか、KとVのヘッドを共有する構成などもあります。それでも、IDそのものではなく、層の途中にある表現からQ、K、Vを作るという区別が重要です。

参考:Attention Is All You Need

生成では処理済みのKとVを再利用する

自己回帰型のLLMは、入力プロンプトを処理したあと、原則として次のトークンを一つずつ生成します。新しいトークンが増えるたびに過去の全位置のKとVを計算し直すと、同じ計算が繰り返されます。

KVキャッシュでは、入力プロンプトを含む処理済みの位置のKとVを、層ごとに保存します。次のトークンでは新しいQ、K、Vを計算し、保存済みのK、Vと合わせます。新しいKとVもキャッシュへ追加します。

KVキャッシュが保存するのは、文章そのものの代わりになるデータではありません。すでに処理した位置の表現から各層で作ったKとVです。トークンID列や入力文字列をシステムが別に保持することとは、役割を分けて考えます。

再計算を減らせる一方で、キャッシュはトークン数や層数などに応じてメモリを使います。モデルによっては参照範囲を制限するため、すべての位置を保持し続けません。

キャッシュを固定長にする、圧縮する、CPUへ移すといった方式でも、速度とメモリの関係が変わります。

参考:Caching — Hugging Face Transformers、Cache strategies — Hugging Face Transformers

logitと生成設定から次のIDを選ぶ

Transformerの最後の表現から、語彙にある各トークン候補のlogitを計算します。logitは確率へ変換する前のスコアです。ここからどのIDを選ぶかは、生成方式と設定によって変わります。

設定・方式 何をするか 混同しやすい点
greedy decoding 最も高いスコアの候補を選ぶ ランダムな抽選ではない
temperature サンプリングに使う確率分布の集中度を変える 候補を選ぶ方式そのものではない
top-k 上位k個の候補を残す k個すべてを出力するわけではない
top-p 累積確率が指定値へ達する最小の候補集合を残す 残る候補数は毎回一定ではない

サンプリングでは、temperatureで調整したlogitを確率へ変えます。必要に応じてtop-kやtop-pで候補を絞り、その分布から次のトークンを選びます。

実装によって処理順や同時に使える設定は異なります。具体的な挙動を調べるときは、実行環境の仕様も確認します。

temperatureを低くすると、高いスコアの候補へ確率が集まりやすくなります。高くすると、確率が広がりやすくなります。

temperatureはモデルの知識を増やしません。値を下げても、回答が必ず正確になるわけではありません。

選ばれたIDを入力列へ追加し、次のlogitを計算します。このループは、文末などの停止トークン、最大生成長、実行環境が定めた停止条件のいずれかに達するまで続きます。

参考:Generation — Hugging Face Transformers

数値を混同しないための確認順

モデルの説明、デバッグ出力、実行ログを読むときは、数値を次の順で切り分けると混同を減らせます。

  1. トークナイザーの出力:文字列がどのトークンへ分かれ、どのIDになったか
  2. モデルへ入る表現:IDから得た埋め込みへ、位置情報をどの段階・形状で組み込むか
  3. 層の途中の表現:何層目の中間表現か、Q、K、Vのどれか、どのマスクを使うか
  4. キャッシュ:どの層・位置のKとVを保持し、どこまで再利用するか
  5. 生成処理:語彙全体のlogitへ、どの方式と設定を適用して次のIDを選ぶか

IDの差を意味の距離として読まないことが重要です。Attentionの重みを、トークンIDの直接比較だと思わないことも重要です。

temperatureをモデル自体の能力変更だと思わず、これらを別物として扱えば、数値を読み違えにくくなります。

まとめ

LLMの推論は、文字列をモデル固有のトークンID列へ変えるところから始まります。IDを使って初期ベクトルを取り出し、Transformerの層で文脈を反映した表現へ更新します。

Attentionは中間表現から参照先の重みを求め、KVキャッシュは処理済みの計算を再利用します。最後に語彙全体のlogitから次のIDを選び、同じ流れを繰り返します。

「いま見ている数値は、どの段階の何を表すのか」を先に確かめれば、トークン、埋め込み、Attention、キャッシュ、生成設定を一つの意味へまとめずに読めます。