Transformer 内部構造ガイド入力から出力までを図・形状・コードで追う

全体図での位置処理順 9 番目 / 全 20 ブロック全体図で確認する

Decoder抽象度: トークン難易度: 標準推定学習時間: 約12分

右シフトした出力トークンShifted Output (Teacher Forcing Input)

30秒で分かる要約学習時のDecoder入力は、正解文を1つ右へずらし先頭に開始記号を置いたものです。各位置が「次のトークン」を予測する形にそろえるための仕組みで、推論時の逐次生成とは動きが異なります。

target_ids[B, T]decoder_input_ids[B, T]

Level 1概要役割と処理の流れ

何をする機能か

学習時に、Decoder へ入力する系列を作ります。 正解の出力文を1 つ右へずらし、先頭に開始記号(<bos> や <s>)を置いたものです。

正解(教師信号):    keys  on   the  table  are  lost  .    </s>
Decoder への入力:  <s>   keys on   the    table are  lost  .

同じ列を縦に並べると、「各位置の入力に対する正解は、1 つ先のトークン」という対応になります。

位置Decoder 入力その位置で予測すべき正解
0<s>keys
1keyson
2onthe
3thetable
4tableare
5arelost

なぜ必要か

Decoder は「次のトークンを予測する」モデルです。 学習では、系列のすべての位置について同時に予測を計算し、まとめて損失を取ります。

このとき、位置 tt の入力が「tt 番目までの確定済みトークン」、 正解が「t+1t+1 番目のトークン」という形にそろっていないと、 1 回の順伝播で全位置ぶんの予測を学習できません。右シフトはこの形を作る操作です。

この方式は Teacher Forcing(教師強制) と呼ばれます。 モデル自身の予測が間違っていても、次の位置には正解を入力するため、 誤りが連鎖せず学習が安定します。

全体の中でいつ呼ばれるか

Decoder 側の最初、出力埋め込み の直前です。 学習時のみの処理で、推論時には行いません(下の節で説明します)。

Level 2内部処理Tensor形状・内部ステップ・設計上の意味

前段から受け取るもの

正解の出力文をトークン化した target_ids [B, T]。

後段へ渡すもの

decoder_input_ids [B, T]。出力埋め込み → 位置情報 → マスク付き自己注意 と流れます。

同時に、損失計算用の labels(1 つ左にずらしたもの、つまり元の正解列)を保持します。

例文での処理

共通例文

The keys on the table are lost.

概念上のトークン列(S = 8): [The, keys, on, the, table, are, lost, .]

この例文を出力側の系列として扱うと、次のようになります。

  1. <s>
  2. The
  3. keys
  4. on
  5. the
  6. table
  7. are
  8. lost
Decoder への入力(右シフト後)。先頭に開始記号が入り、末尾の . が押し出されます。
  1. The
  2. keys
  3. on
  4. the
  5. table
  6. are
  7. lost
  8. .
同じ位置に対応する正解(labels)。位置0の <s> に対する正解は The、位置5の table に対する正解は are です。

位置 5(入力が table)に注目すると、 そこまでに見えているのは <s> The keys on the table で、予測すべきは are です。 are を正しく予測するには、直前の table(単数)ではなく 主語の keys(複数)を参照する必要があります。 この参照を担うのが マスク付き自己注意 です。

学習時と推論時の違い

ここが最も混同しやすい点です。

学習時推論時(生成)
Decoder 入力正解を右シフトした系列(全位置を一度に)これまでに自分が生成したトークン列
実行回数1 回の順伝播で全位置を計算1 トークンにつき 1 回、逐次的に実行
誤りの扱い誤っても次は正解が入力される誤るとその誤りが次の入力になる(誤りが連鎖する)
右シフト操作行う行わない(生成済み列がそのまま入力)

学習時と推論時で入力の作り方が違うことを exposure bias と呼び、 生成品質の議論でよく取り上げられます。

推論時は、先頭の <s> だけを入力して 1 トークン目を得て、 それを末尾に足して再入力する、という手順を繰り返します。 詳しくは 次トークンの確率 を参照してください。

EncoderとDecoderでの違い

Encoder 側にはこの処理がありません。入力文は最初から全部わかっており、 「次を予測する」形にする必要がないためです。

Decoder-only の LLM でも同じ考え方が使われます。 プロンプトと応答をつなげた 1 本の系列に対し、 入力を 1 つずらしたものを正解として学習します。

入力と出力

入力

項目名データの意味データ型Tensor形状記号の意味具体例次の渡し先
target_ids正解の出力文のトークンIDint64 Tensor[B, T]B = バッチサイズ(同時に処理する文の数) / T = 出力系列長(Decoder側のトークン数)[[The, keys, on, the, table, are, lost, .]] のID列右シフト処理
bos_id開始記号のトークンIDint——1(<s>)先頭に挿入

出力

項目名データの意味データ型Tensor形状記号の意味具体例次の渡し先
decoder_input_ids1つ右へずらし、先頭に開始記号を置いた系列int64 Tensor[B, T]B = バッチサイズ(同時に処理する文の数) / T = 出力系列長(Decoder側のトークン数)[[<s>, The, keys, on, the, table, are, lost]] のID列出力埋め込み(Token Embedding)
labels各位置で予測すべき正解(損失計算に使う)int64 Tensor[B, T]B = バッチサイズ(同時に処理する文の数) / T = 出力系列長(Decoder側のトークン数)元の target_ids と同じ交差エントロピー損失
B
バッチサイズ(同時に処理する文の数)
T
出力系列長(Decoder側のトークン数)

このサイトでは Tensor 形状を PyTorch の `batch_first=True` 相当、つまり `[B, S, D]`(バッチ次元が先頭)で統一しています。実装によっては `[S, B, D]` を採用するものもあります。

よくある誤解

Level 3数式・Python数式・疑似実装・計算量・実装差

Pythonで表したインターフェース

from dataclasses import dataclass
import torch
from torch import Tensor
 
 
@dataclass
class DecoderTrainingBatch:
    decoder_input_ids: Tensor  # [B, T] 右シフト後。Decoder へ入力する
    labels: Tensor             # [B, T] 各位置の正解
 
 
def shift_right(target_ids: Tensor, bos_id: int, pad_id: int = 0) -> Tensor:
    """正解列を1つ右へずらし、先頭に開始記号を置く。
 
    Args:
        target_ids: [B, T] int64。正解の出力トークン列。
        bos_id: 開始記号のトークンID。
        pad_id: パディングID(末尾で押し出された分の埋めには使わない)。
 
    Returns:
        [B, T] int64。Decoder への入力列。末尾の1トークンは押し出されて消える。
    """
    batch_size, _ = target_ids.shape
    bos = torch.full((batch_size, 1), bos_id, dtype=target_ids.dtype)
    # [B, 1] と [B, T-1] を連結して [B, T] に戻す
    return torch.cat([bos, target_ids[:, :-1]], dim=1)
 
 
def build_training_batch(target_ids: Tensor, bos_id: int) -> DecoderTrainingBatch:
    """学習用の (Decoder入力, 正解) の組を作る(Teacher Forcing)。"""
    return DecoderTrainingBatch(
        decoder_input_ids=shift_right(target_ids, bos_id),
        labels=target_ids,
    )
 
 
# 最小使用例
BOS = 1
target_ids = torch.tensor([[5, 6, 7, 8, 9, 10, 11, 12]])  # [B=1, T=8]
batch = build_training_batch(target_ids, bos_id=BOS)
 
print(batch.decoder_input_ids)  # tensor([[ 1,  5,  6,  7,  8,  9, 10, 11]])
print(batch.labels)             # tensor([[ 5,  6,  7,  8,  9, 10, 11, 12]])
print(batch.decoder_input_ids.shape, batch.labels.shape)  # [1, 8] [1, 8] == [B, T]
 
# 位置 t の入力に対する正解は、必ず 1 つ先のトークンになっている
t = 4
print(batch.decoder_input_ids[0, t + 1] == batch.labels[0, t])  # tensor(True)

この例で単純化している点

  • パディングと損失のマスク(ignore_index)は最小限にとどめています。
  • 実装によっては labels 側を左シフトする(入力はそのまま)方式を採ります。結果の対応関係は同じです。
  • 推論時の生成ループは next-token-prediction のページで扱います。

型と Tensor 形状の見え方を掴むための擬似実装です。そのまま学習・推論に使えるものではありません。

損失の計算

全位置の予測 logits [B, T, V] と labels [B, T] から、 交差エントロピーをまとめて計算します。

L=−1B⋅T∑b=1B∑t=1Tlog⁡pθ(yb,t∣yb,<t,xb)\mathcal{L} = -\frac{1}{B \cdot T}\sum_{b=1}^{B}\sum_{t=1}^{T} \log p_\theta\big(y_{b,t} \mid y_{b,<t}, x_b\big)

yb,<ty_{b,<t} が「tt より前の正解トークン」であることが Teacher Forcing の要点です。 モデル自身の予測ではありません。

パディング位置は損失から除外します(PyTorch では ignore_index を使います)。

実装差

  • 開始記号: <s> <bos> [CLS] <pad> など、モデルによって異なります。 T5 は <pad> を開始記号として使います。
  • シフトの向き: 入力を右シフトする代わりに、labels を左シフトする実装もあります (logits[:, :-1] と labels[:, 1:] を突き合わせる書き方)。対応関係は同じです。
  • Decoder-only LLM: プロンプト部分の位置を損失から除外し、 応答部分だけで学習することがよくあります(instruction tuning)。

この記事の位置づけ

前提となる知識

入力トークン

このページ

右シフトした出力トークン

次に読むとよい記事

マスク付き自己注意

関連記事