一、まず「名目長」と「実効長」を別の問題として切り分ける
「このモデルは 100 万トークンのコンテキストに対応する」という言葉は度々耳にしますが、厳密に言えば語られているのは能力ではなく容量です。100 万トークンを支えるには、互いに独立した三つの条件が同時に成立している必要があります。第一に、推論フレームワークがこのシークエンスに対して十分な大きさの KV Cache を確保すること。第二に、位置エンコーディングが 100 万の位置でも数値的に崩壊せず、アテンションスコアが潰れないこと。第三に、モデルのアテンション分布そのものが、その長さを跨いでも有効な情報を運んでいることです。三つが同時に 100 万で成立したからといって、モデルが 80 万番目のトークンから数字を一つ取り出せるとは限りません。
そこで、エンジニアリングとしてはより実用的な言い回しとして、コンテキストを二つの量に分けて考えます。名目長(nominal length) とは VRAM と位置エンコーディングが許す上限、すなわち「入れられる」長さです。実効長(effective length) とは、与えられたタスクにおいてモデルの精度がまだ明確に崩壊しない長さ、すなわち「使い切れる」長さです。この 2 つの差分は、2024 年以降のオープンソースモデルでは数倍から数十倍にもなるうえ、定数ではありません。タスクの種類、プロンプトの構造、甚至是質問の作法ひとつで変化します。
1.1 長コンテキスト評価で頻出する三つの錯覚
最初の錯覚は、大海从中の一本の針(needle in a haystack)を長コンテキスト能力の代理指標として扱うことです。このテストは無関係な一文を長文のどこかに差し込み、モデルがそのまま復唱できるかを問うもので、実質的に測れるのは一点だけです。検索型アテンションの位置特定能力がまだ生きているかどうか。それしか測っていません。複数段ホップの推論、段落をまたぐ整合、正確なカウント、そして「散らばった十段落の情報をまとめて一つの質問に答える」といった、本来なら長さを真に喰う処理は、このテストからは一切見えません。より悪いのは、スコアが haystack 自身の妨害の強度に強く依存している点です。背景テキストの意味が高度に関連していれば難易度そのものが跳ね上がり、無関係な百科事典の断片なら難度ははるかに下がります。実装が違えば数字どうしはそもそも比較になりません。
二つ目の錯覚は、Lost in the Middle で観察された U 字の曲線を bug として直そうとすることです。U 字の曲線(冒頭と末尾の情報のほうが中間の情報より使われやすいという性質)は、相対位置エンコーディングまたは回転位置エンコーディングを使うほぼ全てのモデルの共通性質であり、アテンションの減衰が距離とともに単調に減っていくという物理的事実を反映しています。遠い token ほど、そのアテンションスコアは正規化の手前で強く押し潰されます。これはパラメータ調整で消し去れる欠陥ではなく、アテンション機構そのものの本質的な代償です。エンジニアリングとしての対処は、それを消滅させることではなく、情報がアテンション層へ届く経路を変えること――重要な情報を並べ直したり、複製したり、より近い距離で要約を作ったりすることです。
三番目の錯覚は、ウィンドウサイズをモデル能力の唯一の次元として横並びで比較することです。どちらも 128K である 2 つのモデルが、片方は長文要約でほぼ飽和し、もう片方は 4K 以降で明確に退行しながらも 128K まで走ってしまう――後者のほうがむしろ普通です。フレームワークがウィンドウを 1M まで広げたとき、最もよく見られるのは崩壊ではなく、「静かに賢くなくなっていく」ことです。入れ込んだ内容を復唱することはできるのですが、三つの情報をまとめて答える必要が生じた途端、第一のセグメントしか使わなくなります。
1.2 実効長はタスク依存であり、そもそも指示にも依存している
本当に実用的な判断方法は、「実効長はいくらか」と問うことではなく、「自分のタスク分布の上では曲線がどこから落ち始めるか」を問うことです。やり方は泥臭いですが効きます。他はすべて固定し、サンプル長だけを等倍に伸ばして精度曲線を引き、傾きがいつ負になるかを見ます。評価が難しければ手作業の層別テストを一度行うだけでも構いません――同じ種類の質問に対してコンテキスト長を 4K、16K、64K、256K の四段階に分け、それぞれを一度ずつ走らせて、折れ曲がり点を肉眼で見つけます。
ここにも見落とされがちな次元があります。同じモデルでも、実効長はプロンプトの組み立て方によって変わります。関連情報をプロンプトの冒頭と末尾に固める、検索結果の断片を質問との関連度順に並べる、「まず位置を特定してから答えよ」という明示的な指示をモデルに与える――こうしたことはどのアーキテクチャパラメータも変えません。しかしその折れ曲がり点の曲線は明確に動きます。言い換えれば、長コンテキストのアーキテクチャにおいて、プロンプトエンジニアリングとデータ編成は「モデル外の救済策」ではなく、アーキテクチャ能力の一部です。アテンションの疎パターンがどうあるべきかに直接変えてしまうからです。
二、位置エンコーディング:RoPE の外挿のジレンマと補間ファミリ
コンテキストウィンドウを数万から数十万へ引き伸ばしたとき、最初にぶつかる硬い壁は位置エンコーディングです。現代のモデルの大半は回転位置エンコーディング(RoPE)を使います。位置情報を、クエリとキーのベクトルに位置に応じた回転を加えて符号化する方式なので、相対位置を優先する性質が自然に備わっています。問題は、その周波数が学習時の最大長によって決まっていることです。低次元の成分は高い周波数の位置に対応し、減衰が速く周期が短くなります。高次元の成分は低い周波数の位置に対応し、周期が長く、ほぼ位置が変わっても動きません。推論位置を学習長の外へ押し出した瞬間、高周波成分は学習時に一度も経験していないステップ幅で回転します。位相が折り返り、アテンションスコアの意味が失われます。
2.1 なぜ直接の外挿は緩やかに劣化するのではなく崩壊するのか
RoPE の外挿失敗には、よく誤って説明される特徴があります。滑らかに劣化するのではなく、はっきりとした臨界点がある、というのです。モデルのアテンションの重みが突然、一気に偏ります。重みの大半はごく少数の token に集中します。通常は冒頭のいくつかの位置と、直近の短いウィンドウです。中間の token はほとんどアテンションを得られません。このcollapse は perplexity の上では、長文の後半で言語モデルとしての能力が完全に失われ、生成が冒頭を何度も繰り返すという形で現れます。
理由は二つの視点から理解できます。周波数の観点では、高次元の回転ベクトルの周期は 2π です。これを学習長の N 倍の位置まで外挿すると、位相はすでに何周分か回っており、モデルはその位相の区間で一度も学習されていません。アテンションのスケールの観点では、クエリとキーの内積が、学習されていない高周波の振動によって異常に大きな絶対値を生み、スケーリング係数と softmax を通った時点で分布が極端に尖ります。元の RoPE の論文で決まった周波数の次元設計(次元ごとに異なる波長を、2π から 10000·2π まで割り当てる)は学習長を想定して設計されたものであり、外挿時にはこの分布の仮定が完全に崩れます。
さらに見落とされがちな点があります。RoPE の外挿性能とアテンションの疎計化は互いに影響し合っているのです。多くの「外挿失敗」のケースで、実際の原因は二つの問題が重なっていることです。位置エンコーディングがスコアを乱すのが一つの面であり、同時にフルアテンションは長シークエンスそのもの一开始就スコアを正しく配分できない、というもう一つの面です。位置エンコーディングを完璧に解いたとしても、事前学習時の長シークエンスサンプルが十分でなければ、モデルは 100K 以降も「局所を見て、冒頭を重点的に見る」という退行モードしか学びません。1M 対応と称されるモデルの多くが 256K 以降で急激に性能が落ちているのは、このためです。
2.2 補間ファミリ:PI、NTK-aware、YaRN
最も素直な発想は、位置を圧縮することです。Position Interpolation のやり方は、位置インデックスを比率係数 s で割ることで、すべての位置の値を学習時のレンジまで押し戻すことです。すると高周波成分の位相変化が遅くなり、モデルが見てきた分布が再び現れます。代償は非常に直接的です。隣接 token 間の位置差が 1 から 1/s へ変わり、モデルが隣接 token を識別する力が希釈され、短距離でのアテンション解像度が落ちてしまいます。実際には「大体の意味は分かっているが、細部を覚えていない」という形で現れがちです。
NTK-aware スケーリングは、より賢い中間案を提示します。すべての周波数を一様に圧縮するのではなく、高周波の次元だけを比較的強く圧縮し、低周波の次元はほぼ圧縮しないか、ごく僅かに圧縮するだけにするのです。理由は、高周波の次元こそが外挿時に位相の折り返しが最も深刻になる部分であり、低周波の次元はもともと常量のように振る舞って問題を起こさないからです。スケーリングは RoPE の基底周波数 θ を変更することで実現されます。θ を 10000 から 10000·s^(d/(d−2)) のような形にすれば、高周波次元の波長が伸び、低周波次元の波長はほぼ変化しません。これは素朴な補間よりも多くの局所解像度を保持しており、多くの後続研究のデフォルトのベースラインになっています。
YaRN はこの発想をさらに、「周波数帯ごとのブレンド」と「アテンション温度の補正」の組み合わせとして仕上げました。次元を波長によって三種類に分類します。ごく短い波長の次元は、そのまま補間を使います。これらの次元は最も局所的な情報を担っており、圧縮の影響が最小で、しかもアテンション温度の影響と相殺されるからです。中長波長の次元は補間しません。もともと安定しているからです。最も長い波長の次元は、1 の学習長から目標長へ線形に補間します。同時に YaRN は、補間がアテンションのエントロピーを全体として拡大することを指摘し、softmax の直前でクエリベクトルに温度係数 1/s の逆数を掛けて打ち消す必要があると示しました。この組み合わせは 128K オーダーのコンテキストで非常に安定した性能を示し、現在オープンソースコミュニティで最も使われている RoPE 外挿スキームのひとつです。
YaRN の後にあるコンセンサスはこうです。周波数スケーリングが解決するのは「壊れない」ことであって、「使い切る」ことではない。モデルを超長位置上でも数値的に安定させ、基本的な言語能力を保つことはできますが、「遠くにある情報を検索する」という帰納バイアスをモデルに一切注入してはいません。500K より外側の内容を本当に使い切るには、通常、後述のアテンション疎計化や長シークエンスの学習データとの併用が必要です。
2.3 LongRoPE:漸進式拡張と非均一な周波数帯の割り当て
LongRoPE は別の道を進みます。まず「どこまで学習できるか」を解き、そのあとで「どこまで推論出せるか」を解きます。中心的な観察はこうです。学習時のシークエンスそのものを 2M の長さで作っておく、ということです。大半の位置が mask されていても、位置外挿は「より多くの位置で学習する」という普通の課題に変わるのです。こうなると、周波数スケーリングは必需品ではなくなります。スケーリングは、事前学習の重みが初期段階で崩壊しないようするための、過渡的手段にすぎず、長シークエンスのファインチューニングが終わったあとに撤销できます。
LongRoPE の二つ目の観察は、次元ごとに長さへの感度が異なるので、スケーリング率も変えるべきだということです。次元には二分探索のように割り当てを行います。ある一群の次元は、短いシークエンスでは損失が小さく、長いシークエンスでは損失が大きいので、最大のスケーリング係数を割り当てます。別の群は逆で、スケーリングを最小限に抑えます。中には、スケーリングをまったく行わないものもあります。最終的なスケーリングベクトルは、次元空間全体で区分けされた形になります。全次元に一様な係数を掛けるのではなくです。これを漸進式拡張のファインチューニングの流れ(短いものから長いものへ段階的に学習を延ばす)と組み合わせ、論文では LLaMA 系モデルでコンテキストを二百万オーダーに押し上げつつ、passkey 検索のように正確な遠距離での位置特定を要するタスクでも、元のモデルに近い性能を維持できると報告しています。
2025 年以降、変体がいくつか出てきて、注目すべきアイデアがあります。ひとつは、位置外挿とアテンション疎計化を同時に学習する方法です。モデルに疎パターンと外挿の両方を学ばせるものです。もうひとつは、RoPE 以外の手法を採用することです。たとえば回転位置と学習可能な絶対位置を混ぜたり、シーケンスを複数のブロックに切って、ブロックの間に独立した粗い粒度の位置チャンネルを使ったりします。これは interleaved 系のスキームです。Llama 4 の iRoPE は、グローバル位置エンコーディングとローカル位置エンコーディングを交互に並べ、同一層の中に相対尺度の異なる距離信号を二つ存在させます。これらのスキームに共通するのは、単一の周波数分布では「隣接 token の細かい区別」と「百万単位の位置の一意な識別」という二つのニーズを同時に満たせない、という点を認めていることです。
2.4 RoPE 以外:ほかの位置モデリングの系譜は見る価値があるか
RoPE だけが選択肢ではありません。ただ、エンジニアリングの観点では、現時点では依然として最適解です。ほかの系譜がどういう問題を抱えているのかを,简单に触れておく価値があります。線形アテンションバイアス(ALiBi 系)は、距離を線形バイアスとしてそのままアテンションスコアに足す方法で、位置ベクトルをまったく導入しません。そのため本質的に外挿の問題が起きません。しかし代償は超長シークエンスで表面化します。線形バイアスは遠距離の識別度が非常に低く、ある距離を超えると位置どうしのスコア差が無視できます。また、KV Cache の圧縮やページング各種の技術と組み合わせて使うには追加の改造が必要です。ブロック化アテンションではブロックをまたぐバイアス項を正しく扱う必要があるため、RoPE に比べてずっと厄介です。
学習可能な相対位置バイアスは、距離をいくつかのバケットに離散化し、バケットごとに学習可能なスカラーを持たせます。表現力は ALiBi より強いのですが、バケット数には上限があります。外挿時に見たことのないバケットは、外挿するか切り詰める必要があります。つまり問題は RoPE の難点が別のかたちで出直すだけです。位置エンコーディングなし + キャッシュ機構の系譜(Transformer-XL の一派)は、「位置」を token 自体ではなくキャッシュの構造に委ねます。デコード時にキャッシュを任意に伸ばせるのですが、学習時の解像度は学習セグメント長に制限されます。それに現代のバッチ推論フレームワークでは、キャッシュ管理のほうが位置エンコーディング自体よりずっと複雑になります。
本当に注目に値するのは四番目の類型です。つまり位置モデリングの責任をデータ構造とプロンプトの編成に委ねるという考え方です。長い文書には目次と章番号を付けます。長いコードにはファイルパスのツリーとシンボル表を付けます。長い対話にはローリングサマリのアンカーを付けます。こうしたやり方はここ 2 年の実践で非常に有効だと証明されています。しかもモデルの重みには一切触りません。位置エンコーディングとは相補的な関係にあります。エンコーディングが解決するのは「token がシークエンスの何番目にいるか」です。構造が解決するのは「その token が資料全体の意味の階層でどこに位置するか」です。文書理解のようなタスクでは、後者の情報量のほうが前者を上回ることがよくあります。
全体を踏まえると、RoPE が事実上の標準になった理由が分かります。ごく少数の方方案的であり、RoPE はまさにそれだからです。絶対位置の一意性を表現でき、相対距離も自然に表現でき、しかも主要な融合アテンションカーネルや KV 量子化、ページング管理のエコシステムと完全に互換です。他の方式はどれか一つの次元ではより美しいのですが、エンジニアリング上の代償が大きすぎます。
2.4 コード:そのまま組み込める RoPE 周波数スケーリングの実装
以下に示すのは NTK-aware スケーリングの中核実装です。モデル関連の分岐は取り除いてあるので、既存の RoPE 計算経路に直接埋め込めます。ひとつ落とし穴があります。注意してください。スケーリング後の周波数は、実際に使う position_ids と一致していなければなりません。一部の層だけスケーリングし、別の層はしないという構成なら、設定ファイルに明示的に宣言しなければなりません。デフォルト値に任せることはできません。
import torch
import torch.nn as nn
def compute_rope_freqs(dim: int, base: float, scale: float) -> torch.Tensor:
'''NTK-aware 频率缩放:只拉长高频维度的波长,低频维度基本不变。
dim 必须为偶数,且与 head_dim 一致。
'''
assert dim % 2 == 0, 'head_dim 必须是偶数'
# 原始逆频率:exp(-log(base) * 2i / dim)
inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))
# 关键:把 base 抬高,高频分量(inv_freq 大)的波长被拉长更多
new_base = base * (scale ** (dim / (dim - 2)))
inv_freq_scaled = 1.0 / (new_base ** (torch.arange(0, dim, 2).float() / dim))
return inv_freq_scaled
class ScaledRotaryEmbedding(nn.Module):
def __init__(self, dim: int, base: float = 10000.0, scale: float = 32.0):
super().__init__()
self.dim = dim
self.scale = scale
inv_freq = compute_rope_freqs(dim, base, scale)
self.register_buffer('inv_freq', inv_freq, persistent=False)
self._cached_len = 0
def forward(self, x: torch.Tensor, offset: int = 0) -> torch.Tensor:
'''x: (batch, seq, dim)。返回同形状的 cos / sin,供 rotate_half 使用。'''
seq = x.shape[1]
if seq + offset > self._cached_len:
t = torch.arange(offset, offset + seq, device=x.device, dtype=torch.float32)
freqs = torch.outer(t, self.inv_freq.to(x.device))
emb = torch.cat((freqs, freqs), dim=-1)
self._cache_cos = emb.cos()[None, None, :, :]
self._cache_sin = emb.sin()[None, None, :, :]
self._cached_len = offset + seq
sl = slice(offset, offset + seq)
return self._cache_cos[:, :, sl, :], self._cache_sin[:, :, sl, :]
# 在注意力里使用:
# cos, sin = rope(q)
# q_embed = q * cos + rotate_half(q) * sin
# k_embed = k * cos + rotate_half(k) * sin
# 注意 attention score 的缩放 1/sqrt(d) 不变,YaRN 那类方案还要额外
# 对 q 做 1/(t * s) 的温度补偿(t 是缩放后的注意力温度)。
三、注意力計算そのもの:二乗複雑性と二つのメモリウォール
位置エンコーディングが片づいたら、次に待ち構えているのはもっと硬い問題です。計算が回らない、メモリにも収まらない。ここには独立した二つのウォールがあり、両者を混同すると手法の選択を誤ります。
3.1 第一のウォール:計算量の二乗項
注意度のスコアリングと加重集約の計算量は O(N²·d) です。N が 128K から 1M へ増えるとき、二乗項は約 58 倍になります。この倍率は穏やかに見えますが、「1 トークンあたりの線形項」の上に重なると急に厳しくなります。モデルのパラメータ数が FLOPs の下限(おおむね 2·パラメータ数·トークン数)を決めるこの部分は線形です。一方、attention 項は大きな N では逆に支配的になります。7B モデルで 1M トークンの prefill の計算量下限は 1e16 FLOPs のスケールで、高性能カードに数十 TFLOPS の実効スループットをフルに出しても数十秒はかかります。ただしこれは理論上の下限にすぎず、実際にはメモリアクセスとスケジューリングも勘定に入れなければなりません。さらに大きなモデルでは、1M トークンの prefill が「許容範囲」から「分単位」に落ちるのが普通です。
この 2 年間、長コンテキスト対応の inference framework では attention sparsification が「任意の最適化」から「既定のルート」に変わりました。
3.2 第二のウォール:N×N の注意度行列のメモリピーク
第二のウォールはメモリです。しかもその形は計算量より直感に反しています。素朴な実装は N×N の注意度行列(加えて softmax の中間結果)をメモリ上に実体化するため、メモリ使用量は O(N²) になります。N = 1M ではこの行列の要素数は 1e12 で、1 要素あたり 1 バイトしか使わないとしても TB 级に達します。そもそも存在し得ない数字です。FlashAttention 系の融合カーネルの価値はまさにここにあります。ブロッキング計算とオンライン softmax によって N×N 行列を不完全な実体化状態から消し、メモリを O(N) まで落とせます。計算量の漸近的上界は変わりません。それが解いているのはメモリウォールであって、演算量ウォールではない。1M トークンの attention FLOPs は一つも減っていません。
したがって長コンテキストでは、この二つは別々に勘定しなければなりません。FlashAttention とその後の変種(KV Cache をページ管理する attention 変種、疎ブロック対応の attention カーネル、低ビット KV の dequantize 融合カーネル)は「メモリウォール」を TB 级から GB 级まで落としますが、「演算量ウォール」を実際に下げるのは attention sparsification だけです。多くのチームが長コンテキスト案件で前者だけを行い、prefill 時間が長さの二乗に比例して増えることに気づいて、結局ユーザー体験は改善しませんでした。
3.3 MLA:別の方向から KV の次元を下げる
事後圧縮とは別の方向として、DeepSeek 系が採用している Multi-head Latent Attention は「最初からそこまで多くの KV ヘッドが要らない」という道を取ります。各 token の K と V をまず共有の低ランク射影で小さな潜ベクトルに圧縮して Cache に格納し、注意力計算時にその token が持つ少数の成分で潜ベクトルを各ヘッドの K/V 空間へ上射影します。全ヘッドが同じ潜ベクトルを共有するため、KV Cache の次元は「ヘッド数 × ヘッド次元 × 2」から「潜次元 + 上射影に必要な小成分」へと下がります。潜次元は実務上数百程度が標準で、従来の GQA 構成では桁違いに上上万に達する次元と比べて数十倍の減少になります。
MLA の代償は 2 つあり、どちらも明確にしておく価値があります。第一に、上射影にはその token 自身の K 成分が必要です。つまり attention が一部の位置にしかアクセスしなくても、アクセスされた token の潜ベクトルを逆射影して戻す必要があり、計算上の追加コストがかかります。第二に、MLA と PagedAttention のような「ブロック単位で KV を管理する」方式は、エンジニアリング上で互いに張り合います。PagedAttention は KV のレイアウトが規則的でページ単位に copy できることを要求しますが、MLA の潜ベクトルレイアウトは一括読み書きに適しています。幸い後続の absorb(吸収)系の変種がこの問題の一部を解きました。上射影行列を前もってクエリ射影と出力射影に吸収させることで、推論時に潜ベクトルを完全にデコードせず、潜空間上で直接 attention を行えるようにする——これは長コンテキスト配備において非常に価値のあるエンジニアリング上の細目です。
この 2 つの方向を並べて比較すると、より完全な判断が得られます。GQA/MQA は KV ヘッド数を下げ、MLA は KV の次元を下げ、量子化は KV のビット幅を下げ、階層型ストレージは KV が置かれる階層を下げ、疎性化は KV の個数を下げ、eviction 戦略は常駐 KV の個数を下げます。6 つの手段は直交していて、長コンテキスト系システムでは通常 3〜4 つを同時に使うことになります。
3.4 チェーン全体のメモリ予算を洗い出す
長コンテキスト案件のメモリ予算は、KV Cache の 1 項目だけを見ていてはいけません。80GB のカード 1 枚に実際に乗るのは、モデル重み(量子化していなければパラメータ数×2 バイト)、KV Cache(並列度とコンテキスト長に比例して膨張)、活性化値(prefill 段階での瞬間的なピークで、シーケンス長に比例するが定数は小さい)、そしてフレームワーク自体のフラグメンテーションとリザーブです。この勘定をしないと、「KV は計算上ちょうど収まるのに、走らせたら OOM」という状況が普通に発生します。
予算表の正しい書き方は 5 列です。シナリオ(典型的な並列度とコンテキスト長)、重み使用量、KV 使用量、活性化ピーク、そしてフラグメンテーションのリザーブ——この中でどれがボトルネックかを見ます。経験的な判断としては、GQA 構成ではコンテキストが 32K を超えたあたりから KV Cache が最大の列になりやすく、GQA のない旧アーキテクチャでは 8K でもすでにボトルネックです。これも、アーキテクチャ層での次元削減(GQA、MQA、MLA)が推論層のあらゆる圧縮手法より優先される理由です——ソースのその列を小さくするのであり、圧縮は結果にパッチを当てるだけだからです。
もう一つ見落とされがちな列が活性化ピークです。prefill 段階の瞬間的な活性化値は大きくはないものの、batch 内のシーケンス本数と正相関します。chunked prefill ならピークは chunk サイズだけに依存します。分塊をせずに長いプロンプトを一度に処理しようとすると、活性化ピークが直接シーケンス長に結びついて、ある実装では KV Cache 自体を超えることすらあります。これが chunked prefill がスケジューリングとは別の、二つ目の理由です——同時にメモリ保護策でもあります。
四、attention sparsification:二乗項を根本から切り落とす
attention sparsification の完全な系譜は、構造仮定の強弱で見ると 4 層に分けられます。固定疎(sliding window)、構造化疎(どの位置が重要かを人が明示的に指定したもの。vertical-slash など)、検索式疎(安価な近似演算子で動的に重要な位置を選ぶ)、学習型疎(小さなモデルで重要なブロックを予測する)。後ろに行くほど効果は上がりますが、エンジニアリング上の複雑さと失敗リスクも同時に増えます。
4.1 sliding window と StreamingLLM の attention sink
最も単純な疎化は sliding window です。各 token は直近 W トークンしか見ません。これで attention は O(N²) から O(N·W) に下がり、W は定数なので計算量は線形になります。しかし素朴な sliding window には致命的な問題があります。モデルが退行してしまうのです。原因は、causal full attention で訓練しているため、モデルの注意度分布には局所パターン以外にも重要なグローバルパターンが含まれていることです——attention sink です。大量の注意質量が無条件にシーケンス冒頭の数個の特別な token へ流入し、「何の情報も持たないゴミ箱」として機能します。こうすることで softmax の分子と分母に安定した逃げ道が生まれるのです。
StreamingLLM の重要な貢献は、この観測を設計原則に変えたことです。冒頭の若干の sink token と直近の窓だけを残せば、極めて小さい KV Cache 予算でも困惑度を安定に保てます。論文は sink を数個と直近 2〜3 千 token という予算でも、安定性を百万トークン級まで延ばせたと報告しています。つまり、「重要でない位置」は均一に分布していない——それらは冒頭の数個の位置に大量に集中している——ということです。冒頭をゴミ箱として扱うことは、モデルがすでに学習した挙動に合っているだけでなく、中間区間全体を丸ごと省けます。
このテクニックの価値は極めて安価なことにあります。訓練もインデクサーも不要で、純粋に attention mask の変更だけで機能し、しかもほぼ無損失です。すべての長コンテキスト方式の土台になります。ただし上限も明確で、sliding window は遠くの明示的な内容を見られず、sink は何の意味も保持できません。つまり StreamingLLM が解くのは「壊れない」ことだけで、「300K より前のあの一節を覚えていられる」ことまでは解けません。
4.2 構造化疎:MInference と vertical-slash 仮説
モデルが訓練の時点から「特定の位置パターンが重要」という帰納的バイアスに触れているなら、疎トポロジーを人が明示的に指定できます。MInference の手順は、まず少数の長コンテキストサンプルで注意度マップをサンプリングし、いくつかの非常に安定したパターンを観察することです。vertical column パターン(現在の query の注意度が特定の key 位置に集束し、その key 位置がシーケンス全体を貫く。歴史的な sink に相当します)、slash パターン(局所的な対角線に沿う、典型的な sliding window)、vertical-slash パターン(分割された斜線で、より広い範囲をカバー)、そして intra-block パターンです。ほぼすべてのヘッドが固定でこの 4 つのいずれかに属します。
そこで疎化は「各ヘッドにパターンを 1 つずつ割り当ててスパンを 1 つ与える」ことになります。推論時はパターンに従って attention を計算します。この方式は百万トークン級での性能が公式およびコミュニティで繰り返し検証されており、現時点で「構造化疎 + 学習コスト不要」のルートを代表するアプローチの一つです。訓練不要、デプロイ時の変更は mask 改造のみで、長文理解タスクにおける精度損失は通常小さい。その限界もはっきりしています——パターンは前提固定なので、実際の入力の重要度分布が前提パターンと著しく噛み合わない場合(例えば構造が非常に規則的なコードと、役割が交互に現れる台本の比較)、固定されたトポロジーは予算を無駄にするか、肝心な位置を落とすかになります。
4.3 検索式疎と学習型疎:見るべき箇所はモデル自身に決めさせる
検索式疎の考え方を整理するとこうです。まず各 query の先頭の数次元(時には 1 次元だけ)で全 key に対する粗い内積を計算し、top-k の候補を選び、候補上だけで完全な attention を行います。粗い篩いの計算量は O(N·d_kv の小さな射影) で、完全な attention は k 個の位置上でしか行われず、全体の計算量はほぼ O(N·k + N) になります。ここでの本質は「全次元の厳密なマッチングを、安価で高いリコールを持つ低次元近似インデックスに置き換える」ということで、embedding retrieval の世界における IVF/HNSW と同じ発想です。わずかなリコール損失と引き換えに 10 倍以上の速度を得る、という取引です。
学習型疎は「どれを選ぶか」を小さなモデルに任せます。SpargeAttn は軽量なトークンセレクターで各 query から各 KV ブロックの重要度スコアを予測し、高スコアブロックにだけ疎 attention を行い、選択されなかったブロックの注意度は推定値で補償して softmax の正規化が破綻しないようにしています。SeerAttention は非常に小さなゲートネットワーク(各層およそ 100 万パラメータ規模)で層ごとに attention 予算を決め、ブロックレベルの重要度を予測し、層をまたいで段階的に収束させます。前方の層はごく少数のブロックだけを見て、後方の層で少しずつ解放していく、これで総コストを大幅に削減できます。DeepSeek が V3.2 で導入した DSA も同じ考え方です。軽量なインデクサが各 query の top-k 候補位置を計算し、sliding window を組み合わせて局所情報が失われないようにする、公式の示す数字では、長コンテキストで attention 関連のコストを約 3 分の 1 のスケールまで圧縮したと報告されています。
検索式および学習型の疎化の最大の利点は「入力適応性」です。同じ予算でもコードタスクと対話タスクの注意度分布はまったく異なり、動的選択なら自動的に適応できます。ただし 2 つのリスクがあり、正視する必要があります。第一にリコール失敗です。粗い篩いが本当に重要な位置を取りこぼすと、その回の attention の結果そのものが誤りになり、出力からは検知できません。そのためこの種の方式では通常、sliding window や sink を安全網として残し、局所と冒頭は常に予算内に収まるようにします。第二に粗い篩いの安定性です。低次元内積の top-k は数値的に量子化誤差や fp8/fp16 の精度に非常に敏感で、batch ごとに選択結果が揺れることがあり、対話シーンでは出力不安定として現れます。
4.4 ヘッドごとの予算配分:ヘッドによって必要量が桁違いに違う
DuoAttention は非常に有益な視点を提供しました。すべての attention ヘッドが同量の KV Cache を必要とするわけではない、というものです。実験では、多くのヘッドがごく短い窓でしか使っていない(local head)一方、少数のヘッドはコンテキスト全体へのアクセスを要する(retrieval head)ことが示されています。全ヘッドに平均して予算を配ると、少数の retrieval head には過剰に支払い、多くの local head には無駄な支払い支払いことにします。より妥当なのは、まずオフラインで各ヘッドの KV 使用分布を分析し(長コンテキストサンプルを一度流して、各ヘッドがどの程度の窓で注意質量の 95% を覆うかを集計する)、local head には小さな予算を、retrieval head には大きな予算を割り当てたうえで、推論時にそのヘッドがどちらの型かを判断する軽量な分類器を訓練することです。論文が示す数字では、KV Cache のサイズを約 4 割まで下げても精度はほぼ変わりません。
この考え方の価値はメモリ節約だけではありません。実際には「attention の挙動でヘッドを階層化する」というアーキテクチャ的な視点であり、MLA の「次元で削減する」、疎化の「層ごとに予算を配分する」という同じ方法論の異なる切り口です。層・ヘッド・次元によって、長コンテキストへの感度がまったく違う。均等にリソースを配分することが最大の浪費源です。2026 年に増えたモデル(Kimi Linear、MiniMax 系、Gemma 3n など)のアーキテクチャ説明にも、同様の階層型ハイブリッド attention 設計が現れています。一部の層は linear attention や圧縮状態空間を通り、一部の層は圧縮された sliding window attention を通り、一定の圧縮率で KV の必要量を削減します。公式の示す圧縮率は 5:1 から 7:1 のスケールです。それらの共通点は、長コンテキストではほとんどの層が全量 attention を必要としないことを認めている、ということです。
4.5 階層的な疎化率:予算をどう切り分けるか
疎化がエンジニアリングに落地すると、最終的に答えるべきは予算配分の問題です。総予算が決まっているとき、それを各層にどう切り分けるか。経験的に比較的安定した原則が 3 つあります。第一に、浅い層は狭く、深い層は広く。浅い層の attention ヘッドは主に語彙照合と局所的な構文処理を行うため、必要な文脈窓はごく小さい。深い層は抽象化と検索を行い、段落をまたぐ情報の集約が必要です。直感に反するのに重要な現象として、深い層は広い窓と強い段落跨ぎ検索能力を同時に必要とし、浅い層はほぼ局所だけを行う——2025 年以降に階層型 attention アーキテクチャが集中して現れた理由がまさにこれです。第二に、sink と sliding window に予算を全部食べさせない。sink は 4〜16 トークンあれば十分で、sliding window は多くのタスクで 512〜2048 トークンで既に質向上の大半をカバーします。両者の合計は総予算の 3 分の 1 以内に収め、残りを動的検索に回すべきです。第三に、入力タイプで疎化設定を切り替える。コード・対話・長い要約で必要な疎トポロジーはまったく違うため、サービスが入力タイプを識別できるなら、2 つのプリセットを使うほうが 1 つの折衷設定より得策です。
予算の決め方は実測駆動です。まず緩やかな設定(例:sliding window + sink のみ)をベースラインとして、動的検索の予算を段階的に増やし、「予算 vs 下流タスクスコア」のカーブを描き、傾きが明らかに平らになる位置を見つけます。経験的にはこの折れ曲がり点は、想定していたよりずっと低い位置に来ます。総予算が訓練長の 5% から 10% に達した時点で、収穫の 8 割以上を獲得している場合が多く、越えると限界便益は急速に減る一方、メモリとレイテンシのコストは線形に増えていきます。
4.6 コード:検索式疎 attention の最小実装
以下の実装は検索式疎の中心的なロジックを示しています。粗い篩いには射影の 1 次元だけを使い、KV 位置から top-k を選び、選ばれた位置上で完全な attention を行います。flash/ページングの最適化は一切行っていませんが、構造は完結しているので、小規模モデル上で疎度が精度に与える影響を試すためにそのまま動かせます。
import torch
import torch.nn.functional as F
def sparse_topk_attention(q, k, v, sink=4, window=256, k_ratio=0.02):
'''q: (B, H, N, D) k/v: (B, H_kv, N, D),GQA 下用 repeat_kv 展开
返回与全注意力同形状的输出。稀疏预算 = sink + window + topk。
'''
B, H, N, D = q.shape
k = k.repeat_interleave(H // k.shape[1], dim=1)
v = v.repeat_interleave(H // v.shape[1], dim=1)
# 预算分配:固定部分(sink + 局部窗口)永远保留
fixed = sink + window
# 动态部分:用 q 的第一维做粗筛,取 topk 个最相关的 key 位置
coarse = torch.einsum('bhid,bhjd->bhij', q[..., :1], k[..., :1]) # (B,H,N,N)
topk = max(1, int(N * k_ratio))
topk = min(topk, N - fixed)
_, idx = torch.topk(coarse, topk, dim=-1) # 每个 query 各选自己的位置
# 组装稀疏索引集合:[0:sink] ∪ [N-window:N] ∪ topk 命中
dev = q.device
s = torch.arange(sink, device=dev).expand(B, H, N, sink)
w = (torch.arange(N - window, N, device=dev)
.view(1, 1, 1, window).expand(B, H, N, window))
pos = torch.cat([s, w, idx], dim=-1) # (B,H,N,K)
K = pos.shape[-1]
# gather 出被选中的 k/v,并重算精确注意力分数
k_sel = torch.gather(
k.unsqueeze(2).expand(B, H, N, H, D), 3,
pos.unsqueeze(-1).expand(B, H, N, K, D)) # (B,H,N,K,D)
v_sel = v.unsqueeze(2).expand(B, H, N, H, D).gather(
3, pos.unsqueeze(-1).expand(B, H, N, K, D))
scores = torch.einsum('bhnid,bhnkd->bhnik', q, k_sel) / (D ** 0.5)
out = torch.einsum('bhnik,bhnkd->bhnid', scores.softmax(-1), v_sel)
return out
# 要点:
# 1) coarse 矩阵虽然是 (B,H,N,N),但它的乘加深度只有 1 维,
# 在真实实现里应该直接对 K 做投影降维后再算,永远不要物化 N×N 的 fp32。
# 2) 真实实现要用 gather_kernel / paged KV 来做,避免上面的 expand 显存爆炸。
# 3) 生产上通常和滑窗、sink 融合成一个固定模式 + 动态模式的混合预算表。五、KV Cache のライフサイクル:まず勘定、その後に圧縮
KV Cache の最適化は、長コンテキストのエンジニアリングの中で最も地味な作業の部類に属しますが、一方で確実にリターンが得られる部分です。そのライフサイクルはおおよそ、書き込み(prefill で生成)→ 常駐(decode 中に繰り返し読まれる)→ 圧縮/移動(量子化、階層化、スワップアウト)→ 淘汰(コンテキストが予算を超えたら一部を捨てる)。4 つの工程は 4 つのまったく異なる技術に対応します。
5.1 メモリ試算:まず勘定を確定してから手法を選ぶ
KV Cache のバイト数には、ごく素朴な式があります。バイト数 = 2 × 層数 × KV ヘッド数 × ヘッド次元 × dtype のバイト数 × シーケンス長(先頭の 2 は K と V が 1 個ずつあることを意味します)。この式で実際の構成を数えてみると、結論は往々にして気持ちの悪いものになります。
- 32 層、KV ヘッド 32 個、ヘッド次元 128、fp16 の 7B モデル:1 トークンあたり約 512 KiB。128K コンテキストなら 1 シーケンスで 64 GiB となり、すでに単卡の容量を超えています。
- 80 層、GQA で KV ヘッド 8 個まで削減、ヘッド次元 128、fp16 の 70B モデル:1 トークンあたり約 320 KiB。128K コンテキストなら 1 シーケンスでちょうど 40 GiB。しかもこれはまったく圧縮していない素の構成です。
- 上の 2 行目を 1M コンテキストまで伸ばすと、1 シーケンスで 320 GiB になります。80GB のカード 1 枚をすべて KV に使っても、1 シーケンスの 4 分の 1 しか入りません。
この勘定表から直接導けるエンジニアリング上の結論が 3 つあり、それは長コンテキストシステムの 3 本の基本ルートでもあります。第一に次元を下げる(GQA/MQA/MLA。ソースで KV のヘッド数か次元を減らす)、第二にビット幅を下げる(8bit/4bit/2bit に量子化する。ビット幅を半分にするのは線形の収穫)、第三に個数を減らす(圧縮/淘汰/疎化。一部の token の KV だけを残す)。この 3 本のルートは互いに独立であり、互いに競合します。優先順位は、あなたのボトルネックがメモリ容量か、メモリ帯域か、演算量かで決まります。
5.2 量子化:ビット幅を半分にするのが一番安い収穫
KV 量子化の考え方は重み量子化に似ていますが、決定的な違いが 1 つあります。KV の量子化誤差は長シーケンス上で attention の softmax によって増幅されます。重みの誤差は固定ですが、KV の誤差は 몇 個の token が同じ softmax の正規化に参加したかによって決まります。シーケンスが長いほど競合する位置が多く、量子化誤差で撹乱された key 値は正規化後に不釣り合いな注意質量を勝ち取ってしまう。初期の KV 量子化方式が「短いタスクでは無損失だが、長いタスクでは明らかに劣化する」という現象を見せたのはこのためです。
KIVI が提案した重要な観察は、Key と Value で異なる量子化戦略を使うべきだという点です。Key ベクトルの外れ値は少数の特定のチャンネルに集中します(位置と方向の鋭い情報を持たせているため)。したがって Key はチャンネル単位の量子化に向いています。一方、Value ベクトルの外れ値は少数の特定の token に集中します(少数の token が情報量の大部分を背負っているため)。したがって Value は token 単位の量子化に向いています。この「横と縦」の分け方は、同じビット幅でも統一的な戦略より明確に優劣します。後続の版ではビット幅をさらに 6bit まで詰め、attention 出力の重要度で重み付けしたグループ量子化を導入し、公式の示す数字ではほぼ無損失の条件のもとで KV Cache を約 4 割まで圧縮しています。
エンジニアリング上で 2 点、必ず押さえておく必要があります。第一に、量子化はモデル全体で統一ではなく、ヘッド単位でグループ化しなければならない。ヘッドや層によって KV の分布差は非常に大きく、統一した scale では分散の大きいヘッドの精度が崩壊します。第二に、dequantize は attention カーネルに融合しなければならない。毎 decode ステップで KV Cache 全体を fp16 に戻してから attention を計算するなら、節約したメモリを帯域に還だけになり、収穫はゼロです。実カーネルは KV の読み出しと同時に dequantize と内積を完了し、可能なら int8/fp16 の混合精度演算ユニットでそのまま計算する必要があります。
5.3 階層型ストレージと swap out:KV をメモリではなくデータとして扱う
KV Cache は本質的に「各 token 分のベクトルの塊」です。CPU メモリ、SSD、さらにオブジェクトストレージに置き、必要なときに swap in することまでできます。このルートの歴史は、かなり初期の CPU offload 推論までさかのぼります。その頃は全層を offload して token 単位で出し入れし、帯域が PCIe で完全に埋まり、throughput は実用にならない水準まで落ちていました。
後の階層型設計は、考え方に 2 つの重要な改良を加えました。第一に、「ほとんど読まれない」部分だけを evict することです。prefill が生成した KV のうち、直近に生成された token は次の各ステップで必ず読まれるのでメモリ上に残す必要があります。一方、かなり前の履歴 token は生成フェーズでは 1 回だけ読まれる(次の token を生成するとき)ので、これらを evict しても throughput への影響は相対的に小さい。KTransformers 系の設計はまさにこの観察に基づいています。KV Cache を CPU メモリに置き、最適化した token 単位の疎アクセス経路で PCIe の往復を減らす,重要なのは「全部を evict すること」ではなく、「深い層だけを evict して疎な層だけをアクセスする」ことにあります。
第二に、階層型 attention です。ShadowKV 系の設計では、低層に完全だが小さな sliding window KV(直近数百 token)を残し、高層にはごく少数の「shadow token」だけを残します(重要度スコアで選ばれた少量の履歴 token)。こうすると深層の attention で読む KV がごくわずかになり、PCIe 帯域の要求を GPU 直接メモリ構成のレベルまで引き下げられます。LayerCache は別の発想で、層間で同じ KV を再利用し、隣接層が共有する部分は 1 つだけ保存します。KV の冗長性が高いモデルではかなり節約できます。
swap out ルートの最大の問題はレイテンシ分散です。SSD のランダム読みレイテンシは数十〜数百マイクロ秒の桁で、一度ある token を何度も swap in する必要が生じると、尾レイテンシが非常に悪化します。そのためこのルートはオフラインバッチ処理やレイテンシに鈍感な場面に向いています。オンラインサービス向けたシステムシステムでは、主流の選択は依然として「量子化 + 疎化 + ページング」の組み合わせです。
5.4 eviction と圧縮:いったい何捨てるべきか
コンテキストが予算を超えたら、履歴 token の KV を一部捨てるしかありません。eviction 策略の中心問題とは「誰を捨てるか」という点です。ここでの系譜は attention sparsification よりも「有損」です。一度捨てると本当に見えなくなるからです。
- Sliding window / StreamingLLM:一番古いものを捨て、冒頭の sink は残す。実装は最も簡単ですが、捨てる対象がちょうどタスクで必要だった証拠である可能性があります。
- attention スコアのヒューリスティック(H2O 系):各 token がどれだけ注意されたかの累積スコアを数え、高スコア側を保持します。問題は、各 token のスコアカウンタを維持する必要があることと、累積スコアには強い履歴バイアスがあることです。初期に見られた token はスコアが高いままになり、それ自体がバイアスになります。
- 観察窓 + プーリング(SnapKV 系):プロンプト末尾に観察窓(observation window)を置き、この窓の query でプロンプト全体の key にスコアをつけ、top-k を取って保持位置に対してクラスタリングや 1 次元プーリングの補償を行います。これが現在最も使われている方案です。採点基準が「このリクエストに対する関連性」であって全体履歴の統計ではなく、追加の訓練も要らないからです。論文の典型的な構成は、各層を 1K〜4K token まで圧縮するものです。
- タスク認識 / 階層型予算(PyramidKV 系):層ごとに異なる予算を割り当てます。浅層は主に局所パターンマッチングを行うので必要な窓が小さく、深層は抽象推論を行うため遠くを見る必要があるので、より大きな予算を与えます。同時に残りの利用可能コンテキストに応じて予算比率を動的に調整し、残りが少なくなったときには予算を縮めます。
- attention sink + 重要 token の常駐:冒頭の sink と、プロンプト末尾の直近窓、そして本文全体から抜き出した高スコア token の 3 つをつなぎ合わせる。これが現在の本番システムにおけるデフォルトのレシピです。
eviction 策略の共通の落とし穴は、これらはすべて「生成時のアクセスパターン」で「リクエスト全体の正しさの要求」を予測していることです。両者は強く相関しますが、等しいものではありません。multi-hop retrieval には典型的な反例があります。本当に必要だった根拠が、生成の 3 ステップ目までに一度もアクセスされていないため、アクセス統計に基づく eviction では、その根拠を早い段階で廃棄してしまう可能性があります。この種のタスクでは、緩和策は通常 prompt 層での事前 retrieval です。外部 retrieval を使って関係する段落をプロンプトの冒頭か末尾に挟み込むのです。モデル内部の eviction に助けを求めるのではありません。これが、長コンテキストシステムで「外部 retrieval」と「内部疎化」が長く併存している理由です。前者がリコールを解決し、後者がコストを解決します。
5.5 KV Cache のオブザーバビリティ:必ず見ておくべき 4 つの指標
長コンテキストのチューニングに測定がなければ、すべて推測に頼るしかありません。少なくとも 4 つの指標を継続的に集めるべきです。第一にKV Cache の占有率と断片化率です。メモリ上で実際に KV が占める割合と空きブロックの割合で、ページング構成が妥当かどうかを直接教えてくれます。第二に1 トークンあたりの KV バイト数です。eviction の収縮比率が長さによって異なるため、この値はシーケンス長とともに変化します。合計量よりも、圧縮策略の健康状態を示します。第三にprefix cache のヒット率です。複数ターンの対話や共有システムプロンプトの場面では、このヒット率がスループットを直接左右します。期待より低い場合は、prefix が eviction に誤って傷つけられている、またはページのブロック境界が logic prefix の再利用を妨げていることが疑われます。第四に層ごとの注意質量の分布です。定期的にサンプルを取って各層が実際にアクセスした KV の割合と各層の有効窓サイズを統計すれば、階層型予算を調整する最も直接的な根拠になります。
この 4 項目のうち最も過小評価されているのは最後の項目です。階層型予算はアーキテクチャの問題に聞こえますが、実際にどう配分を決めるのは層ごとの attention 分布の測定データです。モデルごとに、しかも同じモデルの別の訓練フェーズごとに、この分布はまったく違います。「各層・各ステップで実際にアクセスされた KV の割合」を時間変動するダッシュボードに落了としたものは、いかなる経験的パラメータより信頼できます。
六、ページ管理:PagedAttention が解いているのは断片化
同時実行するリクエストのコンテキスト長がそれぞれ違うと、KV Cache の割り当てには古典的な外部断片化の問題が生じます。あるリクエストは 8K、別のリクエストは 100K 必要だと、最大長で確保すると、連続稼働の中でメモリが大量の穴に食われます。PagedAttention の做法は OS の仮想メモリのページングを借りたものです。KV Cache を固定サイズのブロックに切り、各ブロックを 1 枚の「ページ」とし、リクエストの論理 KV シーケンスを物理ページの集合にマップし、物理ページは共有プールから必要に応じて割り当てられます。
vLLM の論文が示している数字では、従来の連続割り当てにおける断片化による浪費率は長さ分布によって 60%〜80% の桁ですが、ページング化によって 4% 以内に下がります。メモリを節約するだけでなく、ページングは 3 つの直接的なシステム上の利益をもたらします。第一に、ほぼゼロコピーの prefix cachingです。共有のシステムプロンプト、few-shot の例、複数ターンの対話履歴がすべてページ単位で共有され、命中したら命中しただけで、コピーも要りません。第二に、波動状の連続バッチ処理です。新しいリクエストは任意の時刻に実行中 batch の末尾へ挿入でき、batch 全体が終わるのを待つ必要がありません。第三に、疎 attention をページ単位でアドレスできることです。 앞에서述べた top-k 検索やブロックレベルの重要度スコアリングは、すべて「ブロック」という単位の上に成り立っており、ページングはまさにこの単位を自然に持ちます。
ページングの代償は 1 ページあたりのメタデータオーバーヘッドと、attention カーネルが「非連続の物理ページ + 論理位置」のマッピングを扱える必要があることです。実際のエンジニアリングでは、連続 KV しか扱えない汎用 flash attention ライブラリを流用するのではなく、真正に変長ブロックテーブルに対応した attention カーネルが必要だということを意味します。SGLang の RadixAttention は同じ発想の別実装で、ラディックス木(prefix tree)で共有 prefix を整理し、命中判定も eviction も木上の演算になります。
ページングにはよく忘れられる殺傷力満点のユースケースもあります。共有 prefix の copy-on-writeです。あるリクエストが途中のある位置から複数の候補に分岐する必要があるとき(beam search、多路サンプリング、木構造の生成など)、従来の做法では KV 全体を丸ごとコピーする必要があり、長コンテキストではコピーコストがシーケンス長に比例するので、数万 token の prefix を 1 回コピーするだけで数秒かかります。ページングの後では、ページテーブルを書き換えるだけで済みます。候補同士は prefix の同じ物理ページを共有し、分岐点以降のページだけが各自新しく作られるからです。この性質によって、長コンテキストと木構造デコード、多候補生成といった技術が初めて経済的に実行可能になり、そしてそれらは長コンテキストにおける「一度により良い答えを複数生成する」という製品ロジックの土台となる依存関係です。
ただし共有 prefix はisolation の問題も持ちます。異なるテナント、異なるユーザーのリクエストが同じページ prefix を共有したとき、範囲外書き込みが一度発生すると、リクエストをまたぐ情報漏洩を引き起こします。オンラインサービスの推論フレームワークは、物理ページの書き込み権限が排他的であり、読み込み権限だけが共有可能であることを保証しなければなりません。これは性能最適化の側で回避できる問題ではなく、受け入れ試験では並行下での範囲外書き込みシナリオを専用のテストにすべきです。
七、Prefill と Decode:まったく異なる二つのボトルネック
ここまでの最適化を一度実際の推論に置いてみると、最も大きな判断ミスは「prefill と decode を区別していない」ことに由来することがわかります。この 2 つの段階のボトルネックは、ハードウェア上で正反対を向いています。
7.1 Roofline:演算量律速 vs 帯域律速
Prefill 段階は演算量律速(compute-bound)です。一度に数百から数千の token を処理するため、行列乗の規模が十分大きく、GPU の浮動小数点ユニットを使い切らせられます。attention カーネルの融合、疎化、量子化がいずれもそのまま実時間上の収穫に変換されます。この時点では KV Cache はまだ作られておらず、メモリの圧力は比較的小さく、主なボトルネックは FLOPs です。
Decode 段階はメモリ帯域律速(memory-bound)です。1 トークン生成するたびに、すべての層が KV Cache 全体を HBM から 1 回読み込みます。これはハードウェア上の必然的なコストで、何トークン生成するかとは無関係です。7B モデルの 1 トークンあたりの重み読み出しは約 14 GB で、2 TB/s の帯域なら 7 ms になります。さらに KV Cache の読み出しを加えると、128K コンテキストでは 1 トークンあたり 40〜64 GB 追加で読むことになり、帯域要求は数倍に跳ね上がります。このとき KV のバイト数を下げることは FLOPs を下げることよりずっと重要で、計算側の疎化の収穫はほとんどありません(1 回の計算で扱われるのは query 1 つだけなので、疎化で節約されるのはもともとごく少量の計算だからです)。
帰結は明確です。prefill 側には attention sparsification とチャンク並列を、decode 側には KV 量子化、KV eviction、ページ再利用を入れろということです。多くのチームがこの 2 つを逆にしています。decode 段階で「疎 attention」を強調しておきながら、decode 側のボトネックが計算ではなくバイトの運びであることを忘れているのです。
7.2 スケジューリング:chunked prefill とハイブリッドバッチ処理
chunked prefill の動機はこうです。100K token を一度に処理する prefill は GPU を数秒間占有し、その間に decode しているリクエストはすべてブロックされ、首 token 遅延(TTFT)が跳ね上がる一方で 1 出力 token 遅延(TPOT)は正常という、良くない体験になります。chunked prefill は長い prompt を固定サイズの塊に切り、他のリクエストの decode ステップと組み合わさせてスケジュールし、両者が同じ batch の中で共存できるようにします。代償は prefill の総時間がわずかに伸びること(塊の境目で同期コストが出ること)ですが、収穫は TTFT と TPOT の釣合いが大きく改善することです。
さらに進めたものがハイブリッドバッチ処理(continuous batching)です。decode を「毎ステップ token を 1 つ追加する prefill」と見なし、両者が同じ batch グラフの中で実行され、統一スケジューラが token 予算に応じて演算量を配分します。この仕組みは現代の推論エンジンのスループット能力の土台ですが、新たな問題も持ちます。KV Cache のメモリが足りないとき、スケジューラは一部のリクエストをプリエンプト(preempt)して swap out か破棄しなければならないからです。プリエンプト戦略の選択は長リクエストの公平性に直結します。500K のリクエストが頻繁にプリエンプトされれば、その TTFT は回復できません。したがってコンテキスト長スケジューリングと優先度の間で明示的なトレードオフが要ります。vLLM が後から導入した「token 予算による配分 + 階層キュー」のようなルールは、いずれも長リクエストと短リクエストが共存するときのスケジューリング公平性を解こうとしています。
もう一つ見落とされがちな点があります。speculative decoding は長コンテキストの decode 段階で効果が拡大します。decode は帯域律速なので、1 回のフォワードで有効な token は 1 つしか出力されないのに KV は全部運び出さなければならないという、この「帯域を演算に交換する」取引が非常に割に合います。5〜10 倍の无损加速は長コンテキストの decode ではほぼタダで得られます。ただし前提条件があり、ドラフトモデルが十分に小さく、目標モデルの検証段階で同じ KV Cache を再利用できることです。
7.3 レイテンシ予算:長コンテキストの要求を SLA に書き込む
長コンテキスト案件で受け入れフェーズとして最も問題が起きやすいのは、レイテンシ指標が分解されていないことです。1M コンテキストのサービスに対して「首 token 遅延 5 秒未満」という 1 本の指標しか与えないと、チームは prefill を最適化しに走り、decode 段階の複数ターン対話体験には誰も責任を持ちません。レイテンシは 4 つの数値に分けることを提案します。首 token 時間(prefill コストと待機によるもの)、1 出力 token あたりの時間(KV のバイト数と帯域によるもの)、キャッシュ未ヒット時の首 token 時間(高百分位レイテンシで真の原因になることが多いもの)、そしてコンテキスト成長の傾き(1 出力 token あたりの時間が入力長に対して増えるカーブ)。最後のものが特に重要です。サービスの最大同時実行数がコンテキスト長にどう追随して変わるかを直接教えてくれるからです。
もう一つ実践上の注意があります。圧縮方式は高分位で検証しなければならない。平均だけを見ていてはいけません。KV の swap out、リクエストのプリエンプト、ページングの断片化、この 3 つのコストはいずれも極端に長尾に集中しており、長尾こそがユーザーの不満の源泉です。平均 200 ミリ秒でも高分位が 4 秒という生成レイテンシは、対話製品では使えません。
八、訓練期の長シーケンス分割と推論期の完全なコンテキストとの乖離
これは長コンテキストの中で最も回避されやすく、しかも最も致命的な問題です。推論時には 1M を持たせたい場合でも、訓練時には連続した 1M のシーケンスにほぼ確実に出会っていない。この乖離にはいくつかの層があります。
第一にシーケンス分割がもたらす位置の断裂があります。有限のメモリを最大限に使うため、訓練では長い文書が固定長の断片に切られます。問題は、断片が document id ではなく position id でパッケージされている点(サンプル横断の attention mask のため)にあり、推論時の position id は連続的に増加します。モデルは訓練中に「各断片の内部で相対位置づけを行う」ことを学ぶのに、推論時には断片内の位置が 100 万に達する入力に直面します。RoPE は絶対位置への影響を受けます(主に相対位置を符号化するよう設計されていますが)、この断裂はそのまま分布外の入力を招きます。一般的な回避策は position id も断片ごとにゼロから振り直すこと(これは各断片に独立した「文書」を与えるのと同値)ですが、こうするとモデルは断片をまたぐ長距離の関係を永久に学べなくなります。
第二にattention パターンの訓練カバレッジ不足があります。訓練中に主に「短い断片と文書の境界」だけを見ていると、モデルは超長シーケンスに対する実際の attention 分布を一度も観察していません。解決策は段階的拡張です。まず 4K で訓練し、次に 32K で継続訓練し、さらに 256K で訓練します。各段階は前の段階の重みで初期化し、位置符号化のスケーリング比率も段階ごとに変えます。これが LongRoPE の段階的拡張の発想です。この訓練のコストは実在します。256K での意味のある継続訓練を一度行うコストは、短いコンテキストでのファインチューニングの数十倍から数百倍になります。
第三に長シーケンスのサンプル自体が希少です。ウェブ、コード、書籍には 100 万 token を超える自然文書が非常に多くありますが、洗浄と重複排除、品質フィルタを通すと、残る高品質な長シーケンスサンプルは短いものに比べれば桁違いに少なくなります。長シーケンスの訓練データに大量の低品質な連結(途中で切られた文書、テンプレート化された内容)が混ざっていると、モデルが学ぶのは長距離依存ではなく「ノイズを無視する方法」になります。多くの「長コンテキスト対応を謳うモデル」が実測で期待に及ばない原因の根幹はここにあり、これはアーキテクチャの問題ではなくデータの問題です。
第四にloss の重みが不揃いという問題があります。長さ 1M のシーケンスで、すべての token の loss を等重みで平均すると、モデルは「先頭の 1K」(たいてい文書の冒頭の情報密集領域)の勾配寄与が中央部よりずっと大きくなります。一般的な対処は chunk 単位で重み付けするか、一部の token についてのみ loss を計算することですが、これらは新たな偏りを持ち込みます。普遍的な答えのないエンジニアリング上の細部であり、社ごとの做法には大きな差があります。
8.1 訓練側のコード:ブロック級 attention mask と packing
以下のコードは、訓練時の長シーケンスにおける中核的な mask の組み立てを示しています。複数の短いサンプルを 1 本の長いシーケンスに束ね、ブロック級の mask でサンプルをまたぐ attention を遮断します。これはほとんどのチームが実際に行っている做法であり、推論側の最長コンテキストにおける乖離の源泉でもあります。
import torch
def build_block_causal_mask(seqlen, block_size, device):
'''因果 mask + 块内双向:因果注意力的标准变体。
block_size > 1 时,同一块内允许互相看见,块间仍然严格因果。
推理时如果用滑窗 + 全局 sink,这个结构要保持一致,否则训练/推理不匹配。
'''
idx = torch.arange(seqlen, device=device)
causal = idx.view(-1, 1) <= idx.view(1, -1)
blk = (idx.view(-1, 1) // block_size) >= (idx.view(1, -1) // block_size)
# 混合注意力(如 Gemma 3n / 交替滑窗层)就靠这里给不同层返回不同的 mask
return causal & blk
def packed_document_mask(seq_lens, total_len, device):
'''多文档 packing 的 mask:文档之间完全隔离。
注意 position_ids 要在这里同步重置为 0,
否则模型会把不同文档的位置接成一条连续的长序列——
这正是训练/推理落差的直接来源。
'''
doc_id = torch.zeros(total_len, dtype=torch.long, device=device)
position_ids = torch.zeros(total_len, dtype=torch.long, device=device)
cur, pid = 0, 0
for n in seq_lens:
doc_id[cur:cur + n] = pid
position_ids[cur:cur + n] = torch.arange(n, device=device)
cur += n
pid += 1
mask = doc_id.view(-1, 1) == doc_id.view(1, -1)
# 再和因果 mask 求交,并保留前若干个全局 sink 列
return mask, position_ids
# 训练循环里的常见组合:
# mask = packed_document_mask(lens, N)[0] & causal_mask
# out = model(input_ids, position_ids=position_ids, attention_mask=mask)
# # 关键:position_ids 用上面重置后的版本,不要用默认的 arange(N)
# 对照检查:如果你的推理引擎在 decode 阶段用单调递增的绝对 position,
# 而训练时每篇文档都从 0 开始,那么长到 10 万之后就会有明显的分布漂移。8.2 長コンテキストの訓練データはどう作るか
手元に自然な長文書がないなら自分で作る必要がありますが、いくつかの原則があります。第一に、連結は自然でなければなりません。区切り文字で無関係な文書をそのままつなぐと、モデルは近道を学習します。文書どうしは無関係なのだと、その結果、長シーケンス内の大量の attention が境界の識別と意味のない位置に浪費されます。より良いのは、同一テーマの関連文書、同一書籍の連続する章、あるいは同じコードリポジトリ内で相互参照しているファイルを使うことです。第二に、長距離の依存を必要とするサンプルを意図的に構成すること。前部に重要な実体を埋め込み、後部で質問をする、あるいは前部に定義を出し、後部で使う。公開されている長コンテキスト評価セットは本質的にこの構成で用意されており、訓練セットにもそれに対応する比率を入れるべきです。そうでなければ、モデルが学到するのは局所的な共起であって、長距離の依存ではありません。第三に、超長サンプルの比率を管理すること。超長サンプルの割合が閾値を超えると、短いテキストの能力が明確に退行します(catastrophic forgetting)。最適化器の更新が少数の超長勾配に支配されてしまうからです。一般的なのは、超長サンプルが 1 パーセントから 5 パーセントを占め、残りは通常の長さを維持して短いコンテキストの性能を守るという構成です。
もう一つ見落とされがちな軸があります。混合長訓練では、超長サンプルの loss はサンプル単位ではなく token 単位で重み付けするべきだということです。こうしないと、長さ 100 万のサンプル 1 本がバッチ全体の勾配を支配してしまいます。これは前述の packing とセットです。packing によって複数の短いサンプルが 1 回の計算を共有し、超長サンプルは通常は別バッチになるため、両者を安定して訓練するには異なる勾配蓄積戦略が必要なのです。実際、多くのチームの超長ブランチが調子が悪いままなのは、根因がここにあります。
九、失效境界:これらの手法はどこで破綻するか
長コンテキストは「多くのタスクでは足りるが、いくつかのタスクでは完全に破綻する」分野です。破綻の形を並べて挙げることは、方法名を覚えるよりはるかに役に立ちます。
9.1 位置符号化方式の失效境界
純粋な周波数のスケーリングは、正確な遠距離の特定を要求するタスクで最も劣化します。長さ 200K のコンテキストの中で「第 173,842 番目の token は何か」という問いに対して、補間系の方式はほぼ確実に失敗します。すべての位置を訓練時に見た範囲に押し込むため、隣接する位置の解像度が薄まってしまうからです。この種のタスクの正しい答えは外部検索か明示的な位置ラベルであり、位置符号化に望みはありません。
もう一つの破綻点は推論時の外挿幅が訓練時を大きく超えていることです。32K で YaRN のスケーリングを行い 128K 対応と謳うモデルを引き当てて 1M まで直接押し出した場合、スケーリング比率がすでに合いません。YaRN の 128K 設定は 1M では 8 倍のスケーリングが必要で、8 倍の補間による局所解像度の損失は致命的です。段階的拡張の価値は、どんな時点でもモデルが「現在の長さの分布の内側」で訓練されていることを保証できる点にあります。
9.2 attention 疎化の失效境界
構造化疎(vertical-slash 系)の破綻シナリオは、入力の構造が前提のトポロジーと噛み合わない場合です。自然な言語や一般的なコードでは良好に振る舞います。しかし、規則正しく並んだ表、時系列データ、繰り返し度の高いログのような入力では、vertical column パターンが予算を大量の無意味な同位置 token に浪費してしまう可能性があります。
検索式疎の破綻シナリオは、粗い篩いと精密な並び替えで関連性の順序が一致しないことです。低次元の射影の下で最も似ているベクトルが、全次元の空間では最も関連するとは限らない。答えを決めるその証拠は、query と意味的にしか関連がなくて、字面や方向でのつながりがまったくないこともある。特に、段落をまたいで総合する必要があるタスクでは、top-k 検索式疎が前提とする「少数の強い関連位置が存在する」という仮定自体が成立しません。コードタスクにも同様の問題があります。バグの原因がファイルの冒頭にあり、症状がファイルの末尾にあるとき、両者は低次元空間の中では query から遠い可能性があります。
学習型疎化の破綻シナリオはセレクタとバックボーンの分布のズレです。セレクタは訓練分布の上で学んでいるので、本番で分布外の入力(極長のコード、複数言語の混在、構造が異常な文書)にぶつかると、重要性予測は信頼できなくなります。より厄介なのは、この信頼できなさはずっと静かに現れます。もう一つ実践上の穴があります。セレクタ自体が prefill のコストを増やすので、セレクタが十分に軽量でなければ、疎化で節約した attention の FLOPs はセレクタのコストを相殺できないことがあります。
9.3 KV 圧縮と淘汰の失效境界
KV 圧縮(とくに 4bit 以下)は数値に敏感なタスクで問題を起こします。数学的推論、コードの厳密な一致、長い表から具体的な数値を引き出すようなタスクです。これらのタスクはすべての token の表現精度を要求し、量子化のノイズは softmax の正規化と多層の蓄積によって増幅されます。観察される現象は,目立った誤りではなく、「答えの形式は正しいが数値が少しずれている」あるいは「多段推論の途中の一つの判断がずれている」という形になる。
eviction の方針の失效シナリオはすでに述べたとおり、多段検索です。特に強調しておきたいのは、SnapKV のように「プロンプト末尾の query で採点する」方式には、多段タスクに対して構造的な欠陥があるということです。末尾の query は最後の段の問題しか気にせず、最初の段で必要な証拠には低い点数しか付けません。対策は文書の冒頭と末尾の両方を観察窓にすること、あるいはそもそも淘汰しない(大きなメモリを直接積む)ことです。
9.4 訓練と推論の乖離の失效境界
最も隠れた種類の破綻はフレームワークが謳う対応長と、モデルが実際に訓練された長さが 1 桁も違っていることです。判断方法は単純で、モデルカードに明記された訓練・ファインチューンの最大長を確認します。「YaRN で 128K まで拡張」としか書かれていなければ、その強みは 30K までにある可能性が高い。「256K で継続訓練」と書かれていれば、8K までの能力は通常うまく保たれ、128K 以降については実測が必要です。これはアーキテクチャの欠陥ではなくリソース制約の必然的な帰結ですが、ほとんどすべての宣伝資料が触れません。
9.5 実装前に必ず実行すべき一組の対照実験
どの方式を採用するかを決める前に、少なくとも次の 4 組の対照実験を回しておくと思います。第 1 組は素の長さの KV と圧縮後の等価性です。同じモデルで、完全な KV と 4 分の 1 に圧縮した KV で同じタスク集合を実行し、量子化の精度損失が具体的にどのタスクに落ちるのかを確認します。これは業務が量子化に耐えられるかを直接答えるものです。第 2 組は疎化設定の走査です。総予算を固定したまま、動的検索と sliding window の配分比率だけを変え、自分のタスクにおける最適点を探します。第 3 組は実効長の曲線です。入力長を等比で伸ばして精度曲線を描き、これは容量計画の根拠であると同時に、モデルが静かに愚かくなっているのを防げる唯一の防波堤でもあります。第 4 組はprefix ヒット率の対レイテンシの感度です。実トラフィックのリプレイで、異なる eviction 方針の下のヒット率とエンドツーエンドのレイテンシを測ります。この項目は投資対効果の比が最も高いことがわかっており、モデルを変更する必要がないからです。
この 4 組の実験に共通するのは、「モデルが強いからもっと長く扱える」という仮定に一切依存せず、すべて自分のタスク分布の上に立っているという点です。これは長コンテキストという領域全体に対する私の基本的な判断でもあります。アーキテクチャ上の位置外挿、attention 疎化、KV 圧縮が解決するのはコストと上限です。しかしあなたのシステムがどこまで到達できるかは、自分のデータの上で実効長をどれだけ測定し、その周囲にどのようなエンジニアリング上のトレードオフを組み立てるかに掛かっています。
十、繰り返して現れる六つのよくある誤解
- ウィンドウの数字を能力の約束だと误解する。 モデルカードにある「100 万対応」は容量の説明であって、質の説明ではありません。判断基準は一つだけです。あなたのタスク上で、答えの品質がどのくらいの入力長から落ち始めるかです。この数字は通常、公称値よりはるかに小さく、どの宣伝資料にも書かれていません。
- FlashAttention を入れれば長コンテキストは解決したと思う。 融合 attention カーネルが解決するのはメモリのピークであって、計算量ではありません。シーケンス長が 2 倍になれば attention 部分の実時間はおよそ 4 倍になり、モデル本体の部分は 2 倍だけです。二乗項は短い長さでは線形項に隠れてしまいますが、まさに長い長さで表面化します。
- decode 段階で attention の疎化を強調する。 decode は毎ステップ token を 1 つしか生成しません。ボトルネックは KV をメモリから計算ユニットへ運ぶ帯域であって、演算量ではありません。KV のバイト数を削減する(量子化、淘汰、階層化)ことが効果があり、attention の計算量を減らすことはほとんど効果がありません。これは長コンテキスト最適化の中で最も一般的な方向性の誤りです。
- 全体のアクセス統計で淘汰する。 累積 attention スコアの高い token を残す方式は、単段のタスクではうまく動きます。しかしこのスコアには強い履歴バイアスがあります。初期に見られた token はスコアが高いままになり、多段タスクで本当に必要になるその根拠は第 2 段になって初めて使われるため、その時点ではすでに淘汰されている可能性があります。
- 自然言語だけで長コンテキストをテストする。 コード、表、時系列データの attention 分布は自然言語とはまったく異なり、精度への要求もはるかに高いです。量子化誤差はコードのタスクでは構文エラーとして、表のタスクでは数値のずれとして現れやすくなります。自然言語だけを測ると、本番に入ってはじめて問題が判明します。
- モデルのアップグレード時に実効長をリグレッションさせない。 モデルの重み、位置符号化の設定、推論エンジンのスケジューリング方針、プロンプトの組み立て方、この 4 つのうちどれか 1 つが変わっても実効長は後退しえます。バージョンアップ時に通常の短いタスクだけ動かすと、長コンテキストの能力が誰も気づかないうちに桁違いに退行することがあります。
十一、2026 年のエンジニアリング上の合意と選定のチェックリスト
ここまでの内容を、会議に持っていく 1 枚の表に圧縮すると、こうなります。
| 工程 | 主流の做法 | 典型的な収穫 | 主な代償 |
|---|---|---|---|
| 位置符号化の外挿 | YaRN 系の周波数帯ミックス + attention 温度補償。より長いコンテキストでは段階的拡張 + 非均一スケーリングを使う | 128K 量級ではほぼ無損失 | 「遠くからの検索」は解決しない。訓練長を大幅に超えると退行する |
| 事前学習の段階 | 長さ混合のカリキュラム。短いシーケンスを主役にし、少量の超長シーケンスを下支えに置く | その後の拡張に実現可能性を残す | GPU コストが高く、長シーケンスのデータが希少 |
| アーキテクチャによる次元削減 | GQA / MQA。さらに強い圧縮が必要なら MLA(潜空間 + 吸収技巧) | KV の次元を 10〜100 分の 1 にする | 精度はわずかに落ちる。MLA とページングには実装上の緊張関係がある |
| attention の疎化 | sliding window + sink を土台に、層別・頭別の規則的な疎化または top-k 検索を重ねる | prefill の FLOPs が数分の 1 になる | リコール失敗は静かに起きる。外部検索での受け皿が要る |
| KV の量子化 | Key はチャンネル単位、Value は token 単位。グループ量子化で 4〜8bit | KV のメモリが 2〜3 分の 1 になる | 長シーケンスと数値に敏感なタスクでは退行する |
| KV の淘汰 | sink + 直近の窓 + 観察窓の高スコアプール。層ごとに予算を割り当てる | 長リクエストのメモリを抑えられる | 多段検索で必要なものを誤って削除する |
| メモリ管理 | PagedAttention / トライ(prefix tree)で、共有する prefix をゼロコピーで扱う | 断片化率が 60〜80% から 4% 以内へ下がる | 分ページング attention カーネルの自前開発または専用製品が必要 |
| スケジューリング | chunked prefill + 連続バッチ処理 + speculative decoding | TTFT と TPOT の釣り合いが改善し、decode が数倍速くなる | 長いリクエストは混合バッチ処理の中でプリエンプトされやすい |
さらにいくつかの判断原則にまとめるなら、私としての見立ては次の通りになります。
具体的に実行に落とす段階では、次のチェックリストは 1 項目ずつ確認しておく価値があります。特定のフレームワークを想定したものではなく、長コンテキストシステムそのものが間違えやすい箇所だけを指しています。
- 実効長を測定済みで、監視に落とし込んでいる。 見積もりやモデルカードの数字ではなく、自分たちのタスク上の精度曲線であり、この曲線にはバージョンごとの推移グラフが必要です。
- モデルカードの公称長とファインチューン長を確認済み。 公称長が大きくファインチューン長を超える場合、ファインチューン長を超える部分については外部検索で受け止める必要があり、モデル自体に期待してはいけません。
- prefill と decode の 2 段階でボトルネック指標を分けて取得している。 首 token 時間、1 出力 token あたりの時間、キャッシュ未ヒット時の首 token 時間、そして 1 出力 token 時間が入力長に対して増える傾き、この 4 つの数字がすべて揃っている必要があります。
- 高分位レイテンシを単独で受け入れている。 圧縮率、swap out の方針、プリエンプト方針のコストはいずれも長尾に集中しているため、平均値では問題を覆い隠してしまいます。
- ページング設定と prefix ヒット率にダッシュボードがある。 断片化率、1 トークンあたりの KV バイト数、prefix ヒット率、そして層ごとの実際の KV アクセス率、この 4 つの指標が常に見える状態にあります。
- 圧縮方式の精度損失が具体的なタスクにまで特定されている。 「全体として無損失」ではなく、どの種類のタスクがどの圧縮率で退行し始めるかを明確に把握し、それに準じて業務側の閾値を設定している必要があります。
- バージョンアップ後に長コンテキストのリグレッションテストをやり直している。 モデルの重み、位置符号化の設定、推論エンジンのバージョン、プロンプトのテンプレートのいずれか 1 つが変わったら、このリグレッションを必ず起動する必要があります。
- 多段タスクには独立したリコール受け皿がある。 どのシナリオで必ず外部検索を通すのかを明記し、モデル内部の疎化と淘汰(strategy)に期待を寄せる形にしないことです。
この 8 項目を確実に潰せば、長コンテキストの案件が「デモできる」から「本番に載せられる」への移行はほぼ完了します。
第一に、実効長を決めてから名義長を決めます。 ほとんどのチームの誤りは、ウィンドウの数字を先に伸ばしてから、業務がそもそもそこまで長く必要としていないことに気づき、最後に使っていない長さの分のメモリとレイテンシのコストを払っていることに気づく、という順番を踏むことです。まず自分のタスクがどこから崩れ始めるかを測り、ウィンドウは必要な長さに少しの余裕を足すだけで十分です。
第二に、prefill と decode を 2 つの独立したプロジェクトとして最適化します。 この 2 つのボトルネックも、手段も、受け入れ指標も、すべてが異なります。prefill は TTFT と疎化の収穫を見、decode は TPOT と KV のバイト数を見ます。同じ指標で 2 つを同時に評価することが、長コンテキストの案件で最も資源の配分を誤るポイントです。
第三に、内部の疎化と外部検索は二択ではありません。 疎化はコストを解決し、検索はリコールを解決します。多段タスクでは 2 つが必ず共存します。1M の純粋 attention な方式と 32K + 検索の方式とでは、多くの本番シーンで効果の差よりもコストの差のほうが小さいのです。本当の仕事量は「検索結果をどうコンテキストに組み込むか」にあります。冒頭に置くか、末尾に置くか、関連度順に並べるか——この選択が最終的な精度をそのまま左右します。
第四に、長コンテキストの能力を、継続的に保守すべきシステム特性として扱います。 これはモデルの重み、位置符号化の設定、推論エンジンのスケジューリング方針、プロンプトの組み立て方という 4 つの組み合わせの設定に依存します。どれか 1 つを上げて 1 つを検証しないことが、長コンテキストのシステムにおける最も一般的な退行の原因です。最低限、「実効長」という指標を固定する回帰テスト群は用意しておく必要があります。
この方向を今後 1 年ほど見通すと、追跡しておく価値のあることがいくつかあります。1 つは疎化と量子化の統合です。現状では疎化はどの位置を見るかを決め、量子化は各位置に何バイト保存するかを決めており、両者は別々に設計されています。次の進化は、重要性予測とビット幅の割り当てを結びつけて、モデル自身がどの token を 8 ビットで残す価値があり、どの token を 2 ビットで十分かを判断するようにすることです。2 つ目がattention sink の再解釈です。業界は今やこれをエンジニアリング上の技巧として扱っていますが、仕組みの観点から見ると、大規模なコンテキストの下でのデコーダーに本質的な情報ルーティングの仕組みである可能性があり、推論時のマスクで凑うのではなく、事前学習データの組み立ての観点から設計し直す価値があります。3 つ目がコンテキストと外部記憶の境界の引き直しです。内部の KV が圧縮・階層化・必要に応じて swap out されるようになれば、従来のベクトルデータベースは唯一の記憶層ではなくなり、両者は次第に統一された階層記憶システムへと融合していきます。
最後に、自力で車輪を作ろうとするチームへ。上に挙げた技術のほとんどについて、その中核的な洞察(相対位置符号化の周波数分布、attention sink の存在、ヘッドごとの重要度の差、token の重要度と位置の切り離し)は、すべて公開されていて、すでに繰り返し検証されています。難しいのはアイデアを思いつくことではなく、本番環境でそれらを組み合わせ、各段階の精度損失を抑え込み、そして演算子のレベルで理論上の収穫を本当に受け取るすることです。この部分の仕事の量は、通常、アイデアそのものの 1 桁上のサイズになります。