Transformer / Hugging Face — Structured Reference

Transformer と Hugging Face を、
制約から読み直す

個々の技術を並べても、なぜその形になったのかは見えてこない。この教材は、三段階の質問セット(① 基礎原理② 実装・最適化③ 拡張・運用)への回答を「問い → 要点 → 補足と訂正」の三層に組み直したものです。
全体を貫くのは四つの制約 —— メモリ通信帯域Attention の O(L²)精度とのトレードオフ。この四つを軸に置くと、サブワードも LoRA も ZeRO も量子化も、同じ問題への別々の答えとして並びます。
隣に置くと効く教材: LLMを「モデル」と「ランタイム」で分けて理解する(本稿の前提となる層分け)/ vLLM 推論最適化(§11 のサービングを実測で深掘り)/ 1トークンの旅(帯域律速の体感)/ 特許 RAG ガイド(§10 の実務適用)
00 / Map

全体地図 — 二つの層と、四つの制約

原理層は「なぜこの形なのか」、実装層は「どう回すか」。層が違えば効く手も違うが、効かせたい相手(制約)はどこまでも同じ四つです。

┌───────────────────────────────────────────────┐ Part I 原理層 なぜこの形なのか ML/DL の帰納性 → Attention → 離散化 └───────────────────────────────────────────────┘ ┌───────────────────────────────────────────────┐ Part II 実装層 どう回すか 訓練効率 → 圧縮 → 検索統合 → 配信 → 自律化 └───────────────────────────────────────────────┘ 貫く制約 メモリ 通信/帯域 O(L²) 精度とのトレードオフ
レイヤ支配的な制約代表的な解
表現語彙サイズ × 系列長サブワード
計算Attention の O(L²)並列化・FlashAttention・FiD
訓練VRAM とインターコネクトLoRA / ZeRO / 混合精度
推論メモリ帯域律速量子化・蒸留・continuous batching
知識パラメータに焼き込めない情報RAG
制御単発推論では解けないタスクエージェントループ
01 / Corrections

先に訂正する 12 件

元の質問文に含まれていた前提のうち、実際とずれていたもの。ここだけ先に読んでも価値があります。共通して厄介なのは、どれも例外を投げないこと —— 精度やスループットだけが静かに落ちます。

よくある前提 → 実際
√dk で割るのは、勾配爆発を防ぐため
防いでいるのは softmax 飽和による勾配「消失」。爆発対策ではない。内積の分散が dk に比例して大きくなると softmax がほぼ one-hot になり、そのヤコビアン diag(p) − ppᵀ が全成分ゼロに漸近する。つまり WQ, WK に勾配が届かなくなる
§06.1
streaming=True にすれば RAM への全展開を避けられる
非ストリーミングでも datasets は RAM に全展開しない。Apache Arrow でディスクに置き mmap する。100GB でも RAM は数百 MB。真の違いはディスク容量と初回起動時間、そして len() と再開性を失うこと。
§07.2
混合精度にすれば VRAM が 50% 減る
パラメータ関連(重み+勾配+オプティマイザ状態)は 16 バイト/param のまま変わらない。fp16 化で浮いた 4 バイトを fp32 マスター重みが埋めるため。減るのは アクティベーション。「最大 50%」はアクティベーションが支配的な設定での話。
§08.3(計算機あり)
勾配蓄積を使えばメモリ制約を超えられる
アクティベーションのピークは ミクロバッチ 1 個分のまま不変。増やしているのは「更新 1 回あたりのサンプル数」であって、収まる大きさではない。真のメモリ削減が要るなら gradient checkpointing を併用する。
§08.3
ZeRO Stage 3 の通信オーバーヘッドは 1.5 倍
理論総量は確かに 1.5 倍。だが実効劣化はそれより大きい。原因は総量ではなく 構造 —— forward/backward の各層に同期点が生じ、層数 × 2 回のレイテンシが積み上がる。NVLink なら実用的、10GbE なら GPU 利用率が半分以下に落ちることも珍しくない。
§08.4
4bit 量子化すれば推論が速くなる
NF4 は weight-only 量子化で、計算時には bf16 に逆量子化される。得られるのは第一義的に メモリ削減であって演算速度ではない。ただし LLM の生成フェーズはメモリ帯域律速なので、結果として速くなることは多い。int8 の整数演算がハードウェア加速されるのとは性質が違う。
§09.1
Late Fusion=文字列連結 / Early Fusion=クロスアテンション統合
RAG 文献での一般的用法は 。Izacard & Grave の FiD が "Fusion-in-Decoder"(=後段での融合)と名乗っている通り、クロスアテンション統合は通常 late / deep fusion 側に分類される。この語を使うなら 定義を明示するのが安全。
§10.0
FastAPI + asyncio.Queue で LLM をバッチ配信する
その設計は分類・埋め込みなど 固定長出力 には正しい。だが LLM の自己回帰生成は 1 件ごとに生成長がバラバラで、素朴なバッチングでは最長の 1 件が終わるまで他が待たされる。必要なのは continuous batching(vLLM / TGI 等)。
§11.2
SentencePiece は BPE や WordPiece と並ぶトークナイズ「アルゴリズム」だ
SentencePiece はそれらを実装するフレームワークで、中身の学習アルゴリズムとして BPE と Unigram を選べます。比較軸が違うので並列に並べると混乱する。真の貢献は前処理(pre-tokenization)の廃止にあり、日本語で効くのは「日本語に特化したから」ではなく空白依存という前提を取り除いたからです。
§14
weight decay は損失に L2 項を足すことと同じ
SGD では等価、Adam では等価になりません。 L2 項を損失に入れると、その寄与まで √v で割られるため 勾配の大きいパラメータほど正則化が弱くなる —— 意図と正反対です。AdamW は減衰項を適応的スケーリングの外に出して分離します。Transformer で AdamW が標準になったのは、この一点の修正の効果。
§16.2(計算機あり)
ONNX にすると推論が最適化されて速くなる
ONNX は計算グラフの交換フォーマットで、それ自体は何も最適化しません。速くするのは ONNX Runtime や TensorRT といった実行エンジンのほう。しかも torch.compile が同種の最適化を PyTorch 内で行う現在、ONNX の主価値は速度より PyTorch 非依存の環境への移植性に移っています。
§18.1
CLIP は画像とテキストを単一の Transformer の中で結合している
CLIP は二つの完全に独立したエンコーダを持ち、両者の間に情報のやり取りは一切ありません。やっているのは同じ埋め込み空間への写像と対照学習だけで、機構としては融合ゼロのデュアルエンコーダ。だから画像について文章を生成できない。融合しているのは Whisper(クロスアテンション)や LLaVA(トークン空間統合)のほうです。
§19
PART I基礎原理レイヤ
02 / Foundations

学習という営みの形

機械学習とルールベースの差は「賢さ」ではなく、人間が書く対象が入れ替わったことにあります。

2.1ルールベースと機械学習の本質的差異

従来型プログラミングでは、人間が写像 f そのものを記述していました。機械学習で人間が書くのは二つだけです。(a) パラメータ付き関数族(アーキテクチャ)(b) 損失関数f はその中から誤差最小化の探索結果として決まります。

2.2なぜ「深く」するのか

理由内容
① 非線形性の合成線形変換をいくら積んでも線形変換のまま。非線形活性化(ReLU 等)を挟んで初めて、単層では表現できない決定境界が作れる。古典例はパーセプトロンと XOR。
② 表現の階層性普遍近似定理より単層でも理論上は任意関数を近似できる。しかし必要な幅が指数爆発する。深くすると下位層の特徴を上位層が部品として再利用でき、同じ表現力を遥かに少ないパラメータで達成できる。
要点「深さ」の実質的な意味は、人手の feature engineering が学習の対象に取り込まれたことにある。
03 / Transformer

Transformer のメカニズム

長距離依存と計算効率という別々の問題を、同じ設計変更ひとつで解いた——ここが要諦です。

3.1RNN / LSTM が抱えていた三つの制約

Transformer の一手は、再帰を捨てて任意の 2 トークン間を 1 回の Attention で直結すること。これで 経路長 O(1)逐次依存の消滅 が同時に成立します。

3.2Attention の計算

各トークン埋め込みを 3 つの学習済み行列で射影し、Query(何を探すか)/Key(何を提供できるか)/Value(実際に渡す情報)を得る。Q と全 K の内積でスコアを取り、√dk でスケールし、softmax で合計 1 の重みへ正規化し、その重みで V を加重平均する。

Attention(Q, K, V) = softmax( Q Kᵀ / √dk ) V
核心「重要度」は射影空間における方向の一致度(内積)で測られる。そして 何を重要と見なすかの基準そのものが、射影行列という学習パラメータとして訓練で獲得される

Multi-Head は複数の部分空間で並列実行する仕組みです。あるヘッドは主語-述語の一致を、別のヘッドは代名詞の先行詞を——というように異なる関係を同時に捉える。単一の注意分布では、どれか一つの関係しか表現できないからです。

3.3並列処理と、その二つの代償

再帰がないので全トークンの Q, K, V を一度に計算でき、Attention 全体が QKᵀ という一回の行列積に帰着します。GPU が最も得意な形式です。ただし代償が二つ。

見落とされやすい点

並列なのは訓練時と入力符号化時です。自己回帰生成(推論)は 1 トークンずつ逐次にしか進みません。実務では KV キャッシュで再計算を避けますが、逐次性そのものは消えない。§11 のサービング最適化がすべてここに由来します。

3.4画像・音声への応用

Transformer が要求するのは ベクトルの列と位置情報だけ。言語であることは要件ではありません。

モダリティ系列化の方法
画像(ViT)16×16 等の固定パッチに分割 → 平坦化 → 線形射影で d 次元へ。以降「パッチ=単語」。2 次元の位置埋め込みを付与する。
音声(Whisper 系)25ms 窓 / 10ms ホップ → メルスペクトログラム → 時間フレーム列。
音声(wav2vec 2.0 系)生波形に畳み込み層をかけて特徴ベクトル列にする。
ここが効くテキストだけが「語彙 ID という離散化」を経由する例外。画像も音声も元から連続値なので線形射影だけで済む。トークナイザという工程がテキスト固有である理由が、ここにあります。
04 / Tokenizer

トークナイザ

「文字列を数字にする」以上のことをしています。語彙サイズ V と系列長 L のトレードオフを最適化する装置です。

4.1入力 ID は意味を持たない

帰結

モデルとトークナイザは必ずペアです。片方を差し替えると ID の意味づけが総崩れになる。AutoTokenizer.from_pretrained() にモデルと同じチェックポイント名を渡す規約は、この制約の反映にすぎません。

4.2未知語をどう扱うか

語彙に 文字(またはバイト)レベルの単位を必ず含める ことで、任意の文字列を既知断片の列へ分解可能にする——これが設計の骨格です。

手法統合 / 削減の基準
BPE頻出する隣接ペアを貪欲に統合していく。
WordPiece尤度向上量 p(xy) / p(x)p(y) が最大のペアを統合する。
Unigram大きな候補集合から損失の小さい単位を削り落とす(逆方向のアプローチ)。
例: supercalifragilistic → super + ##cal + ##if + ##rag + ##ilis + ##tic ## は語中継続の印。##cal と単独の cal別トークン

4.3Padding と Truncation

バッチ処理は (batch_size, seq_len) の矩形テンソルを要求します。長さの不揃いは許されない。

Padding

短い系列に pad_token_id を詰めて長さを揃える。

決定的に重要なのは同時生成される attention_mask。パディング位置を 0 とし、Attention スコア計算時にそこへ −∞ を加算して softmax 後の重みをゼロにする。渡し忘れると、モデルは詰め物を実在の文脈として読む。

Truncation

max_length(多くは位置埋め込みの上限。BERT なら 512)の超過分を切り捨てる。

[CLS] [SEP] 等の特殊トークン分を予め差し引く必要がある。文ペアでは longest_first 等でどちらを削るか決める。

実務padding='max_length' より padding='longest'(動的パディング)。Attention が O(L²) である以上、無駄なパディングのコストは 2 乗で効きます。
05 / Lifecycle

三ステージと pipeline() の内側

事前学習・ファインチューニング・推論で、モデルの内部状態は何が変わり、何が変わらないのか。

5.1三ステージの内部状態変化

ステージ重みの状態目的関数コスト
事前学習ランダム初期化 → 全パラメータ更新自己教師あり(MLM / 次トークン予測)大半がここに集中
ファインチューニング事前学習済みをロード、タスクヘッドのみ新規初期化教師あり中〜小
推論一切変化しないなし小(ただし累積する)
重要な帰結 — in-context learning の正体

プロンプトに例を並べて挙動が変わるのは学習ではありません。前向き計算のアクティベーションが変わっているだけで、重みは 1 ビットも動いていない。会話が終われば何も残らない。「AI が対話で学ぶ」という比喩が誤解を招くのは、このためです。

5.2pipeline() は何を隠しているか

■ 初期化時 タスク名 → 既定チェックポイント解決 → Hub から取得・~/.cache/huggingface/hub にキャッシュ → config.json 読込 → AutoConfig 構築 → config.architectures から具体モデルクラスを決定 → 重みロード → tokenizer.json 等から AutoTokenizer 構築(可能なら Rust 高速版)
実行時の段処理
preprocessトークナイズ → テンソル化 → デバイス転送
_forwardno_grad 下でモデル呼び出し → 生のロジット
postprocesssoftmax → config.id2label を引いて LABEL_0POSITIVE に変換

隠蔽しているのは四つ —— Auto クラスによる設定駆動のクラス解決キャッシュ管理前処理・後処理の定型デバイス配置。この四つを手書きすれば AutoTokenizer + AutoModelForXxx と等価になります。降りるべきタイミングは §06.3 に。

PART II実装・最適化レイヤ
06 / Math & API

アーキテクチャの数理と API 設計

細部に見える定数が、実は学習可能性そのものを決めていることがあります。

6.1√dk スケーリング — 消失であって爆発ではない

qi, ki が平均 0・分散 1・独立なら、内積 q·k = Σ qiki は平均 0・分散 dk、標準偏差 √dk になります。dk=64 なら典型スコアは ±8、dk=128 なら ±11。

softmax は入力差が数倍開くと急速に one-hot 化します。そのヤコビアンは diag(p) − ppᵀ で、p が one-hot に近づくと全成分がゼロに漸近する。Attention 重みが事実上ハード選択になった瞬間、WQ, WK へ勾配が流れなくなる。学習初期にこれが起きると、注意の割り当て方そのものを学習できないまま固まります。

Interactive 1
softmax は、どこから「選んで」しまうのか
同じ Q/K 分布から出たスコアを、スケーリングなしとスケーリングありで並べます。dk を上げていくと、左は一本だけが立ち上がって他が消える。そこが勾配の止まる場所です。

PARAMETERS

読み方:棒は 8 トークンに配られた注意の重み。実効エントロピーは 1.0 が一様、0 に近いほど「ほぼ 1 本に決め打ち」=勾配が流れない状態。
効く操作:dk を 4 から上げていってください。BERT / GPT-2 の実際のヘッド次元は 64 です。そこに届く前に、スケーリングなし(黄)が崩れます。緑は dk によらず一定 —— これが「次元数に対するスケール不変性」の意味。

ATTENTION WEIGHTS (8 tokens)

スケーリングなし  
√dk で除算  
本質√dk で割ればスコア分散が 1 に戻り、softmax が感度の高い領域に留まる。やっているのは 次元数に対するスケール不変性の確保 です。

限界も知っておく:これは初期化時の統計を仮定した近似にすぎません。低精度学習では大きな logit が数値問題を起こすため、近年は QK-LayerNorm(Q/K に直接 LayerNorm)や logit soft-capping が併用されます。

6.2位置符号化 — 並列化の代償を払い戻す

Attention は 置換同変(permutation equivariant) です。入力順を入れ替えると出力も同じく入れ替わるだけで、順序そのものは区別できない。語順情報がアーキテクチャから完全に消えているので、外から足し戻す必要があります。

世代方式性質
正弦波(絶対)PE(pos,2i) = sin(pos / 100002i/d)周波数が次元ごとに幾何級数変化。PEpos+k が PEpos線形変換で書けるため、相対距離を線形写像として学習できる。外挿は原理上可能だが実性能は良くない。
学習型絶対位置埋め込みテーブルを学習(BERT / GPT-2)上限が固定(512 / 1024)、外挿不可。
RoPE(現主流)埋め込みを 2 次元ペアに分け、位置 m に応じ mθi 回転Qm·Kn で回転が相殺され、結果が相対位置 m−n にのみ依存する。加算でなく Q/K の回転として作用するため、相対位置性が数学的に保証される。文脈長拡張(NTK-aware, YaRN)が base 値の調整で可能なのも、この構造ゆえ。

別解:ALiBi — 符号化そのものを捨て、Attention スコアに距離比例の負バイアスを直接加算する。

6.3pipeline() から降りるべきとき

観点pipeline()カスタム構成
前処理既定の truncation/padding が黙って適用されるmax_length、動的パディング、slow/fast を明示制御
バッチbatch_size 引数のみ。長さの偏りに無防備length-grouped sampling、bucketing が可能
中間表現取り出せないoutput_hidden_states / output_attentions
後処理id2label 固定、閾値調整不可ロジット段階での温度・閾値・アンサンブルが自由
デバイスdevice / device_map の粗い制御量子化・torch.compile・ONNX と組合せ可

判断基準は一つ:入出力の契約が既定と一致するか。 降りるべきトリガは、スループット最適化が要るとき/埋め込みや Attention 重みを副産物として使うとき/共有エンコーダ上に複数ヘッドを乗せるとき/量子化・コンパイル済みモデルを差し込むとき。

隠れたコスト

pipeline()前処理の失敗を沈黙のうちに吸収します。長文が黙って 512 で切られていて精度が出ない、が典型事故。§22 の D5 と同じ構造です。

07 / Data

データ準備とメモリ

語彙戦略の失敗は、精度ではなく計算量とAPI課金という形で請求されます。

7.1語彙戦略とドメイン適応 — fertility という指標

単語単位の語彙は Zipf 則により裾が無限に伸びて飽和しません。サブワードがやっているのは、語彙サイズ V と系列長 L の積に対するトレードオフの最適化です。

V = 256(バイト単位) → OOV ゼロ、L 爆発 V = ∞ (単語単位) → L 最小、OOV 発生 V ≈ 3〜10 万(サブワード)→ 最適点

ドメイン適応の定量指標は fertility(1 単語あたりの平均トークン数)。これが 2 倍になると何が起きるか:

決定的制約

トークナイザの差し替えは埋め込み行列の行の意味を破壊します。だから事前学習済み重みをそのまま流用できない。ここが選択肢を狭めます。

選択肢内容評価
1. ゼロから事前学習効果は最大コストも最大
2. 語彙拡張ドメイン語を追加し resize_token_embeddings()。新規行はサブワード平均等で初期化 → 継続事前学習現実的な折衷案。多くの実務でこれが最適
3. 既存のままfertility 悪化を許容する

7.2streaming=True の真の違い

訂正 A2
非ストリーミングだと RAM に全展開されるので、巨大データには streaming が必須
非ストリーミングでも datasets は RAM に全展開しません。Apache Arrow でディスク保存し mmap でメモリマップする。100GB でも RAM 使用は数百 MB。真の違いは (a) ディスク容量 と (b) 初回起動時間です。
通常streaming=True
実体Dataset(map-style)IterableDataset
ダウンロード全体を事前取得HTTP range request で逐次
ディスク全量必要ほぼ不要
起動数十分〜数時間即座
len()可能不可
ランダムアクセスds[42]不可(順次のみ)
map()即時実行・結果保存遅延実行
# DataLoader への供給構成 ds = load_dataset("...", split="train", streaming=True) ds = ds.shuffle(seed=42, buffer_size=10_000) # バッファ内シャッフル ds = ds.map(tokenize_fn, batched=True) # 遅延適用 ds = ds.with_format("torch") # 複数プロセス並列時: ノード/ワーカーごとにシャード分割 ds = split_dataset_by_node(ds, rank=rank, world_size=world_size) loader = DataLoader( ds, batch_size=32, num_workers=4, # シャード数の約数にすること collate_fn=DataCollatorWithPadding(tokenizer), # 動的パディング pin_memory=True, )
落とし穴 1 — シャーディング必須

IterableDataset に素朴に num_workers > 1 を指定すると、各ワーカーが同じデータを読んで 重複バッチ が流れます。元データのシャード数(Parquet ファイル数)がワーカー数で割り切れないと不均衡にもなる。

落とし穴 2 — シャッフルは近似

buffer_size 分のリザーバ内でしか混ざりません。元データがラベル順に並んでいれば局所的な偏りが残る。事前のシャードレベルシャッフルが前提です。

落とし穴 3 — len() がない

Trainer では max_steps の明示が必要(num_train_epochs は使えない)。スケジューラの総ステップ数も自分で決めることになります。

落とし穴 4 — 再開が難しい

resume は skip() による読み飛ばししかなく、大規模では非現実的。StatefulDataLoader(torchdata)を検討する。

08 / Training

分散訓練とファインチューニング効率化

VRAM が足りないとき、何が実際に減るのか。ここが最も直感を裏切ります。

8.1LoRA — なぜ 0.4% で足りるのか

h = W₀x + ΔWx = W₀x + (α/r) BAx B ∈ ℝ^(d×r)、A ∈ ℝ^(r×k)、r ≪ min(d,k)

表現力が保たれる根拠は Aghajanyan らの 固有次元(intrinsic dimensionality):事前学習済みモデルの下流適応に必要な更新は、パラメータ空間の極めて低次元な部分空間に収まる。事前学習で汎用表現が獲得済みなので、必要なのは既存特徴の再結合であって新規特徴の獲得ではない、ということです。

帰結内容
効く範囲LoRA が効くのは 適応。新しい知識ドメインの大量注入では、r を上げても full FT に及ばない。
r の実用域8〜64。それ以上上げても伸びないことが多い。
効くのは r よりどのモジュールに適用するか(Q/V のみ vs 全線形層)のほう。近年は全線形層が推奨。
α/r スケーリングr を変えても実効学習率が変わらないための正規化。α = 2r が経験則。
推論時ゼロオーバーヘッドW₀ + (α/r)BA を事前計算して単一行列にマージできる。Prefix Tuning との決定的な差です。マージせず複数アダプタを保持し、リクエスト毎に切り替える運用(S-LoRA 的マルチテナント)も可能。QLoRA は基盤モデルを NF4 で保持しつつ LoRA を bf16 で学習し、65B を 48GB 単一 GPU に収めます。

8.2Prefix / Prompt Tuning との構造的な差

LoRAPrefix / Prompt Tuning
介入対象重み(ΔW)アクティベーション(KV / 入力埋め込み)
作用の性質変換そのものを変える参照可能な文脈を追加する
推論オーバーヘッドゼロ(マージ可能)あり(実効系列長が p 増加)
文脈長消費しない有効文脈を圧迫する
学習安定性高い低い(学習率に敏感、収束が遅い)
スケール依存性小〜大まで安定10B 超で急に効きはじめる
概念的まとめLoRA は「関数を書き換える」=帰納バイアスの変更。Prefix Tuning は「関数を据え置き、入力側に恒常的な条件を注入する」=条件付け。
Prompt Tuning が 10B 超で full FT に追いつくという結果は、「モデルが十分な能力を持てば、必要なのは能力の引き出し方の指定だけ」という解釈を支持します。

実務:安定性と推論効率の両面から LoRA が事実上の標準。Prefix Tuning は、単一基盤モデルに膨大なタスク数を乗せてバッチ内で混在させたい場合に利点が残ります。

8.3混合精度と勾配蓄積 — 「50% 削減」を分解する

1 パラメータあたり(Adam)fp32混合精度
重み42 (fp16)
勾配42 (fp16)
FP32 マスター重み4
Adam m44
Adam v44
16 バイト16 バイト

削減の出所はアクティベーションです。 バックワード用に保持される中間表現は batch × L × d × 層数 に比例し、大規模学習ではメモリの支配項になる。これが半減する。「最大 50%」はアクティベーションが支配的な設定での話です。

Interactive 2
VRAM の内訳を分解する — 何が減って、何が減らないのか
パラメータ関連(青)とアクティベーション(黄)を分けて積み上げます。「混合精度」を切り替えても青は動きません。動かしたいなら LoRA か checkpointing。それが訂正 A3 と A4 の意味です。

MODEL

PRECISION

SAVINGS

MEMORY BREAKDOWN

パラメータ関連
(重み+勾配+optimizer)
アクティベーション
(batch × seq × d × layers)
合計(概算)
パラメータあたり
バイト数
パラメータ関連 アクティベーション
計算の前提:アクティベーションは Megatron-LM の概算式 s·b·h·L·(34 + 5·a·s/h) バイト(bf16、a=h/128 と仮定)、fp32 はその 2 倍。d は params ≈ 12·L·d² から逆算。checkpointing は層境界のみ保持する full recompute を仮定。桁の感覚を掴むための概算であって、実測の代わりにはなりません。実装・カーネル・Flash Attention の有無で変わります。

マスター重みが必要な理由:fp16 の仮数部は 10 ビットしかなく、1.0 + 0.0001 のような微小更新が丸めで消える。だから更新は fp32 マスターコピー上で行い、fp16 にキャストして forward に使います。

指数部仮数部損失スケーリング
fp165 bit(範囲 6×10⁻⁵ 〜 65504)10 bit必須(小勾配が subnormal でアンダーフロー)
bf168 bit(fp32 と同範囲)7 bit不要

A100 以降は bf16 が推奨です。

勾配蓄積 — 擬似的にしか等価でない五点

for i, batch in enumerate(loader): with autocast(dtype=torch.bfloat16): loss = model(**batch).loss / N # ← 正規化が必須 loss.backward() if (i + 1) % N == 0: clip_grad_norm_(model.parameters(), 1.0) # 蓄積完了後に実行 optimizer.step() scheduler.step() optimizer.zero_grad()

PyTorch の .backward()param.grad加算します(上書きしない)。この性質を利用して step()zero_grad() を N ミクロバッチに 1 回だけ呼ぶ。ただし大バッチと等価ではありません。

8.4DeepSpeed ZeRO — 総量ではなく構造が効く

出発点は「DDP は全 GPU に全状態を完全複製している」という無駄です。混合精度 + Adam で 16 バイト/param、うち 12 バイト(マスター重み 4 + m 4 + v 4)がオプティマイザ状態。7B なら 112GB がそれだけで消える。N 台あるなら重複保持する必要はない。

Stage分割対象メモリ/param通信量削減率 (N=64)
DDPなし16
1 (P_os)オプティマイザ状態4 + 12/N≈4×
2 (P_os+g)+ 勾配2 + 14/N≈8×
3 (P_os+g+p)+ パラメータ16/NN に線形
■ Stage 3 の劣化は「総量」ではなく「構造」から来る Stage 1/2 : ステップ末に 大きな塊で 1 回 の通信 Stage 3 : forward/backward の 各層で同期点 → 層数 × 2 回のレイテンシが累積 → prefetch で隠蔽しきれない部分が露出

インターコネクト依存性が強い。 NVLink / InfiniBand なら実用的ですが、10GbE 程度のクラスタでは GPU 利用率が半分以下に落ちることも珍しくありません。Offload はさらに CPU RAM / NVMe へ退避する最後の手段。PCIe(〜32GB/s)は GPU HBM(〜2TB/s)より 2 桁遅く、速度を大きく犠牲にします。

実務の降り順Stage 2 → Stage 2 + gradient checkpointing → Stage 3 → Stage 3 + offload
09 / Compression

モデル圧縮と推論最適化

量子化は「精度を捨てて速くする」ではありません。何を捨てて何を得るかが手法ごとに違います。

9.1量子化

アフィン(非対称) q = round(x / s) + z s = (x_max − x_min) / (2ᵇ − 1), z = −round(x_min / s) 対称 z = 0 固定, s = max|x| / (2^(b−1) − 1)
粒度用途
per-tensor高速だが精度劣化が大きい
per-channel(出力チャネル毎)Transformer 線形層ではほぼ必須
per-group(64〜128 要素毎)4 ビットでの標準

キャリブレーションの役割:重みの分布は静的に既知なので測定不要。問題は入力依存のアクティベーションです。代表データを流して範囲を推定する。

手法内容
Min-Max単一の外れ値でスケールが破壊される
Percentile99.99%tile でクリップ。クリッピング誤差を許容して大多数の分解能を守る
MSE 最小化‖x − Q(x)‖² が最小になるクリップ範囲を探索
KL 最小化(TensorRT)元分布と量子化後分布の情報損失を最小化

要は クリッピング誤差と丸め誤差のトレードオフ最適化。データは 128〜512 サンプル、本番分布を代表するものが必要です。ドメインがずれると劣化します。

LLM 特有の困難 — 系統的外れ値

Dettmers らの知見。6.7B を超えると、特定の隠れ次元に桁違いに大きな外れ値(他の 100 倍以上)が系統的に出現し、per-tensor 量子化を破壊します。

対処発想
LLM.int8()外れ値次元だけ fp16 で計算し、残りを int8 で処理する混合分解
SmoothQuant数学的に等価な変形で、アクティベーションの外れ値を重み側へ移す
GPTQ層ごとに二次情報(ヘッセ行列近似)を使い、あるパラメータの量子化誤差を残りの調整で補償する(OBQ の効率化)
AWQ全重みは等価でないという観察。アクティベーションの大きさで重要度を推定し、上位 1% を保護するチャネル単位スケーリング
NF4(NormalFloat4)

事前学習済み重みが概ね正規分布に従う事実を利用した 分位点量子化。等間隔ではなく標準正規分布の分位点に基づいて 16 個の代表値を配置し、情報理論的に各ビンの期待値が等しくなるようにする。

ブロック単位(64 要素)にスケールを持ち、そのスケール定数自体も 8 ビット化する(double quantization)ことで約 0.37 bit/param を節約する。

訂正 A6
4bit 量子化すれば推論演算が速くなる
NF4 は weight-only 量子化で、計算時には bf16 に逆量子化されます。得られるのはメモリ削減であって演算速度ではない。ただし LLM の生成フェーズはメモリ帯域律速なので、結果として高速化することは多い。int8 の整数演算がハードウェア加速されるのとは性質が異なります

PTQ の限界:4 ビット以下や小規模モデルでは劣化が無視できず、QAT(straight-through estimator で疑似量子化を通す)が必要になります。

9.2知識蒸留 — dark knowledge を運ぶ

ハードラベルは「犬」としか教えません。教師の出力分布「犬 0.9 / オオカミ 0.08 / 猫 0.015 / 車 0.000001」は 誤答クラス間の相対的な類似構造を含んでいる。Hinton の言う dark knowledge です。

pi = exp(zi/T) / Σj exp(zj/T) ℒ = α · T² · KL( pTteacher ‖ pTstudent ) + (1−α) · CE( y, p1student )

T=1 が通常。T>1 で平坦化、T→∞ で一様、T→0 で one-hot。目的は 確率がほぼゼロのクラス間の相対的な大小関係を可視化することです。0.000001 と 0.0000001 の差は T=1 では損失にほぼ寄与しないが、これこそ教師の持つ細かい類似度情報。実用域は T = 2〜10。

実装で見落とされやすい二点

① T² 係数:ソフト損失のロジット勾配は 1/T² に比例して小さくなる。T² を掛けることで、ハード損失との相対重みが T に依存しなくなる。
KLDivLoss は log 確率を要求する:PyTorch の KLDivLoss は入力に log_softmax を通したものを渡す必要がある。典型的なバグ源です。

KL を使う理由:目標が「教師分布の再現」であり、分布間距離の自然な尺度だから。KL(P‖Q) = H(P,Q) − H(P) で H(P) は生徒パラメータに関して定数なので、勾配としては交差エントロピーと同一になります。

発展形DistilBERT は上記に加えて隠れ状態のコサイン類似度損失を併用し、さらに生徒を教師の層から間引いて初期化する(表現空間の整合が学習を容易にする)。FitNets は中間層の特徴マップを合わせ、Attention Transfer は Attention 行列を転写します。

10 / RAG

RAG アーキテクチャ

まず用語を整理しないと議論が噛み合いません。Early / Late Fusion は文献によって逆です。

10.0用語の整理(先に)

「Late Fusion=文字列連結」「Early Fusion=クロスアテンション統合」という説明は、RAG 文献の一般的用法とは逆です。Izacard & Grave の FiD が "Fusion-in-Decoder"(=後段での融合)と命名されている通り、クロスアテンション統合は通常 late / deep fusion 側に分類されます。本稿では機構名で呼び分けます。

本稿の呼称資料での呼称実体
入力連結型Late Fusionプロンプトに検索結果を結合する
周辺化型(なし)RAG-Sequence / RAG-Token。検索スコアで確率的に重み付け
デコーダ融合型Early FusionFiD。文書を個別符号化しクロスアテンションで統合

実務でこの語を使うときは、定義を明示するのが安全です。

10.1入力連結型 — 現行 RAG の圧倒的多数

メリット:実装が単純(プロンプトテンプレートのみ)/生成モデル無改変で閉じた API モデルにも使える/検索器と生成器を独立に差し替え可能/引用元の追跡が容易。

長文脈での課題内容
Lost in the middleLiu ら。文脈中央の情報利用率が両端より有意に低い。文書数を増やすと中央の正解が無視される
計算量O(L²)。10 文書連結で 1 文書の 100 倍。
ノイズ劣化無関係文書の混入で「無し」より精度が下がることがある → リランカー(cross-encoder)が事実上必須
文書間干渉矛盾する文書があってもモデルは矛盾を検出せず、片方を採用しがち。
文脈長の上限会話履歴・システムプロンプト・出力予算との取り合いになる。

10.2周辺化型と FiD

Lewis らのオリジナル RAG は、検索文書を潜在変数 z として扱い明示的に周辺化します。

RAG-Sequence p(y|x) ≈ Σz∈top-k pη(z|x) · Πi pθ(yi | x, z, y<i) k 文書それぞれで系列全体を生成し検索スコアで重み付け統合。1 回答 = 1 文書。 RAG-Token p(y|x) ≈ Πi Σz∈top-k pη(z|x) · pθ(yi | x, z, y<i) トークン毎に周辺化。前半を文書 A、後半を文書 B から取る動的使い分けが可能。

検索スコアの統合機構の実体:pη(z|x) は DPR の質問/文書エンコーダ内積を top-k 上で softmax したもの。k 個の (x, zi) ペアを BART エンコーダに通し、デコーダのクロスアテンションが各文書表現を参照する。「動的に統合される」の実体は、デコーダ出力の対数確率に log pη(z|x) が加算されて周辺化されることです。

設計上の急所 — 微分可能性の非対称性

質問エンコーダには勾配が流れ、検索器が生成タスクに合わせて共同最適化される。しかし文書エンコーダは凍結されます。更新すると FAISS インデックス全体の再構築が必要になるためです。

FiD は周辺化を省き、k 文書を別々にエンコードして、その出力を連結したものにデコーダがクロスアテンションを張る。エンコーダ計算量が O(kℓ²)(ℓ=1 文書長)で済み 100 文書規模までスケールします。入力連結型の O((kℓ)²) に対する明確な優位。

現状評価周辺化型は学習コストと運用複雑さの割に、強力な LLM + 良質なリランカーによる入力連結型への優位を示しにくく、実務採用は限定的。FiD 的発想はロングコンテキスト対応に吸収されつつあります。

10.3BM25 と密ベクトル検索

score(D,Q) = Σqi∈Q IDF(qi) · f(qi,D)(k₁+1) / ( f(qi,D) + k₁(1 − b + b·|D|/avgdl) )

数理的には語彙空間(数十万次元)における疎ベクトルの重み付き内積。転置インデックスで候補文書のみ走査するため高速です。ただし語の一致が前提なので、「腎臓」と「renal」は無関係と判定される。

密検索はバイエンコーダで ℝ⁷⁶⁸ 程度へ写像し、幾何的近さで測ります。対照学習を通じて意味的近接性が距離に埋め込まれる。同義語・言い換え・多言語をまたげる一方、訓練分布外の固有名詞・型番・略語に弱いABC-1234X は BM25 なら確実に当たるが、密ベクトルでは埋もれます。

実務の結論ハイブリッド。両者を並列実行し Reciprocal Rank Fusion(Σr 1/(k + rankr)、k=60)でスコアスケールの違いを気にせず統合する。近年は SPLADE 等の学習型疎表現が中間を埋めています。

10.4コサイン類似度 vs L2 距離 — 静かに壊れる罠

正規化済み(‖a‖=‖b‖=1)のとき ‖a − b‖² = ‖a‖² + ‖b‖² − 2a·b = 2 − 2cos(a,b)

L² 距離は cos の単調減少関数。つまり 正規化済みなら L2 と内積の検索順位は完全に一致します。計算が軽い方(FAISS では METRIC_INNER_PRODUCT)を選べばよい。

正規化していない場合挙動リスク / 用途
内積(IP)ノルムに比例。ノルムの大きいベクトルが常に有利「人気度」「情報量」をノルムに載せる設計なら意図的に使える(推薦の MIPS)。RAG では長い文書がノルムが大きいだけで上位に来る事故
L2ノルム差を距離として拾う。方向が同じでもノルムが違えば遠い

選択基準は埋め込みモデルの訓練目的に従う。これが唯一の正解です。

モデル訓練時の目的推奨
Sentence-BERT, E5, BGE, OpenAI embeddingsコサイン類似度正規化 → IP または L2(等価)
DPR非正規化内積正規化しない IP
画像特徴(一部)ユークリッド距離L2

E5 系のように prefix(query: / passage: )の指定があるモデルは、それも守らないと性能が大幅に落ちます。

実務上の罠

HNSW / IVF は距離計量ごとに別の空間構造を作ります。構築時と検索時で計量が食い違うと、エラーにならず静かに精度だけ劣化する。正規化の有無はパイプライン入口の一箇所に固定してください。

10.5FAISS のインクリメンタル更新

■ IndexIVFFlat の構造 train() : 代表サンプル(30〜256 × n_list 個)に k-means → n_list 個の粗量子化セントロイド = ボロノイ分割を定義 add() : 各ベクトルを最近傍セントロイドに割当て、転置リストに追記するだけ search() : nprobe 個の近傍セルのみ走査 → 追加は再訓練を伴わない。セントロイドは固定のままリストが伸びる。

劣化のメカニズムは分布ドリフトです。訓練時と異なる分布のデータを追加し続けると、① リスト長が不均衡化(特定セルに集中)してそのセルに当たったクエリのレイテンシが悪化し、② 新分布の密な領域にセントロイドが無いため、同じ nprobe でのリコールが落ちる。

対処:リスト長の分散と recall@k を監視し、閾値超過でバックグラウンド再訓練・再構築 → 原子的差し替え(blue-green)。FAISS はオンライン部分再クラスタリングを提供しないため、インデックス全体の再構築が標準です。

データ規模推奨インデックス
〜10 万IndexFlatIP(総当たり、完全精度)
10 万〜100 万IndexHNSWFlat(高速・高リコール、メモリ大)
100 万〜1000 万IVF{n}_HNSW32,Flat
1000 万〜IVF{n}_HNSW32,PQ64(圧縮、精度と引換え)

n_list ≈ 4√N が経験則。

10.6Milvus のシャードとパーティション

両者は目的が異なります。

シャード (Shard)パーティション (Partition)
分割基準主キーのハッシュで自動ユーザーが論理キーで明示(テナント ID、日付、言語)
目的書き込みスループットと容量のスケールアウト検索範囲の枝刈り
機構各シャードが独立の書き込みチャネルを持ち並列取込。検索は scatter-gather(各シャード top-k を統合再ソート)partition_names=["tenant_42"] でそのセグメントのみスキャン。Partition Key でフィールド値に基づく自動振り分けも可
効く場面取込量が多い10 万テナントの SaaS で、1 テナント検索が全体をスキャンしない
■ セグメントのライフサイクル(スケーラビリティの核) Growing : 書込直後。インデックス未構築 → ブルートフォース検索 ↓ サイズ閾値(既定 512MB) Sealed : 封印 ↓ IndexNode がバックグラウンド構築 Indexed : HNSW / IVF 構築済み ↓ Compaction : 小セグメント併合、削除データの物理削除

アーキテクチャの要点はストレージとコンピュートの分離です。実データを S3/MinIO に置き、QueryNode はセグメントをロードして応答するだけのステートレスに近い存在にする。だから水平スケールが容易で、ノード追加時は既存セグメントが再配分(load balance)されます。数十億規模ではこれに DiskANN(メモリに収まらないインデックスを SSD へ)、PQ 圧縮、レプリカによる読取スケールを組み合わせる。

FAISS vs Milvus の境界FAISS は「ライブラリ」、Milvus は「分散システム」。Milvus は CRUD 一貫性・フィルタ付き検索・マルチテナンシー・障害回復を引き受けます。単一ノードで足りるうちは FAISS の方が速く、運用も軽い。この境界の見極めが設計判断です。
11 / Serving

サービング最適化

GPU がメモリ帯域律速であるという事実が、サーバ設計をまるごと決めます。

11.1動的バッチング — 固定長出力の場合

queue: asyncio.Queue = asyncio.Queue() async def batch_worker(): while True: # 1件目はブロッキングで待つ(アイドル時のビジーループ回避) first = await queue.get() batch = [first] deadline = time.monotonic() + MAX_WAIT # 例: 10ms # 2件目以降は締切まで貪欲に集める while len(batch) < MAX_BATCH: timeout = deadline - time.monotonic() if timeout <= 0: break try: batch.append(await asyncio.wait_for(queue.get(), timeout)) except asyncio.TimeoutError: break # GPU 推論はイベントループを塞ぐのでスレッドに逃がす results = await asyncio.to_thread(run_model, [r.payload for r in batch]) for req, res in zip(batch, results): req.future.set_result(res) @app.post("/predict") async def predict(item: Item): fut = asyncio.get_running_loop().create_future() await queue.put(Request(payload=item.text, future=fut)) return await fut # ← 待機中は他のコルーチンが走る
設計点内容
Future による結果返送各ハンドラは自分の Future を待つだけ。await 中にイベントループが解放され新規受付が継続する。ハンドラとワーカーを疎結合にする鍵。
GPU 呼出をループから追い出すrun_model は同期の CPU/GPU バウンド処理。直接呼ぶとループ全体が停止し新規接続を受けられない。asyncio.to_thread / run_in_executor へ(PyTorch は GIL を解放するので有効)。
二つの締切条件MAX_BATCH 到達 or MAX_WAIT 経過。これがレイテンシとスループットの調整ダイヤルMAX_WAIT = 最悪ケースの上乗せ遅延なので、p99 SLA から逆算する。
バックプレッシャasyncio.Queue(maxsize=N) で上限を設け、溢れたら 503。無制限キューは過負荷時にメモリを食い潰し、「誰も待っていないリクエスト」を延々処理する。
長さのばらつきバッチ内は最長にパディングされる。5 トークンと 500 トークンの同居は 100 倍の無駄。長さ帯ごとの別キュー(bucketing)が効く。
クライアント切断await fut 中に切れても計算は続く。request.is_disconnected() の監視やキャンセル伝播を入れる。

11.2生成モデルでは、この設計が不適切になる

訂正 A8
上の FastAPI + asyncio.Queue を LLM 配信にも使う
上記は分類・埋め込みなど 固定長出力 の話です。LLM の自己回帰生成ではバッチ内の生成長がバラバラなため、素朴なバッチングでは 最長の 1 件が終わるまで他が待たされ、GPU が空回りします。

解決は continuous batching(in-flight batching)。トークン生成の各ステップでバッチ構成を組み替えます。完了したシーケンスを即座に抜き、待機中リクエストを即座に入れる。vLLM の PagedAttention は KV キャッシュを OS の仮想メモリのようにページ単位で管理し、断片化を排して同時実行数を大幅に増やします。

結論LLM サービングを自前 FastAPI キューで組むのは車輪の再発明です。vLLM / TGI / TensorRT-LLM / SGLang を使う。手組みが正当化されるのは、埋め込みや分類のような小型固定長モデルの場合に限られます
→ 実測での深掘りは vLLM 推論最適化 へ。
12 / Agents

自律型エージェントと連携基盤

「思考の連鎖」の実体は、while ループと文字列の追記です。

12.1ReAct ループ — 制御の実体

Thought: 現在の株価を知る必要がある Action: search Action Input: Apple 株価 Observation: [ツール実行結果] Thought: 為替レートも必要だ Action: ... ... Thought: 十分な情報が揃った Final Answer: ...
  1. ツール名・説明・出力フォーマットを記述したプロンプトを組む
  2. LLM を呼ぶ。停止シーケンス "Observation:" を指定する(これが決定的。指定しないとモデルはツール結果まで自分で捏造して続ける)
  3. 出力を正規表現でパースし Action / Action Input を抽出
  4. ツール実行 → 結果を Observation: として文字列に追記
  5. 履歴全体を再投入。Final Answer が出るまで反復
構造の正体制御構造は while ループと文字列パースであり、思考の「連鎖」はコンテキストへの追記として実現されている。エージェントに内部状態はなく、状態はすべてプロンプト文字列の中にあります
脆弱性内容
パース失敗LLM がフォーマットを守らない。handle_parsing_errors はエラーを Observation として返し自己修正させるが、確実ではない。
無限ループ同じ Action を繰り返す。max_iterations が必須。
誤り伝播中間の誤った Observation を訂正できない。ステップ数に対して成功率が指数的に減衰する。
コンテキスト膨張履歴が線形に伸び、長いタスクで上限に達する。

現代的な代替:ネイティブ tool calling(構造化 JSON でツール呼出を返す API 機能)がテキストパースの脆さを根本解消し、並列ツール呼出にも対応します。新規実装で ReAct のテキストパース方式を選ぶ理由はほぼありません。ただし Thought → Action → Observation という認知ループの構造自体は不変で、tool calling はその実装層を置き換えただけです。

12.2タスク分解の三つの型

機構強み弱み
インターリーブ型
(ReAct)
計画を立てず各ステップで次の一手を決める環境応答に適応できる大局を見失い迂遠な経路を辿る
Plan-and-ExecutePlanner が全ステップを列挙 → Executor が順次実行計画が明示的で監査可能。実行に安価なモデルを使える初期情報だけで計画するため実行中の発見に対応不可 → 再計画ノードが必要
再帰的分解サブタスクをさらに分解し木構造で解く(Least-to-Most, Tree of Thoughts)探索空間の広い問題に強いコストが跳ね上がる
実務上の要諦分解の粒度を 「1 ステップ = 1 ツール呼び出し」 に揃える。粗すぎると 1 ステップの失敗で全体が破綻し、細かすぎるとステップ数増加による誤り伝播とコストが問題化します。

LangGraph 等の状態機械ベースが主流化した理由は、制御を暗黙のプロンプトではなく明示的なグラフとして書けるからです。分岐条件・リトライ・人間の承認ゲート・チェックポイント再開を、コードとして検証可能な形で表現できる。

12.3RabbitMQ vs Redis — 判断軸は速度ではない

観点RabbitMQ (AMQP)Redis Pub/Sub
永続性キュー・メッセージをディスク永続化。再起動後も残るなし。発行の瞬間に接続中の購読者へ配るだけ。購読者不在なら消滅
配送保証ack を待ち、未 ack で切断なら別コンシューマへ再配送(at-least-once。冪等性は消費側の責任)at-most-once(fire-and-forget)
配送モデル競合コンシューマ(1 メッセージ = 1 コンシューマ、ワークキュー)ブロードキャスト(全購読者が受信)
バックプレッシャprefetch_count で未処理数を制限。滞留がプロデューサへの信号になるなし。遅い購読者は出力バッファ上限で切断される
エラー処理Dead Letter Exchange、TTL、リトライ上限が組込みなし
ルーティングdirect / topic / fanout / headersチャネル名のみ
レイテンシミリ秒オーダーサブミリ秒

Redis Streams がこの対比で重要です。Pub/Sub の欠点を埋める後発機能で、追記型ログ、コンシューマグループ、XACK、pending entries list による再配送、AOF/RDB 永続化を備える。RabbitMQ に近い保証を Redis のレイテンシで得る中間解です。

用途適する技術理由
コーディネータ → ワーカーへのタスク委譲RabbitMQ / Redis Streams消失が許されない。再配送と DLQ が要る
エージェント間の共有スクラッチパッドRedis(KV/Hash)低レイテンシの状態共有
進捗のリアルタイム通知・UI 更新Redis Pub/Sub消失許容。ブロードキャストが自然
分散ロック(同一リソースの排他)Redis(SET NX PX / Redlock)RabbitMQ にこの機能はない
LLM 応答のキャッシュRedis(TTL 付き)
長時間タスクの結果保管Redis / DBブローカーは保管庫ではない
最後に一点LLM エージェントは 1 ステップが数秒〜数十秒かかるため、ブローカー選択がレイテンシのボトルネックになることはまずありません。RabbitMQ の数ミリ秒は誤差です。
だから判断基準は速度ではなく 信頼性・可観測性・運用コストであるべきで、そこでは RabbitMQ の再配送・DLQ・管理 UI が明確に優位。
逆に、単一プロセス内で完結するエージェントに分散メッセージングを持ち込むのは過剰設計。まずインメモリキューから始め、水平スケールが必要になった時点で導入します。
PART III拡張・運用レイヤ
13 / Vision

ViT と CNN — 帰納バイアスの取引

「どちらが優れているか」ではありません。帰納バイアスをどこまで手放すかという連続量の問題です。

CNN には二つの帰納バイアスがアーキテクチャに焼き込まれています。局所性(近傍ピクセルのみ結合)と平行移動同変性(同じフィルタを全位置で共有)。この事前知識のおかげで、比較的少ないデータで学習が成立します。

ViT はこれをほぼ全部捨てます。パッチ分割の中に弱い局所性が残り、位置埋め込みが座標を与えるだけ。代わりに 第 1 層から受容野が画像全体になる。CNN が「局所特徴を積み上げて徐々に大域へ」なのに対し、ViT は最初から任意のパッチ対を直結できます。

■ 捨てた帰納バイアスは、データで買い戻すことになる データ量 ──────────────────────────────→ CNN ████████████████░░░░░░░░░░░░░░░░ バイアスが効く / 天井は低め ViT ░░░░░░░░░░░░████████████████████ 買い戻すまで弱い / 天井は高い ImageNet-1k では ResNet に負け JFT-300M 規模で逆転(Dosovitskiy ら)

DeiT が蒸留トークンを使って ImageNet だけで戦えるようにしたのは、CNN 教師から帰納バイアスを転写する迂回路と読めます(→ §09.2 の蒸留)。データで買えないなら、教師から借りる。

原論文の attention distance 解析が面白い下位層でも一部のヘッドは大域を、他のヘッドは局所を見ています。 つまり ViT は局所性を捨てたのではなく、学習で再獲得している。上位層ではほぼ全ヘッドが大域的になります。アーキテクチャに焼き込むか、データから学ばせるか —— 同じものを別の場所に置いただけ、とも言えます。

現実の解は融合です。Swin Transformer はウィンドウ分割+シフトで局所性を戻しつつ階層構造を持たせ、ConvNeXt は逆に CNN 側を ViT の設計思想で作り直しました。両側から歩み寄っている。

CNNViT
局所性アーキテクチャに内蔵学習で再獲得(下位層の一部ヘッド)
平行移動同変性重み共有で保証なし(位置埋め込みで座標を与えるのみ)
第 1 層の受容野カーネルサイズ分画像全体
必要データ量少なくて済む大規模事前学習が前提
性能の天井低め高い
歩み寄りConvNeXt(ViT 思想で再設計)Swin(ウィンドウ+シフトで局所性を回復)
14 / SentencePiece

SentencePiece — 前処理を廃止するということ

日本語で効く理由は「日本語に特化したから」ではありません。空白依存という前提を取り除いたからです。

訂正 A9
SentencePiece は BPE や WordPiece と並ぶトークナイズアルゴリズム
それらを実装するフレームワークです。中身の学習アルゴリズムとして BPE と Unigram を選べる。比較軸が違うので、BPE / WordPiece / SentencePiece と並列に並べると混乱します。正しくは SentencePiece(BPE)SentencePiece(Unigram)

14.1従来の BPE 実装が隠していた前提

従来の BPE 実装は「空白で単語に分ける」という前処理(pre-tokenization)を暗黙の前提にしていました。これが二つの問題を生みます。

14.2解 — 空白を「ただの 1 文字」にする

SentencePiece の解は、入力を生の Unicode ストリームとして扱い、空白を含めて記号 (U+2581)に置き換えてから学習することです。空白が特別扱いでなくなり、単なる 1 文字になる。

■ 従来の BPE "Hello world" →[空白で分割]→ ["Hello", "world"] → BPE ↑ 連続空白の情報がここで消える ■ SentencePiece "Hello world" → "Hello▁▁world" → BPE / Unigram ↑ 空白も語彙の一部。前処理という工程が存在しない
正確な理解日本語で有効に機能する理由は「日本語に特化した」からではなく、空白依存という前提を取り除いたことが、たまたま空白のない言語で決定的に効いた、と読むのが正確です。§04 で「テキストだけが離散化を経由する例外」と述べましたが、その離散化の入口から言語固有の仮定を追い出した、ということでもあります。

14.3それでも残るトレードオフ

実務での注意

日本語に対して SentencePiece を素で回すと、「という」「ました」のような機能語の連なりが 1 トークンに固まり、形態素境界を無視した分割になりがちです。日本語モデルの多くが MeCab 等で粗く分割してから SentencePiece をかける二段構えを取るのはこのためで、可逆性と言語知識のトレードオフが残ります。前処理を完全に追い出せたわけではない。

この二段構えを採ると、§14.1 で挙げた「辞書とバージョンが再現性の依存先になる」問題が部分的に戻ってきます。何を優先するかの判断であって、正解が一つあるわけではありません。

15 / Adaptation

適応の連続体 — 線形プローブから full FT まで

「ファインチューニング vs 転移学習」は対立概念ではありません。前者は後者の一手法です。

実務で問題になる区分は full fine-tuning vs feature extraction(linear probing) なので、そう読み替えます。

線形プローブfull fine-tuning
更新対象分類ヘッドのみ全パラメータ
必要データ数百〜数千数千〜数万
計算forward のみ(特徴を一度キャッシュすれば以降ほぼ無料)backward が全層
メモリ小(アクティベーション保持が最小限)
上限性能低い高い
破滅的忘却起きない起きうる

原理的なトレードオフの本質は「事前学習表現が下流タスクに対して線形分離可能か」です。ドメインが近ければ(ImageNet → 一般物体)線形プローブで十分。遠ければ(自然画像 → 医用 X 線、一般文書 → 特許明細書)特徴自体を作り直す必要があり、full FT が要る。

15.1見落とされやすい二つの知見

① full FT は分布外性能を「悪化させうる」

Kumar らが示した通り、full FT は事前学習で得た良質な特徴を歪めます(feature distortion)。ID 精度は上がるが OOD 精度は線形プローブに負けることがある
対策として提案された LP-FT(まず線形プローブでヘッドを収束させ、その後に全体を微調整)は、ヘッドがランダムなまま backward すると大きな勾配が下層を破壊する、という機序に基づいています。実務でも効きます。

② この二択は現在では偽の二分法PEFT(→ §08.1)が中間を埋めており、LoRA は「線形プローブ並みのコストで full FT に近い性能」を出します。選択肢は連続体として捉えるべきです。
線形プローブ ── 上位数層のみ解凍 ── LoRA ── full FT 低コスト・低リスク ←──────────────────────→ 高コスト・高性能

実務の順序:線形プローブでベースラインを取り、不足なら LoRA、それでも足りなければ full FT。最初から full FT に行くのは、コストとリスクの両面で非効率です。しかも LP-FT の知見からすると、full FT に行く場合でも「まずヘッドを収束させる」一手を挟むほうが安全になります。

16 / Optimization

学習率と重み減衰 — AdamW が分離したもの

「重みを小さく保つ」という説明は結果としては正しい。ただし L2 正則化と weight decay は Adam では別物です。

16.1学習率 — 効くのはスケジュール

更新式 θ ← θ − η∇ における η の役割は自明ですが、実務で効くのはスケジュールのほうです。

Warmup が必要な理由

Adam の構造にあります。Adam は勾配の二次モーメント v で更新を正規化しますが、学習開始直後は v のサンプル数が少なく推定が不安定で、実効ステップが暴れる。Transformer は特にこれに弱く、warmup なしでは学習が崩壊することが知られています。

線形 warmup(数百〜数千ステップ)→ cosine decay が現在の標準。

16.2重み減衰 — SGD では等価、Adam では等価でない

SGD では両者は等価です。損失に (λ/2)‖θ‖² を足すと勾配に λθ が加わり、更新は θ ← θ − η(∇L + λθ) となる。これは減衰そのものです。

Adam では等価になりません。 L2 項を損失に入れると、その寄与も √v で割られるからです。

Adam + L2(損失に L2 項を入れる) θ ← θ − η · ( + λθ ) / ( √ + ε ) ↑ 正則化項まで √v で割られる AdamW(Loshchilov & Hutter — decoupled) θ ← θ − η · / ( √ + ε ) − ηλθ ↑ 減衰項を適応的スケーリングの外へ出す

つまり Adam + L2 では 勾配の大きいパラメータほど正則化が弱くなる。「大きく動いているパラメータこそ抑えたい」という意図と正反対です。AdamW はこれを分離し、全パラメータに一様な減衰をかけます。Transformer の学習で AdamW が事実上の標準になっているのは、この一点の修正の効果です。

Interactive 3
正則化は、どのパラメータに効いているのか
勾配スケール(√v)の違う 5 つのパラメータ群に、実際にかかる1 ステップあたりの減衰量を並べます。AdamW(緑)は一様Adam + L2(赤)は √v に反比例して、いちばん抑えたいパラメータほど効かなくなります。

PARAMETERS

読み方:棒は 1 ステップで θ から引かれる量(η=1 で正規化)。
効く操作:広がりを上げると赤だけが傾きます。緑は常に水平 —— それが decoupled の意味。広がり 1× まで下げると両者は一致します(=勾配スケールが揃っているとき、両者の差は消える)。

EFFECTIVE DECAY PER STEP

Adam + L2(損失に L2 項) 
AdamW(decoupled) 
実装上の注意 — バイアス項と LayerNorm を除外する

バイアス項と LayerNorm のパラメータには weight decay をかけないのが標準です。LayerNorm の γ を 0 に引っ張るのは正規化の破壊であって正則化ではありません
Hugging Face の Trainer は既定でこれらを除外していますが、自前のオプティマイザ設定では自分でパラメータ群を分ける必要があります

decay, no_decay = [], [] for n, p in model.named_parameters(): if not p.requires_grad: continue (no_decay if (p.ndim < 2 or "bias" in n or "norm" in n) else decay).append(p) optimizer = torch.optim.AdamW([ {"params": decay, "weight_decay": 0.01}, {"params": no_decay, "weight_decay": 0.0}, ], lr=2e-5)
17 / Memory

勾配チェックポインティングと、メモリ手法の全体像

§08.3 で「アクティベーションが訓練メモリの支配項」と述べました。その直接的な解です。

通常の backward は、連鎖律の計算に各層の入力が必要なので、forward 時の全アクティベーションを保持します。層数 n に対しメモリ O(n)

チェックポインティングは、一部の層(チェックポイント)の出力だけ保存し、残りは破棄する。backward でその区間に来たら、直前のチェックポイントから forward を再実行して必要なアクティベーションを復元します。

forward ●───○───○───●───○───○───●───○───○───● 保存 破棄 破棄 保存 破棄 破棄 保存 破棄 破棄 保存 backward 区間に入ったら直前の から forward を再実行して ○ を復元 √n 間隔で置くのが最適 → メモリ O(n) → O(√n) / 追加計算は forward 1 回分(約 +30%
model.gradient_checkpointing_enable() model.config.use_cache = False # ← 必須。KV キャッシュと衝突する

17.1メモリ節約手法の位置づけ

手法削減対象代償
混合精度 (bf16)アクティベーション(半減)ほぼなし
勾配チェックポインティングアクティベーション(O(n) → O(√n))計算 +30%
勾配蓄積減らない(実効バッチを増やすだけ)ステップ時間 N 倍
LoRA勾配・オプティマイザ状態表現力(適応用途なら軽微)
ZeRO 2/3オプティマイザ状態・勾配・パラメータ通信

この表は §08 の対話パネルで実際に動かせます(checkpointing / LoRA / QLoRA のトグル)。

17.2OOM が出たときの診断

OOM が出た — まず「何が支配項か」を切り分ける │ ├─ バッチや系列長を減らすと解決する │ → アクティベーション支配 │ → gradient checkpointing / bf16 / 動的パディング │ └─ モデルをロードしただけで落ちる → パラメータ支配量子化 / ZeRO / LoRA・QLoRA

この切り分けを飛ばして手当たり次第に試すと、効かない手法に時間を使うことになります。§22 の D4「制約が設計を決める」の、最も日常的な適用例です。

18 / Runtime

実行系 — ONNX と Accelerate

どちらも「魔法の高速化ボタン」として誤解されやすい。実際に何をしているかを押さえます。

18.1ONNX — 最適化しているのは ONNX ではない

訂正 A11
ONNX に変換すると推論が最適化されて速くなる
ONNX は計算グラフの交換フォーマットで、それ自体は何も最適化しません。高速化するのは ONNX Runtime(ORT) や TensorRT といった実行エンジンのほうです。

原理は「動的グラフから静的グラフへの転換」です。PyTorch の eager 実行は、Python インタプリタが 1 演算ずつカーネルを起動する。エクスポート時にグラフ全体が確定すると、実行前に全体を見渡した最適化ができるようになります。

最適化内容
① 演算子融合最大の効き。 Conv → BatchNorm → ReLUMatMul → Add → GELU を単一カーネルに統合する。効果の本質は計算量ではなくメモリ往復の削減で、中間結果を HBM に書き戻さずレジスタ/共有メモリに留めたまま次の演算へ渡す。GPU がメモリ帯域律速である以上、ここが支配的(→ §11.1)。Attention 全体を融合したのが FlashAttention だと考えると位置づけが見えます。
② 定数畳み込み推論時に不変な部分計算を事前実行。BatchNorm の畳み込み層への吸収など。
③ メモリプランニンググラフのライフタイム解析でバッファを再利用し、確保・解放のオーバーヘッドを消す。
④ Python / GIL の除去C++ ランタイムで実行するため、インタプリタのディスパッチコストが消える。小さいモデル・小バッチほどこの比率が大きいので、BERT-base のようなサイズで効果が顕著に出る。
適用範囲の注意

効果が大きいのは エンコーダ系(分類・埋め込み・NER)と CPU 推論。ORT の CPU 実行が特に強い。
LLM 生成では話が別です。動的形状・KV キャッシュ・continuous batching が絡み、エクスポート自体が難しい。ここは vLLM / TensorRT-LLM の領域(→ §11.2)。
制御フローや動的形状を含むモデルはエクスポートで壊れやすいので、必ず元モデルと出力を数値比較して検証すること。
そして PyTorch 2.x の torch.compile が同種の最適化(融合・グラフキャプチャ)を PyTorch 内で行うため、ONNX を経由する必然性は以前より下がっています。現在の主価値はむしろ PyTorch 非依存の実行環境への移植性(C++、C#、モバイル、ブラウザ)にある。

18.2Accelerate — 置き換えではなく薄いラッパー

設計思想がここに出ています。Trainer が学習ループごと引き受けるのに対し、Accelerate は ループを手元に残したまま分散化します。

from accelerate import Accelerator accelerator = Accelerator(mixed_precision="bf16", gradient_accumulation_steps=4) model, optimizer, dataloader, scheduler = accelerator.prepare( model, optimizer, dataloader, scheduler ) for batch in dataloader: with accelerator.accumulate(model): loss = model(**batch).loss accelerator.backward(loss) # ← loss.backward() を置換 optimizer.step(); scheduler.step(); optimizer.zero_grad()
prepare() の対象実際にやっていること
model適切なデバイスへ移動 → DistributedDataParallel / FSDP / DeepSpeed でラップ
dataloaderDistributedSampler を注入(各プロセスが異なるシャードを読む)。バッチをデバイスへ自動転送
optimizerAMP の GradScaler を統合したラッパーに置換
backward損失スケーリング、勾配蓄積中の no_sync() を自動処理(→ §08.3 の通信量 N 倍問題)

バックエンド抽象化が核心です。同じコードのまま、accelerate config の設定だけで 単一 GPU / DDP / FSDP / DeepSpeed / TPU / Apple MPS を切り替えられる。ZeRO Stage の変更が YAML の 1 行になる(→ §08.4 の降り順が設定変更だけで試せる)。これが「コードを修正せずに」の実体です。

訂正 A12
Accelerate はハードウェアに合わせて最適なバッチサイズを自動で選んでくれる
提供されるのは find_executable_batch_size デコレータで、OOM が出たらバッチサイズを半分にして再試行する仕組みです。賢く選ぶわけではない。素朴なリトライループの自動化と理解するのが正確です。
なお、学習率をバッチサイズに合わせて調整することはしません。それは利用者の責任です(→ §16.1)。
@find_executable_batch_size(starting_batch_size=64) def train(batch_size): ...
19 / Multimodal

マルチモーダル — 三つの融合パターン

CLIP と Whisper は同じ機構ではありません。ここを混ぜると、何ができて何ができないかを読み違えます。

19.1CLIP — 融合しない。整列させるだけ

訂正 A13
CLIP は異なるデータタイプを単一の Transformer の中で結合している
CLIP は 二つの完全に独立したエンコーダ(ViT と Text Transformer)を持ち、両者の間に情報のやり取りは一切ありません。機構としては融合ゼロのデュアルエンコーダです。

やっているのは、両エンコーダの出力を同じ埋め込み空間に写像し、N 枚の画像と N 個のキャプションの N×N 類似度行列を作って、対角成分(正しいペア)が最大になるよう対照学習することだけです(InfoNCE)。

画像 → ViT → z_i ─┐ ├─ cos 類似度行列(N×N) → 対角を最大化 テキスト → Text → z_t ─┘ ※ 二つのエンコーダの間に接続は無い。融合は一度も起きていない

N 個の負例が同時に効くため、バッチサイズが性能に直結します(原論文は 32,768)。この構造ゆえです。

帰結 — CLIP にできないこと

画像について文章を生成できない。「画像のこの部分がテキストのこの語に対応する」という細かい対応も取れない。できるのは類似度を測ることだけで、ゼロショット分類はクラス名を「a photo of a {class}」に埋めて類似度比較しているにすぎません。

融合しないことが利点になる画像埋め込みを事前計算してベクトル DB に入れておけば、テキストクエリで数十億枚を検索できる。§10.3 のバイエンコーダと同じ構造で、融合しないことがスケーラビリティを生んでいる。RAG の密検索で「クロスエンコーダは強いが全件には使えない」のと、まったく同じトレードオフです。

19.2Whisper — こちらは本当に融合している

Whisper は全く違う機構です。音声を対数メルスペクトログラムにして Audio Encoder に通し、Text Decoder がクロスアテンションでそのエンコーダ出力を参照しながらトークンを自己回帰生成する。標準的な seq2seq です。

ここでは融合が起きています。ただし方向は 音声 → テキストの一方向で、対称ではありません。マルチタスクをタスクトークン(<|transcribe|> / <|translate|> / 言語タグ)で切り替えるのが設計上の特徴です。

19.3三つのパターンとして整理する

パターン機構代表できること
整列のみ独立エンコーダ + 対照学習CLIP, SigLIP検索・ゼロショット分類
クロスアテンション融合一方のエンコーダ出力を他方が参照Whisper, Flamingo, BLIP-2条件付き生成
トークン空間統合非テキストを射影して LLM の入力トークン列に混ぜるLLaVA, GPT-4V, Gemini対話的な推論

三番目が現在の主流です。LLaVA が典型で、CLIP の視覚エンコーダ出力を MLP プロジェクタで LLM の埋め込み次元に写像し、画像をただのトークンとしてテキストと同じ列に並べる。以降は LLM が自己注意で両者を区別なく扱います。

これは §03.4 の直接的な帰結「Transformer が要求するのはベクトルの列と位置情報だけ」という性質から、モダリティの違いは入口の射影で吸収され、本体は何も変えなくてよいことが導かれます。マルチモーダル化がこれほど速く進んだ理由がここにあります。
20 / Accountability

バイアスと説明責任

厄介なのは、バイアスが性能の副作用ではなく、性能そのものの一側面だという点です。

20.1継承の原理 — 四つの経路

経路内容
① 訓練データの分布コーパスは世界の中立な標本ではなく、書く手段と動機を持つ人々の産物です。Web テキストは英語圏・特定層に強く偏る。
② 目的関数の性質次トークン予測は共起の統計を忠実に再現することが最適解。「看護師 → 彼女」の共起が実際に偏っていれば、それを学習するのはモデルの正しい振る舞いであり、バグではない
③ 増幅モデルは統計的偏りを元データより強く再現する傾向が報告されています(Zhao らの bias amplification)。決定境界を鮮明にする方向に最適化が働くため。
④ RLHF による二次的バイアスアノテータ集団の価値観と報酬モデルの選好が新たな偏りを作る。「無害化」自体が特定の文化的規範の反映であるという批判は妥当なものです。
ここが核心②が意味するのは、バイアスの除去と性能の追求が原理的に衝突しうるということです。データの偏りを忠実に写し取る能力こそが、次トークン予測の性能そのものだからです。

20.2緩和手法と、その限界

段階手法効果の限界
データフィルタリング、counterfactual data augmentation("he"↔"she" 入替)、再重み付けフィルタが特定方言・集団の言語を除去し新たな排除を生む
訓練敵対的除去、公平性制約、INLP(部分空間の射影除去)表面的にしか消えない
推論プロンプト制約、self-debiasing、出力フィルタ根本解決ではない
正直に言うべき二つの限界

① デバイアスは「隠している」だけかもしれない。 Gonen & Goldberg は、単語埋め込みのデバイアス(Bolukbasi らの手法)が性別情報を隠しているだけで消していないことを示しました。デバイアス後の埋め込みからも、クラスタリングだけで元の性別が高精度に復元できる。「バイアス方向」という一次元的な定式化が現象を捉え損ねている、ということです。
② 公平性の定義は同時に満たせない。 demographic parity / equalized odds / calibration は数学的に同時充足不可能であることが証明されています(Kleinberg ら)。どれを採るかは技術判断ではなく価値判断で、技術的に決着させられません。

20.3説明責任 — Attention 重みは説明ではない

重要な注意
Attention マップを根拠として提示する
Jain & Wallace の "Attention is not Explanation" が示した通り、Attention 分布を大きく変えても出力がほとんど変わらない敵対的な重み配置を構成できます。相関はあっても因果的根拠ではない。Wiegreffe & Pinter の反論も含め議論は続いていますが、Attention マップを根拠として提示するのは危ういという結論は共有されています。
より妥当な手法内容
勾配ベースIntegrated Gradients、Grad-CAM。公理を満たす帰属を与える
摂動ベースSHAP、LIME。入力を変えて出力の変化を見る(計算コスト大)
反実仮想テスト属性語だけを入れ替えて出力差を測る。バイアス検出には最も直接的
機構解釈可能性SAE による特徴分解、回路解析。研究段階だが最も本質的

評価ベンチマーク:StereoSet、CrowS-Pairs、WinoBias、BOLD、HolisticBias。ただしこれらも英語中心で、日本語を含む多くの言語で十分な評価資源が存在しません。日本語で運用するなら、既製ベンチマークの通過は根拠として弱い、と考えるべきです。

20.4実務としての結論 — 制度で受け止める

技術的緩和には上限があり、残るのはプロセスの問題です。Mitchell らの Model Cards と Gebru らの Datasheets for Datasets が提案した方向 —— 訓練データの由来、評価した集団と結果、意図された用途と意図されない用途を文書化して開示する —— が、現時点で最も実効性のある枠組みです。EU AI Act の高リスク分類、透明性義務、技術文書要件も基本的にこの線に沿っています。

責任の所在を移す設計これは、証明責任を「モデルが公平であること」から「どの範囲で検証したかを開示したこと」へ移す設計です。
知財における明細書の開示要件と構造がよく似ています。対象の完全な性質を保証するのではなく、主張範囲と根拠を公開して外部検証に晒すという形で責任を分配している。技術的に解けない問題を制度で受け止める、標準的なやり方です。
21 / Cheat Sheet

実務判断チートシート

詰まったときに上から降りていくための降順リスト。

訓練 — VRAM が足りない

VRAM が足りない ├─ まず bf16 混合精度 (アクティベーション半減) ├─ 次に gradient checkpointing (アクティベーション大幅減、計算 +30%) ├─ 全パラメータ更新が不要なら LoRA / QLoRA (最も効果が大きい) └─ マルチノードなら ZeRO Stage 2 └─ なお足りない → Stage 3 (インターコネクトを確認) └─ なお足りない → Stage 3 + offload (速度を大幅に犠牲) ※ その前に「何が支配項か」を切り分ける → §17.2 ※ optimizer は AdamW 。bias / LayerNorm は decay 除外 → §16.2

推論 — レイテンシ / コストを下げたい

レイテンシ/コストを下げたい ├─ 生成モデル → vLLM / TGI (continuous batching + PagedAttention) ├─ 分類・埋め込み → 自前の動的バッチング (Future + 締切二条件 + bucketing) ├─ メモリ削減 → NF4 / GPTQ / AWQ (weight-only) ├─ 演算高速化 → int8 (ハードウェア加速)/ONNX / TensorRT └─ 恒久的な軽量化 → 知識蒸留 (訓練コストと引換え)

RAG — 検索品質が出ない

検索品質が出ない ├─ 固有名詞・型番が拾えない → BM25 を併用 (ハイブリッド + RRF) ├─ 上位に無関係文書 → cross-encoder リランカー ├─ 文書数を増やして悪化 → Lost in the middle (件数を絞るか FiD 的構成) ├─ 精度が静かに悪い → 正規化と距離計量の整合 (§10.4 の罠) └─ 追加を続けたら劣化 → IVF の分布ドリフト (再訓練+blue-green 差替え)

インフラ選定 — 境界はどこか

判断境界
FAISS か Milvus か単一ノードで足りる & CRUD 一貫性が不要 → FAISS
RabbitMQ か Redis か消失が許されない配送 → RabbitMQ / Redis Streams。共有状態・通知 → Redis
pipeline() かカスタムか入出力の契約が既定と一致し、スループット要件が緩い → pipeline()
メッセージング導入単一プロセスで完結するうちはインメモリキュー。水平スケール時に導入

数式インデックス

内容
softmax(QKᵀ/√dk)VScaled Dot-Product Attention§03.2
Var(q·k) = dkスケーリングの根拠§06.1
diag(p) − ppᵀsoftmax のヤコビアン(飽和で消失)§06.1
PE(pos,2i) = sin(pos/10000^(2i/d))正弦波位置符号化§06.2
h = W₀x + (α/r)BAxLoRA§08.1
θ ← θ − η·m̂/(√v̂+ε) − ηλθAdamW(decoupled weight decay)§16.2
q = round(x/s) + zアフィン量子化§09.1
pi = exp(zi/T)/Σexp(zj/T)温度付き softmax§09.2
αT²·KL(pTtea‖pTstu) + (1−α)CE蒸留損失§09.2
Σz pη(z|x)Πi pθ(yi|x,z,y<i)RAG-Sequence§10.2
BM25 スコア式疎検索§10.3
‖a−b‖² = 2 − 2cos(a,b)正規化時の L2 と cos の等価性§10.4
Σr 1/(k + rankr)Reciprocal Rank Fusion§10.3
22 / Themes

横断テーマ — 個別技術より寿命の長い五つ

ここまでの全体を貫く原理。技術が入れ替わっても、この五つは残ります。

D1

冗長性の除去 — 同じ発想が階層をまたいで繰り返される

冗長性除去手法
表現語彙の裾サブワード
訓練全 GPU が全状態を複製ZeRO
適応全パラメータを更新LoRA(固有次元の低さ)
表現精度fp32 の情報量量子化
モデル容量教師の過剰な容量蒸留
D2

「等価に見えて等価でない」の検出

性能劣化の多くは、等価だと思い込んだ操作が実は等価でないことから生じます。

  • 勾配蓄積 ≠ 大バッチ(BatchNorm、クリッピング、損失正規化)
  • L2 正則化 ≠ weight decay(Adam では √v で割られ、正則化が勾配に反比例する)
  • 正規化なしの IP ≠ cos(ノルムが順位を汚染する)
  • インデックス構築時の計量 ≠ 検索時の計量(エラーが出ない
  • in-context learning ≠ 学習(何も残らない)
  • CLIP の「マルチモーダル」≠ 融合(独立エンコーダ 2 本。整列させているだけ)
  • ONNX ≠ 最適化エンジン(交換フォーマット。最適化するのは ORT / TensorRT)
  • LLM のバッチ ≠ 分類のバッチ(生成長のばらつき)

共通する厄介さ:どれも例外を投げず、精度やスループットだけが静かに落ちる。

D3

総量ではなく「構造」がボトルネックを決める

  • ZeRO Stage 3:通信「総量」1.5 倍でも、同期点が層ごとに散らばる「構造」が劣化を生む
  • Lost in the middle:文脈の「量」ではなく位置という「構造」
  • 動的バッチング:バッチ「サイズ」より長さ分布の「構造」(bucketing の効き)
  • タスク分解:ステップ「数」より粒度の「構造」
D4

制約が設計を決める — 何がボトルネックかを常に問う

局面律速要因帰結
LLM 生成メモリ帯域バッチ 1 と 32 で時間が変わらない → 動的バッチングが効く
AttentionO(L²)文脈長拡張が難問。fertility 悪化のコストが 2 乗で効く
ZeRO Stage 3インターコネクトNVLink か 10GbE かで実用性が反転する
OffloadPCIe(HBM の 1/100)最後の手段
エージェントLLM 呼出(数秒)ブローカーのミリ秒は誤差 → 判断軸は信頼性
D5

抽象化は「隠すもの」と引き換えに「見えなくなるもの」を作る

  • pipeline() は前処理の失敗を沈黙のうちに吸収する
  • streaming=Truelen() と再開性を奪う
  • LangChain のエージェント抽象は制御フローを暗黙にする(→ LangGraph が明示化に回帰)
  • FAISS のインデックスは距離計量の不整合を報告しない

抽象化を使う判断とは、「何が見えなくなるか」を把握したうえで、それを許容できるかを決めること。

この教材の一番の使い道は、通読ではなく 逆引き です。何かが遅い・精度が出ない・メモリが足りないとき、まず §22 の D2(等価でない操作)と D4(何が律速か)を見る。そこで当たりを付けてから該当節へ降りる。

そして §01 の訂正 12 件 は、そのままレビューのチェックリストになります。混合精度を入れたのに VRAM が減らない、4bit にしたのに速くならない、streaming にしたのに起動が遅い —— どれも「間違った期待」から出発した結果であって、実装のバグではありません。

SOURCE — 三段階の Q&A セッション(① 基礎原理 ② 実装・最適化 ③ 拡張・運用)を三層構造に再編
NOTE — §06 / §08 / §16 の対話パネルは桁と傾向を掴むための概算モデルです。実測の代替にはなりません
AI × 知財 もくもく会