生成は一歩ずつ進む:LLMは毎回「次の1トークン」を選んでいる
LLMの生成は、文を一気に出すのではなく、次のトークンを1つ選び、その結果を文脈に追加し、また次を選ぶ、という手順を繰り返す。だから生成が長くなるほど計算は増える。さらに、各ステップでモデルは語彙表の全候補に対して「どれが来そうか」の分布を出す。ここから実際に出力するトークンをどう選ぶかが、デコーディングである。
最も単純なのは、毎回もっとも確率が高いトークンを選ぶ方法で、安定しているが単調になりやすい。逆に確率分布からランダムに選ぶと多様性は出るが、破綻もしやすい。現実の運用では、その中間を取るためにいくつかの制御を組み合わせる。
代表的なのが温度で、確率分布の尖り具合を調整する。温度を下げると保守的になり、上げると多様になる。また、上位の候補だけを残すtop-kや、確率の合計が一定になるまで候補を残すtop-pもよく使われる。これらは「あり得ない候補を切り捨て、あり得る範囲でランダム性を残す」ための道具である。
さらに、同じフレーズを繰り返す癖を抑えるために、過去に出たトークンにペナルティを与える方法もある。停止条件の設計も重要で、EOS(終了記号)や特定の区切りが出たら止める、といった制御が品質と安全に直結する。
KVキャッシュ:過去の計算を使い回して“毎回やり直し”を避ける
推論が遅い最大の理由は、生成のたびに過去の文脈を参照する必要があるからだ。素朴に実装すると、新しいトークンが増えるたびに、過去全体を含む計算を繰り返してしまう。これを避けるのがKVキャッシュである。
自己注意は、過去のトークンの情報を参照する際に、過去側の内部表現をある形に変換して使う。KVキャッシュは、この「過去側の変換結果」を保存しておき、次のステップでは新しいトークンに関する部分だけ計算し、保存した過去情報と組み合わせて参照を作る。結果として、毎回“全文を再計算する”必要が減り、生成が高速化する。
ただし、キャッシュはメモリを消費する。文脈が長くなるほど、保存する過去情報が増え、GPUメモリを圧迫する。長文対応の実装では、速度だけでなくメモリ管理が主要テーマになる。
バッチング:一人ずつ処理するより、まとめて流す方が速い
推論のコストを下げるには、GPUを遊ばせないことが重要だ。単発のリクエストを一つずつ処理すると、GPUの計算ユニットが十分に埋まらず、効率が落ちる。そこで複数のリクエストをまとめて処理するバッチングが使われる。
実務では、入力の長さや生成の進み具合がユーザごとに異なるため、固定バッチは扱いにくい。そこで「到着したリクエストを短い時間窓でまとめて流す」「生成ステップが近いものをまとめて処理する」といった動的バッチングが行われる。さらに、入力を一気に読み込む段階と、生成を刻む段階では計算の性質が違うため、それぞれで最適なスケジューリングが必要になる。
量子化:軽くするほど速くなるが、壊れ方にも癖がある
量子化は、モデルの重みや計算を低いビット幅で表現し、メモリと計算コストを下げる手法である。たとえば8ビット、4ビットの表現を使うと、メモリ帯域が節約され、推論が速くなることがある。特に大きいモデルほど効果が出やすい。
ただし量子化は、精度劣化と引き換えである。劣化は一様ではなく、数字の計算、長い文脈での一貫性、微妙な言い回し、専門領域の正確性などで目立つことがある。また、すべてを同じ精度で落とすのではなく、「特定の層は高精度のまま残す」「重みは低精度だが一部の計算は高精度で行う」といった折衷が取られることが多い。量子化の設計は、速度と品質のバランス設計である。
まとめ:推論は“確率の選び方”と“計算の回し方”の両輪で最適化される
LLMの生成は、次トークンを選ぶ手順の繰り返しであり、デコーディングの設計が出力の性格を決める。KVキャッシュは過去の計算を使い回して速度を上げるが、メモリを消費する。バッチングはGPU効率を高め、スループットを上げるが、遅延とのトレードオフがある。量子化は軽量化と高速化に効くが、壊れ方を理解した上で使う必要がある。推論最適化は、モデルそのものではなく、生成戦略とシステム設計を含む“運用の科学”である。