一文要約:訓練データには、各位置の正しい前文とターゲットがすでに書かれているので、一つのSequence内の全位置をまとめて計算できます。推論では次のTokenがまだ存在しないため、それを選んでからでなければ次のStepへ進めません。直列なのは一つの回答内のDecode列であって、すべてのRequestが一列に並ぶわけではありません。


16.1 訓練と推論:本質的な違い

16.1.1 まず表で比べる

訓練推論
目的次Token予測のParameterを学ぶ学習済みParameterで生成する
入力既知の文をInputとTargetに1位置ずらすPromptと、それまでに生成したToken
Target各位置の正しい次Tokenが既知次Tokenは未知で、Decoderが選ぶ
Sequence内の計算1回のForwardで全位置のlogitsを得るPromptはまとめてPrefillし、その後は前Stepに依存
勾配とParameter勾配を計算し、Optimizerが更新する勾配もParameter更新もない
訓練では既知の全位置をまとめて計算し、推論ではPromptをPrefillしてから順番にDecodeする

ここでいう「並列」と「直列」は、一つのSequence内の位置についての話です。

  • 訓練にはBackwardもあるので、1回の推論より安いという意味ではありません。
  • 推論Serverは複数のRequestをBatchにして同時に実行できます。
  • ただし一つの回答では、新しいToken 2はToken 1が選ばれるまで完全な前文を持てません。

16.1.2 なぜ違いが生まれるのか

日本語版では、少し珍しい例を使いましょう。

  • 完成した訓練文:「蒔絵師が硯箱を修復した。」
  • 推論時のPrompt:「蒔絵師が硯箱を」
  • 訓練文には各位置の続きが書かれていますが、推論時にはまだありません。

この表示は読みやすい文字列であって、「漢字1文字=Token」と主張しているわけではありません。区切り方はTokenizer次第で、byte-level Tokenizerでは一つの漢字が単独では読めないbyte断片に分かれることもあります。


16.2 訓練の詳細

16.2.1 ここでのTeacher Forcingとは

Tokenizerの出力を次のように置きます。

全Sequence:[x0, x1, x2, ..., xT]
Model入力: [x0, x1, x2, ..., xT-1]
Target:    [x1, x2, x3, ..., xT]

Targetを曖昧に「右へずらす」と覚えるより、同じ列のInputより1Token先と考える方が確実です。

  • 位置0はx0を読み、x1を予測
  • 位置1はx0、x1を読み、x2を予測
  • ...
  • 位置T-1はx0からxT-1を読み、xTを予測

訓練では、前の位置でModelがSampleした結果ではなく、Datasetにある正しい前文を入力します。これは一般に Teacher Forcing(教師強制) と呼ばれ、Decoder-only Language Modelでは標準的なnext-token trainingです。

16.2.2 なぜ全位置をまとめて計算できるのか

x0からxTまでがSample内にすでに存在するため、Causal Maskを使った一つの行列計算でT位置分のlogitsを得られます。

import torch.nn.functional as F

def train_step(model, optimizer, tokens):
    """tokens: [batch, T + 1]。ここではPaddingを省略する。"""
    model.train()
    optimizer.zero_grad(set_to_none=True)

    input_ids = tokens[:, :-1]
    target_ids = tokens[:, 1:]

    logits = model(input_ids)  # [batch, T, vocab_size]
    loss = F.cross_entropy(
        logits.reshape(-1, logits.size(-1)),
        target_ids.reshape(-1),
    )

    loss.backward()   # ここでは勾配を計算するだけ
    optimizer.step()  # Parameterが変わるのはここ
    return loss.detach()

BatchにPaddingがある場合は、AttentionがPad Keyを読まないようにし、Padに対応するTargetもlossへ入れないようにします。PyTorchでは、そのTargetを-100へ置き換える方法がよく使われます。

16.2.3 Causal Maskは何を隠すのか

位置tはx0からxtまでを読み、x(t+1)を予測します。

位置0が見る:[x0,  -,  -,  -, ...]  x1を予測
位置1が見る:[x0, x1,  -,  -, ...]  x2を予測
位置2が見る:[x0, x1, x2,  -, ...]  x3を予測

対角線上の「現在のToken」は見えます。隠すのはその後ろです。Maskがなければ、位置tが未来位置にある自分の答えx(t+1)を直接読めてしまいます。


16.3 推論の詳細

16.3.1 PrefillとDecode

自己回帰推論はPromptのPrefillと、互いに依存するDecode Stepからなる

標準的な自己回帰推論は二段階です。

  1. Prefill:Prompt全体はすでに存在するため、そのToken位置をまとめて処理します。
  2. Decode:最後の位置のlogitsから新しいTokenを選び、前文へ追加してから次を予測します。
Prompt:「蒔絵師が硯箱を」
           Prefill
次のTokenを選ぶ(読みやすい文字へDecodeすると「修」へ進むかもしれない)
           Token IDを前文へ追加
さらに次のTokenを選ぶ(表示文字列が「修復」へ伸びるかもしれない)
          
EOS、Stop条件、max_new_tokensのいずれかまで続ける

「修」「復」は説明のための表示です。Programが追加するのはToken IDであり、1Tokenが1文字や1単語に対応するとは限りません。

16.3.2 KV Cacheが変えるもの

最も単純なCodeは、Stepごとに可視Window全体を再計算します。KV Cacheは各Layerの過去のKeyとValueを保存します。

  • PrefillでPromptのK、Vを計算して保存
  • Decodeでは通常、最新Tokenだけを各Layerへ送り、Cacheを読む
  • Tokenを選ぶ順序は変わらない。消えるのは重複計算であって、依存関係ではない

第22章で、この仕組みを詳しく分解します。

2026年補足:標準Decodeの論理は順序付きですが、Speculative Decoding は小さなモデルに複数Tokenを下書きさせ、Target Modelがまとめて検証します。最初の草稿が却下されても、補正StepはTarget分布から1Tokenを出力します。草稿が合えば、1回の検証で複数Token分進めます。これはTarget Modelの反復回数を減らしますが、Target Modelを非自己回帰Modelに変えるものではありません。

16.3.3 Context Lengthは自動Sliding Switchではない

Prompt Tokenと生成済みTokenの合計は、Modelが対応するContext内に収まる必要があります。超えたときは、Application側で方針を決めます

  • 生成を止める、または長すぎる入力を拒否する
  • 古いTokenを切り落とす
  • 古い内容を要約してContextへ戻す
  • Modelが本来対応するSliding-window Attentionを使う

「常に直近N Tokenだけ残す」は一つの方針であり、すべてのGPTが自動で行うわけではありません。切り落とされたTokenは、そのForward Passの入力に存在しません。「心理的に忘れた」と言うより正確です。


16.4 KV Cacheを使わない基準Loop

次のCodeは、直列依存を見やすくするために意図的に単純化しています。Batch size 1だけを扱い、各Stepで現在のWindowを再計算します。Production用Inference Engineではありません。

import torch

@torch.inference_mode()
def generate_greedy(
    model,
    prompt_ids,
    context_length,
    eos_token_id,
    max_new_tokens=50,
):
    """prompt_ids: [1, prompt_length]"""
    if prompt_ids.ndim != 2 or prompt_ids.size(0) != 1:
        raise ValueError("This teaching loop expects batch size 1")

    model.eval()
    generated = prompt_ids.clone()

    for _ in range(max_new_tokens):
        model_input = generated[:, -context_length:]
        logits = model(model_input)
        next_token = logits[:, -1, :].argmax(dim=-1, keepdim=True)
        generated = torch.cat((generated, next_token), dim=1)

        if next_token.item() == eos_token_id:
            break

    return generated

混同しやすい点を四つ確認します。

  1. model.eval() はDropoutなどの挙動を切り替えます。最大確率Tokenを自動選択する命令ではありません。
  2. torch.inference_mode() は勾配追跡とAutogradの追加処理を無効にします。
  3. 次Tokenの選択には、最後の位置のlogitsだけを使います。
  4. argmaxを使うからGreedy Decodingです。Samplingへ変えれば、eval modeでも出力は変わります。

16.5 PaddingとBatch推論

16.5.1 左Paddingが便利な理由

Decoder-only Batch推論における左PaddingとAttention Mask

PromptごとにToken数が異なるため、Batch行列へ入れる前に同じ長さへ揃えます。Decoder-only生成では左Paddingがよく使われます。

Request A:[a0, a1, a2, a3]   mask = [1, 1, 1, 1]
Request B:[PAD, PAD, b0, b1] mask = [0, 0, 1, 1]

これなら最後の列は全Requestで実Tokenなので、logits[:, -1, :]を取りやすくなります。a、bがTokenであり、元の文字列は「蒔絵師が硯箱を」と「続きを」のような内容です。

16.5.2 二つのMaskを混同しない

  • Causal Mask:未来位置を読ませない。
  • Padding Mask:PADをContextとして読ませない。

実装は通常この二つを統合し、Softmax前にMaskされたKey scoreを負の無限大へ変えます。そのAttention weightは0になります。推論中にModelが「Paddingを無視するよう学習する」のではなく、その呼び出しでMaskが強制します。

tokenizer.padding_side = "left"
batch = tokenizer(prompts, padding=True, return_tensors="pt")

logits = model(
    batch["input_ids"],
    attention_mask=batch["attention_mask"],
)
next_token_logits = logits[:, -1, :]

右Paddingでは最後の列がPADになり得るので、全行に対して無条件に-1を取るのは誤りです。各行の最後の実位置を集めます。Learned absolute position embeddingを使う自作Modelでは、左Paddingに対応するposition IDsも正しく作る必要があります。成熟したgenerate helperは通常処理してくれますが、自作Codeで決めつけてはいけません。

BatchingやContinuous Batchingは複数Requestを同じHardware上で走らせます。利用率は上がりますが、各回答内部のToken依存は残ります。


16.6 計算と実行Modeの正確な比較

訓練自己回帰推論
Sampleごとの主処理1回のForward + Backward + Optimizer更新1回のPrefill + N個の依存するDecode Step
並列化できる次元Batch、Sequence位置、Attention headBatch、Prompt位置。別Requestも同時実行可能
保持する状態Activation、勾配、通常はOptimizer stateWeightと、一般にはKV Cache
よくあるボトルネック計算量、Memory容量、通信Prefillは計算寄り、DecodeはMemory帯域と1 Step latency寄り
Parameterは変わるかoptimizer.step()で変わる変わらない

したがって「推論は訓練より遅い」とだけ覚えるのは誤りです。Model全体の訓練は、一つの回答生成よりはるかに大きな総計算を使います。ここでの要点は、一つの回答の新Tokenを、既知の訓練Target位置のように全部まとめて計算できないことです。

16.6.1 Dropoutの挙動

ArchitectureにDropoutがある場合:

model.train()  # Dropoutがランダムな無効化に参加
model.eval()   # Dropoutを無効化

現代のLLMにはDropoutを0に設定したものもあります。Dropoutを無効にしてもSamplingはランダムです。逆に低Levelの数値非決定性がGreedy実行へ影響する場合もあります。eval()の意味は「評価時の挙動へ切り替える」であって、「毎回同じ文章を保証する」ではありません。


16.7 Decoding Strategy

Modelが出すのはlogitsです。そこから次Tokenをどう選ぶかはDecoderの仕事です。

16.7.1 Greedyと直接Sampling

# GreedyはSoftmax不要:argmax(logits) == argmax(Softmax(logits))
next_token = logits.argmax(dim=-1, keepdim=True)

# Samplingでは確率が必要。temperatureは正でなければならない
probs = F.softmax(logits / temperature, dim=-1)
next_token = torch.multinomial(probs, num_samples=1)
  • Greedy:各Stepで最大logitを選びます。再現しやすい一方、局所的な最大がSequence全体の最善とは限らず、反復も起こり得ます。
  • Sampling:分布から抽選します。多様になる一方、低品質な低確率Tokenを引くこともあります。
  • Temperature:1未満で分布を尖らせ、1より大きいと平らにします。APIの「temperature = 0」はGreedyへ分岐すべきで、logitsを本当に0で割ってはいけません。

16.7.2 正しいTop-K Index

top_k_probs, top_k_ids = torch.topk(probs, k=50, dim=-1)
sampled_slot = torch.multinomial(top_k_probs, num_samples=1)
next_token = top_k_ids.gather(-1, sampled_slot)

以前のIndex式はBatch次元があると別の軸を選びました。gatherなら、各行で「候補の何番目か」を語彙Token IDへ戻せます。

16.7.3 正しいTop-P(Nucleus Sampling)

logitsにTemperatureとTop-KまたはTop-Pを適用して次Tokenを選ぶ
def sample_top_p(logits, top_p=0.9, temperature=1.0):
    if not 0.0 < top_p <= 1.0:
        raise ValueError("top_p must be in (0, 1]")
    if temperature <= 0:
        raise ValueError("temperature must be positive")

    sorted_logits, sorted_ids = torch.sort(
        logits / temperature, descending=True, dim=-1
    )
    sorted_probs = F.softmax(sorted_logits, dim=-1)
    cumulative = torch.cumsum(sorted_probs, dim=-1)

    # 累積確率を初めてtop_pへ届かせるTokenは残す
    remove = cumulative > top_p
    remove[..., 1:] = remove[..., :-1].clone()
    remove[..., 0] = False

    filtered_logits = sorted_logits.masked_fill(remove, float("-inf"))
    filtered_probs = F.softmax(filtered_logits, dim=-1)
    sampled_slot = torch.multinomial(filtered_probs, num_samples=1)
    return sorted_ids.gather(-1, sampled_slot)

以前のcumsum <= 0.9という概略Codeは、閾値を跨いだTokenを捨て、極端な場合は候補を一つも残しません。Top-Pは累積確率がPへ達する最小集合を残します。これはNucleus Samplingの論文で導入された方法です。実運用ではTemperature、Top-K、Top-Pを組み合わせることも多く、互いに排他的なSwitchではありません。


16.8 なぜGPT系Modelは自己回帰なのか

16.8.1 「言語には順序がある」だけでは足りない

「蒔絵師が硯箱を修復した」と「硯箱が蒔絵師を修復した」は当然違います。しかし、順序を表せるModelは自己回帰Modelだけではありません。自己回帰Modelingが選ぶのは、次の確率分解です。

p(x1, x2, ..., xT) = Π p(xt | x1, ..., x(t-1))

この形には次の利点があります。

  • 各訓練位置に明確なnext-token objectiveがある
  • 可変長Sequenceを生成できる
  • Promptと生成済みPrefixの両方を条件にできる
  • LikelihoodをTokenごとに分解して訓練・評価できる

ただし、文章の一貫性や事実性を保証するものではありません。以前のContextを条件にできるだけで、品質はModel、Data、Decoding、Promptに左右されます。

16.8.2 非自己回帰生成は消えていない

非自己回帰機械翻訳、反復Mask予測、Diffusion Language Modelは、より並列な生成を研究しています。たとえば2025年のLLaDA論文は、ゼロから訓練した大規模Diffusion Language Modelを報告しました。

したがって「非自己回帰は必ず品質が低い」「最良Modelはすべて自己回帰」と断言するのは強すぎます。長く使える結論は、自己回帰Decoderが今も成熟した一般的な経路であり、並列・反復生成も異なる速度と品質のTrade-offを持つ現実的な代替案だ、ということです。


16.9 章のまとめ

16.9.1 中核の比較

観点訓練推論
Targetは既知かはい。訓練Textにあるいいえ。その場で選ぶ
Sequence内処理全Target位置をまとめて計算Prefill後、依存順にDecode
計算StageForward + Backward + OptimizerForwardのみ。一般にはKV Cacheを使う
Dropout設定に応じて有効eval()で無効
Parameter更新optimizer.step()で更新更新しない

16.9.2 中核となる理解

訓練で位置を並列計算できるのは、各位置の正しい前文がDataに存在するからです。標準推論のDecodeが順序付きなのは、選んだTokenが次Stepの前文になるからです。KV Cacheは重複計算を減らし、BatchingはRequestを並列化し、Speculative DecodingはTarget Modelの反復回数を減らしますが、この条件依存の鎖そのものは消しません。


章のチェックリスト

この章を終えたら、次を説明できるはずです。

  • x0からxTを使い、InputとTargetの1Token差を書ける
  • Causal Maskが現在Tokenを見せ、次Tokenを隠す理由を説明できる
  • Prefill、Decode、KV Cacheを区別できる
  • 「単一回答のDecodeは直列」と「推論は全部直列」が違うと説明できる
  • 閾値を跨ぐTokenを落とさないTop-Pを実装できる

次章予告

推論の流れは見えました。KV Cacheの内部は第22章で詳しく扱います。その前に、訓練で最も敏感な調整器の一つ、Learning Rateを理解しておきましょう。大きすぎれば良い領域を飛び越え、小さすぎれば進みません。第17章では、この値が実際に何を制御しているのかを見ていきます。

このページを引用する
Zhang, Wayland (2026). 第16章:訓練と推論の違い - なぜ推論は1トークンずつ進むのか. In Transformer アーキテクチャ:直感から実装まで. https://waylandz.com/llm-transformer-book-ja/chapter-16-training-vs-inference/
@incollection{zhang2026transformer_ja_chapter-16-training-vs-inference,
  author = {Zhang, Wayland},
  title = {第16章:訓練と推論の違い - なぜ推論は1トークンずつ進むのか},
  booktitle = {Transformer アーキテクチャ:直感から実装まで},
  year = {2026},
  url = {https://waylandz.com/llm-transformer-book-ja/chapter-16-training-vs-inference/}
}