Token Lab搜索

IMPLEMENT → TEST → DEFEND

因果注意力与 KV cache 一致性

实现稳定 softmax、因果注意力与增量缓存,验证数值不变性。

学习指南与本课目录LEARNING CONTRACT

理解 → 实现 → 研究

目标:解释本课机制、复现一个例子、测试一个失败案例。读完不自动代表掌握。

先修与预测

学习先修课程 →

最后一个 value 从 7 改为 999,哪些更早输出可以变化?

提示 1:识别机制

写出每个 query 允许访问的 key 索引。

预测后展开分析

在这个因果单层计算中都不能变化,每个早期 query 只能访问自身前缀。

运行参考实现

{
  "baseline": [
    [
      1
    ],
    [
      2.3395230986533138
    ],
    [
      3.5104695304536615
    ],
    [
      2.980121288333647
    ]
  ],
  "incremental": [
    [
      1
    ],
    [
      2.3395230986533138
    ],
    [
      3.5104695304536615
    ],
    [
      2.980121288333647
    ]
  ],
  "cacheError": 0,
  "causalError": 0,
  "stableSoftmaxSum": 0.9999999999999999,
  "passes": true
}

初始输出在构建时实际计算;按钮会在浏览器重新计算。不涉及模型、GPU 或硬件。

完整实现与测试入口

将下面四个文件下载到同一目录,执行 node check.mjs。检查覆盖三个项目,无需安装依赖或 API Key。

// Node.js 18+ or modern browser; no dependencies, no random seed needed.
export function softmax(xs) {
  const m = Math.max(...xs);
  if (!Number.isFinite(m)) throw Error('All-masked or invalid attention row');
  const exp = xs.map(x => Math.exp(x - m)), sum = exp.reduce((a,b)=>a+b,0);
  return exp.map(x=>x/sum);
}
export function attend(q, keys, values) {
  if (!q.length || keys.length !== values.length || !keys.length) throw Error('Invalid shape');
  if (!keys.every(k=>k.length===q.length) || !values.every(v=>v.length===values[0].length)) throw Error('Shape mismatch');
  if (![q,...keys,...values].flat().every(Number.isFinite)) throw Error('Non-finite input');
  const weights = softmax(keys.map(k=>k.reduce((s,x,j)=>s+x*q[j],0)/Math.sqrt(q.length)));
  return values[0].map((_,j)=>weights.reduce((s,w,i)=>s+w*values[i][j],0));
}
function validateSequence(Q,K,V){
 if(![Q,K,V].every(Array.isArray)||!Q.length||Q.length!==K.length||K.length!==V.length)throw Error('Invalid sequence shape');
 if(!Q[0]?.length||!V[0]?.length||!Q.every(q=>Array.isArray(q)&&q.length===Q[0].length)||!K.every(k=>Array.isArray(k)&&k.length===Q[0].length)||!V.every(v=>Array.isArray(v)&&v.length===V[0].length))throw Error('Shape mismatch');
 if(![...Q,...K,...V].flat().every(Number.isFinite))throw Error('Non-finite input');
}
export function full(Q,K,V) {
  validateSequence(Q,K,V);
  if (Q.length!==K.length || K.length!==V.length || !Q.length) throw Error('Invalid sequence shape');
  return Q.map((q,t)=>{
    const scores=K.map((k,j)=>j>t?-Infinity:k.reduce((s,x,i)=>s+x*q[i],0)/Math.sqrt(q.length));
    const weights=softmax(scores);
    return V[0].map((_,i)=>weights.reduce((sum,w,j)=>sum+w*V[j][i],0));
  });
}
export function cached(Q,K,V) {
  validateSequence(Q,K,V);
  const keys=[], values=[];
  return Q.map((q,t)=>{keys.push(K[t]);values.push(V[t]);return attend(q,keys,values)});
}
export function run() {
  const Q=[[1,0],[0,1],[1,1],[.5,-1]], K=[[1,0],[0,1],[1,1],[-1,1]], V=[[1],[3],[5],[7]];
  const baseline=full(Q,K,V), incremental=cached(Q,K,V);
  const changedK=K.map(x=>[...x]), changedV=V.map(x=>[...x]);changedK[3]=[100,-100];changedV[3]=[999];
  const perturbed=full(Q,changedK,changedV);
  const cacheError=Math.max(...baseline.flat().map((x,i)=>Math.abs(x-incremental.flat()[i])));
  const causalError=Math.max(...baseline.slice(0,3).flat().map((x,i)=>Math.abs(x-perturbed.slice(0,3).flat()[i])));
  const stable=softmax([1000,1001,999]);
  return {baseline,incremental,cacheError,causalError,stableSoftmaxSum:stable.reduce((a,b)=>a+b,0),
    passes:cacheError<1e-12&&causalError<1e-12&&stable.every(Number.isFinite)};
}
查看可执行验收测试
import assert from 'node:assert/strict';
import {run as attention,softmax,attend,full,cached} from './attention.mjs';
import {run as retrieval,retrieve} from './retrieval.mjs';
import {run as control,simulate} from './control.mjs';
assert.ok(attention().passes);assert.throws(()=>softmax([-Infinity,-Infinity]));assert.throws(()=>attend([1,2],[[1]],[[1]]));
assert.equal(retrieval().baseline.accuracy,.4);assert.equal(retrieval().treatment.accuracy,1);assert.equal(retrieve('unseen vocabulary'),null);
const result=control();assert.ok(result.baseline.maxForce<=5);assert.ok(result.delayed.maxForce<=5);assert.equal(simulate({steps:1}).trace[0].x,0);assert.equal(simulate({steps:1}).trace[0].v,.05);assert.throws(()=>simulate({mass:0}));

// Full and cached paths must reject inconsistent shapes, not just the single-query helper.
assert.throws(()=>full([[1,2]],[[1]],[[1]]),/Shape mismatch/);
assert.throws(()=>cached([[1,2]],[],[]),/sequence shape/);

console.log('PASS: 12 capstone checks. Toy arithmetic and invariants only; no GPU or hardware benchmark.');

迁移任务与消融

给完整分数矩阵增加 padding 掩码,与增量前缀实现比较,并测试全掩码行。

评分标准

解释 Q/K 维度与 √d 缩放;实现无指数溢出;通过前缀/缓存不变性;解释权重为何不是因果归因。

四项分别按 0(缺失)、1(需帮助)、2(有独立证据)评分。每项均为 2 才过关,自评不是认证。

参考报告与自己的报告

参考配置:即 run() 中的常量,不使用随机采样。上方展示样例实测结果。先复现,再只改一个因素并记录影响。

这里只测试给定 Q/K/V,不含分词、位置编码或多层缓存集成。扩展多层前不能声称实现了 LLM 缓存。

Hypothesis:
Code revision / runtime:
Dataset or fixture version:
Baseline:
One-factor intervention:
Raw outputs and acceptance tests:
Negative result / failure case:
What this does NOT establish:
Next falsifiable hypothesis:
← 全部项目
MATHEMATICAL REVIEW · 2026-10-26

假设、参考实现与验收

固定自注意力,Q/K/V 有限且维度一致非空,逐行遮罩,无 dropout。前缀缓存须与完整因果输出一致;未来扰动不得改变较早输出。

已复核实现、不变性及声明的证据边界;保留原有验收测试。

运行前:预测一个边界情况

固定自注意力,Q/K/V 有限且维度一致非空,逐行遮罩,无 dropout。前缀缓存须与完整因果输出一致;未来扰动不得改变较早输出。

提示:隔离假设

固定基线输入,只改变一个参数或边界;同时比较数值与声明单位。单例通过仅验证该样例。

最小可运行算例

将算例及所链接模块保存在同一目录。使用 Node.js 22 或更高版本,然后运行 node project-attention.mjs. 无需第三方运行依赖。

import {run} from './project-attention-core.mjs';
console.log(run());

测试与迁移

固定自注意力,Q/K/V 有限且维度一致非空,逐行遮罩,无 dropout。前缀缓存须与完整因果输出一致;未来扰动不得改变较早输出。

在下方笔记记录输入、代码版本、期望值和实测值;再构造违反上述假设的反例,解释原结论为何不再成立。

下载完整代码包(.tar.gz) · 完整参考代码与回归检查

预测、检验与证据记录

展开答案前先预测一个结果;练习后记录差异和迁移测试。记录仅存本地,在不同语言版本间共用。

打开记录/导出/清除 →

数学复核范围逐篇记录,不代表完成论文实证复现;可在笔记中记录疑似错误。 复核范围