fullseye

LLM に至る系譜の芯(速くする工夫は、全部おなじ数の別の括り方) — 使い方ガイド

この族は何をする道具箱か

状態機械 → RNN → Attention → Transformer という系譜に出てくる仕組みのうち、厳密な等式・保存量・整数不変量が立つものだけをアルゴリズムとして持っています。「それらしく動く」ものは入れていません —— 蒸留も Sampler も Perplexity も、真値が無いのでこの箱の外です。RAG・Tool Calling・Guardrails は系の設計であってアルゴリズムではないので、こちらではなく llmesh / llive の側に置きます。

なぜ画像の箱に入っているか: 注意はトークン列の上の演算で、画像を格子に切ればそのまま ViT の入口になります。この族の op は tokens (T, d) を受けて tokens か attnmap (T, S) を返すだけなので、トークンが単語だろうが画素パッチだろうが同じ式で動きます。

10 op / 4 カテゴリ(numpy だけ。台帳は opsllmcore.py、実体は llmcore.py):

連鎖

flowchart LR
    img[画像] --> P[パッチ列 tokens]
    P --> N[rms_norm] --> R[rope_rotate]
    R --> S[attention_scores] --> W[attention_weights] --> A[attention_apply] --> out[tokens]
    R --> F[attention_softmax] --> out
    R --> T[attention_tiled] --> out
    R --> L[attention_linear] --> out
    R --> G[attention_grouped] --> out
    R --> K[kv_cache_decode] --> out
import fullseye as fs
L = fs.ledger

q, k, v = patches @ Wq, patches @ Wk, patches          # (T, d) の列。画素パッチでよい
q, k = L.rope_rotate(q), L.rope_rotate(k)              # 位置符号(ノルムは動かない)

w = L.attention_weights(L.attention_scores(q, k), "causal")   # 行和 1 / 未来は厳密に 0
y = L.attention_apply(w, v)                                   # = attention_softmax(q,k,v,"causal")

y2 = L.attention_tiled(q, k, v, tile=32, mask="causal")       # (T,T) を作らない。答えは同じ
y3 = L.attention_linear(q, k, v, causal=True)                 # softmax を外すと結合則が効く
y4 = L.attention_grouped(q, k, v, n_heads=4, n_kv_heads=1)    # MQA(n_kv_heads=4 で MHA)
step = L.kv_cache_decode(q[-1:], k, v)                        # 1 トークンだけ進める

実測(画像パッチ 64 トークン、examples/poc_attention_identities.py)

主張 閉形式・真値 実測
注意の行和 1 ずれ 2.22e-16
マスク位置の重み 0 0.0e+00(許容差が要らない)
因果の非ゼロ数 T(T+1)/2 = 2080 2080(整数で一致)
窓の非ゼロ数(W=9) Σ min(i+1,W) = 540 540(整数で一致)
タイル + online softmax 一括と同じ 7 通りのタイルで最悪 1.33e-15
(QKᵀ)V == Q(KᵀV) 0 1.18e-15
未来を書き換えたときの前半 0 0.0e+00(後半は 0.403 動く)
KV Cache の逐次 一括と同じ 5.92e-16
置換同変(位置符号なし) 0 4.4e-16(RoPE を入れると 0.018)
RoPE のノルム保存 0 4.44e-16
RoPE の相対位置(絶対位置 40 通り) 0 1.11e-15
GQA == MHA / MQA 0 0.0e+00 / 0.0e+00
RMS 正規化 1 ずれ 2.22e-16(eps=1e-6 で 1.53e-04)

FlashAttention も線形 Attention も近似ではありません。 速いのは計算を変えたからではなく、前者は (T, T) の行列を作らないから、後者は同じ積を別の順で括るから。交差点は T == d で、比は T/d に沿って育ちますが T=2048 では式が言う 32 倍に届かず 15〜16 倍で頭打ちになります(二次の側が帯域律速。これは時間の測定なので機械に依ります)。

★外した予言を 2 つ残してあります: タイルを細かくするほど誤差が積もる(→ 64 倍のタイル数で 1.1 倍にしかならない)/ online softmax の途中経過は答えに単調に近づく(→ 単調ではない。走っている最大値が更新されるたびに分母が組み替わるので、途中の値は答えの近似ですらない)。

型の話

新語を 2 つ足しました。基準はこの repo 共通の 1 つ ——「混ぜたときに例外ではなく、もっともらしく間違った数値が出るか」。

どちらも backends_typed.TYPE_TO_SORT には入れていません。2-D レジストリへの自動の橋(tb_<op>)が架かると、注意の入力として意味の無い 1 枚の画像が渡されて「走ったが全面が同じ値」になるからです。

torch は使いません。 上の恒等式は全部 numpy で書けます。任意依存を要る op は CI の一部の Python にしか入らず、環境によって落ちます(2026-09-24 に実際に落として学びました)。