Add interactive HTML pages for planning explainer and spectral explainer
Sync to site1 / sync (push) Has been cancelled

- Created planning_explainer.html to demonstrate orthogonal invariance and optimal planning with interactive controls and visualizations.
- Created spectral_explainer.html to illustrate spectral decomposition and linear identifiability, featuring adjustable spectral weights and real-time correlation calculations.
This commit is contained in:
gaojie
2026-06-02 05:05:39 +08:00
parent 14ff05d9b8
commit 5de86388db
8 changed files with 1403 additions and 0 deletions
@@ -0,0 +1,178 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>谱分解与线性可识别性 — 交互式探索</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Spectral:ital,wght@0,400;0,600;1,400&family=Sora:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet" />
<style>
:root{--bg:#0e1116;--panel:#161b22;--panel-2:#1b222c;--line:#232b36;--txt:#e6edf3;--txt-2:#8b949e;--txt-3:#5c6672;--linear:#4cc9f0;--linear-soft:rgba(76,201,240,.16);--nl:#f4a261;--nl-soft:rgba(244,162,97,.16);--ok:#56d364;--r:14px;}
*{box-sizing:border-box;}html,body{margin:0;height:100%;}
body{background:radial-gradient(1200px 700px at 78% -8%,rgba(76,201,240,.06),transparent 60%),var(--bg);color:var(--txt);font-family:'Sora',system-ui,sans-serif;-webkit-font-smoothing:antialiased;overflow:hidden;}
.app{height:100vh;display:grid;grid-template-columns:320px 1fr 330px;grid-template-rows:auto 1fr;grid-template-areas:"head head head" "ctrl stage read";gap:18px;padding:22px 26px;}
header{grid-area:head;display:flex;align-items:baseline;gap:18px;flex-wrap:wrap;}
header .kicker{font:500 12px/1 'JetBrains Mono',monospace;letter-spacing:.32em;color:var(--linear);text-transform:uppercase;}
header h1{margin:0;font-family:'Spectral',serif;font-weight:600;font-size:30px;}
header h1 em{color:var(--txt-2);font-style:italic;font-weight:400;}
header .sub{font-size:13px;color:var(--txt-2);max-width:560px;line-height:1.5;}
.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--r);}
.controls{grid-area:ctrl;padding:20px;display:flex;flex-direction:column;gap:20px;overflow-y:auto;}
.block .label{font:600 11px/1 'JetBrains Mono',monospace;letter-spacing:.18em;color:var(--txt-3);text-transform:uppercase;margin-bottom:10px;}
.srow{display:flex;align-items:center;gap:12px;margin-bottom:6px;}
.srow .nm{font:500 12px 'JetBrains Mono',monospace;width:78px;}
.srow .nm.d1{color:var(--linear);}.srow .nm.dN{color:var(--nl);}
.srow .vv{font:600 12px 'JetBrains Mono',monospace;color:var(--txt-2);width:42px;text-align:right;}
input[type=range]{-webkit-appearance:none;flex:1;height:4px;border-radius:4px;background:#2a323d;cursor:pointer;}
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:16px;height:16px;border-radius:50%;background:#eaf6fb;border:3px solid var(--linear);}
input[type=range]::-moz-range-thumb{width:16px;height:16px;border-radius:50%;background:#eaf6fb;border:3px solid var(--linear);}
input.nl::-webkit-slider-thumb{border-color:var(--nl);}input.nl::-moz-range-thumb{border-color:var(--nl);}
.hint{font-size:12px;color:var(--txt-3);line-height:1.55;margin-top:8px;}
.preset{display:flex;gap:6px;flex-wrap:wrap;margin-top:6px;}
.preset button{flex:1;padding:8px 6px;border-radius:8px;border:1px solid var(--line);background:var(--panel-2);color:var(--txt-2);font:500 11px 'Sora';cursor:pointer;}
.preset button:hover{border-color:var(--linear);color:var(--linear);}
.big-corr{font:700 34px/1 'JetBrains Mono',monospace;}
.stage{grid-area:stage;padding:18px 20px 16px;display:flex;flex-direction:column;gap:14px;min-height:0;}
.stage .ttl h2{margin:0;font-family:'Spectral',serif;font-weight:600;font-size:18px;}
.canvas-wrap{position:relative;flex:1;min-height:0;border-radius:10px;overflow:hidden;background:#0b0e13;border:1px solid var(--line);}
canvas{display:block;width:100%;height:100%;}
.roadmap{flex:none;display:flex;gap:8px;}
.step{flex:1;padding:10px 8px;border-radius:9px;border:1px solid var(--line);background:#0b0e13;font-size:11px;line-height:1.35;color:var(--txt-3);transition:.25s;}
.step.on{border-color:var(--linear);color:var(--txt);background:var(--linear-soft);}
.step b{display:block;font:700 11px 'JetBrains Mono',monospace;color:var(--linear);margin-bottom:3px;}
.reading{grid-area:read;padding:22px;display:flex;flex-direction:column;gap:16px;overflow-y:auto;}
.reading h3{margin:0;font-family:'Spectral',serif;font-weight:600;font-size:17px;}
.formula{font:500 14px 'JetBrains Mono',monospace;background:#0b0e13;border:1px solid var(--line);border-radius:8px;padding:14px 16px;line-height:1.7;}
.formula .hi{color:var(--linear);}.formula .hl{color:var(--nl);}
.verdict{margin-top:auto;padding:16px;border-radius:10px;font-family:'Spectral',serif;font-size:14px;line-height:1.6;}
.verdict.win{background:linear-gradient(135deg,rgba(86,211,100,.16),transparent 70%);border:1px solid rgba(86,211,100,.4);}
.verdict.lose{background:linear-gradient(135deg,var(--nl-soft),transparent 70%);border:1px solid rgba(244,162,97,.4);}
.verdict b{color:var(--linear);}
.src{font:400 11px 'JetBrains Mono',monospace;color:var(--txt-3);}
.navbar{position:fixed;bottom:14px;left:50%;transform:translateX(-50%);display:flex;gap:6px;background:rgba(22,27,34,.9);border:1px solid var(--line);border-radius:30px;padding:6px 10px;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);z-index:20;}
.navbar a{font:500 12px 'JetBrains Mono',monospace;color:var(--txt-2);text-decoration:none;padding:5px 11px;border-radius:20px;}
.navbar a:hover{color:var(--linear);}.navbar a.on{background:var(--linear-soft);color:var(--linear);}
::-webkit-scrollbar{width:8px;height:8px;}::-webkit-scrollbar-thumb{background:#2a323d;border-radius:8px;}
@media (max-width:1080px){.app{grid-template-columns:1fr;grid-template-rows:repeat(4,auto);grid-template-areas:"head" "ctrl" "stage" "read";overflow:auto;height:auto;}body{overflow:auto;}.canvas-wrap{height:300px;flex:none;}}
</style>
</head>
<body>
<div class="app">
<header>
<div style="display:flex;flex-direction:column;gap:6px;">
<span class="kicker">LeJEPA · Topic 3 · 定理 1</span>
<h1>谱分解 <em>与线性可识别性</em></h1>
</div>
<p class="sub">调节编码器的谱权重 <b style="color:var(--linear)">w₁</b>/<b style="color:var(--nl)">w₂,w₃</b>(自动归一化),实时计算相关性 <b>corr = Σ wᵈ·ρᵈ</b>,看它何时能触到上界 ρ——只有纯线性(w₁=1)才能取等。</p>
</header>
<section class="card controls">
<div class="block">
<div class="label">谱权重 wᵈ(Σ=1 自动归一化)</div>
<div class="srow"><span class="nm d1">w₁ 线性</span><input id="w1" class="lin" type="range" min="0" max="1" step="0.01" value="1" aria-label="w1" title="w1"/><span class="vv" id="v1">1.00</span></div>
<div class="srow"><span class="nm dN">w₂ 二次</span><input id="w2" class="nl" type="range" min="0" max="1" step="0.01" value="0" aria-label="w2" title="w2"/><span class="vv" id="v2">0.00</span></div>
<div class="srow"><span class="nm dN">w₃ 三次</span><input id="w3" class="nl" type="range" min="0" max="1" step="0.01" value="0" aria-label="w3" title="w3"/><span class="vv" id="v3">0.00</span></div>
<div class="preset">
<button data-p="1,0,0">纯线性</button>
<button data-p="0,1,0">纯二次</button>
<button data-p="0.5,0.5,0">各半</button>
<button data-p="0.6,0.25,0.15">混合</button>
</div>
<div class="hint">谱权重 = 编码器各阶非线性成分占总方差的比例。w₀=0(零均值),Σwᵈ=1(单位方差)。</div>
</div>
<div class="block">
<div class="label">相关性 ρ</div>
<div class="srow"><span class="nm">ρ</span><input id="rho" class="lin" type="range" min="0.05" max="0.97" step="0.01" value="0.90" aria-label="rho" title="rho"/><span class="vv" id="vr">0.90</span></div>
</div>
<div class="block">
<div class="label">当前相关性 corr</div>
<div class="big-corr" id="corrTxt">0.900</div>
<div class="hint">上界 = ρ。差距 = ρ − corr ≥ 0,仅纯线性时为 0。</div>
</div>
</section>
<section class="card stage">
<div class="ttl"><h2>corr = Σ wᵈ·ρᵈ 相对上界 ρ</h2></div>
<div class="canvas-wrap"><canvas id="cv"></canvas></div>
<div class="roadmap" id="roadmap"></div>
</section>
<section class="card reading">
<h3>关键不等式</h3>
<div class="formula">corr = Σ<sub>d≥1</sub> w<sub>d</sub><span class="hl">ρ<sup>d</sup></span> ≤ Σ w<sub>d</sub><span class="hi">ρ</span> = <span class="hi">ρ</span><br><br>等号 ⟺ ∀d≥2: w<sub>d</sub>=0 ⟺ <span class="hi">w₁=1</span></div>
<h3 style="margin-top:2px;">从相关到正交</h3>
<div class="formula">corr=ρ → h(z)=Az(线性)<br>h(z)~N(0,I) → AAᵀ=I<br><span class="hi">h(z)=Qz, Q∈O(n)</span></div>
<div class="verdict win" id="verdict"></div>
<div class="src">来源:JEPA/math/03_spectral_identifiability.md</div>
</section>
</div>
<nav class="navbar" id="nav"></nav>
<script>
const NAV=[["01","Hermite","hermite_explainer.html"],["02","OU·Mehler","ou_mehler_explainer.html"],["03","谱分解","spectral_explainer.html"],["04","高斯唯一","gaussian_uniqueness_explainer.html"],["05","近似界","approx_bound_explainer.html"],["06","规划等价","planning_explainer.html"]];
const nav=document.getElementById('nav');NAV.forEach(([n,t,f])=>{const a=document.createElement('a');a.href=f;a.textContent=n+' '+t;if(f==='spectral_explainer.html')a.className='on';nav.appendChild(a);});
const STEPS=[["步骤1","Hermite 展开 hᵢ=ΣcₐHeₐ"],["步骤2","Mehler: corr=Σwᵈρᵈ"],["步骤3","corr≤ρ, 等号⟺w₁=1"],["步骤4","L=2(1−ρ)n 最优"],["步骤5","每个 hᵢ 线性"],["步骤6","AAᵀ=I → 正交 Q"]];
const rm=document.getElementById('roadmap');STEPS.forEach(([b,t])=>{const d=document.createElement('div');d.className='step';d.innerHTML='<b>'+b+'</b>'+t;rm.appendChild(d);});
let w=[1,0,0], rho=0.90;
const ids=['w1','w2','w3'], vids=['v1','v2','v3'];
function readW(changed){
let raw=ids.map(id=>+document.getElementById(id).value);
let s=raw[0]+raw[1]+raw[2];
if(s<=0){raw=[1,0,0];s=1;}
w=raw.map(x=>x/s);
ids.forEach((id,i)=>{document.getElementById(id).value=w[i];document.getElementById(vids[i]).textContent=w[i].toFixed(2);});
draw();
}
ids.forEach(id=>document.getElementById(id).addEventListener('input',()=>readW(id)));
document.querySelectorAll('.preset button').forEach(b=>b.addEventListener('click',()=>{const p=b.dataset.p.split(',').map(Number);ids.forEach((id,i)=>document.getElementById(id).value=p[i]);readW();}));
const rhoEl=document.getElementById('rho');
rhoEl.addEventListener('input',e=>{rho=+e.target.value;document.getElementById('vr').textContent=rho.toFixed(2);draw();});
const cv=document.getElementById('cv'),ctx=cv.getContext('2d');
function fit(){const r=cv.getBoundingClientRect(),dpr=devicePixelRatio||1;cv.width=r.width*dpr;cv.height=r.height*dpr;return{w:r.width,h:r.height,dpr};}
function corrVal(){return w[0]*rho+w[1]*rho*rho+w[2]*rho*rho*rho;}
function draw(){
const {w:W,h:H,dpr}=fit();ctx.setTransform(dpr,0,0,dpr,0,0);ctx.clearRect(0,0,W,H);
const padL=70,padR=40,padT=30,padB=40,x0=padL,x1=W-padR,y0=padT,y1=H-padB;
const sy=v=>y1-v*(y1-y0);
// grid
ctx.strokeStyle='#1c2530';ctx.lineWidth=1;ctx.font="11px 'JetBrains Mono',monospace";ctx.fillStyle='#5c6672';
for(let g=0;g<=10;g++){const v=g/10,py=sy(v);ctx.beginPath();ctx.moveTo(x0,py);ctx.lineTo(x1,py);ctx.stroke();ctx.fillText(v.toFixed(1),x0-34,py+4);}
// 上界 ρ 虚线
ctx.strokeStyle='var(--linear)';ctx.strokeStyle='#4cc9f0';ctx.setLineDash([6,5]);ctx.lineWidth=1.8;ctx.beginPath();ctx.moveTo(x0,sy(rho));ctx.lineTo(x1,sy(rho));ctx.stroke();ctx.setLineDash([]);
ctx.fillStyle='#4cc9f0';ctx.font="600 13px 'JetBrains Mono',monospace";ctx.fillText('上界 ρ = '+rho.toFixed(2),x1-150,sy(rho)-8);
// 三个堆叠贡献柱: w1ρ, w2ρ², w3ρ³ 累加
const cx=(x0+x1)/2, bw=140;
const parts=[[w[0]*rho,'#4cc9f0','w₁ρ'],[w[1]*rho*rho,'#f4a261','w₂ρ²'],[w[2]*rho*rho*rho,'#e76f51','w₃ρ³']];
let acc=0;
parts.forEach(([val,col,lab])=>{if(val<=0)return;const yT=sy(acc+val),yB=sy(acc);ctx.fillStyle=col;ctx.globalAlpha=.9;ctx.fillRect(cx-bw/2,yT,bw,yB-yT);ctx.globalAlpha=1;
if(yB-yT>18){ctx.fillStyle='#0b0e13';ctx.font="700 12px 'JetBrains Mono',monospace";ctx.textAlign='center';ctx.fillText(lab+'='+val.toFixed(3),cx,(yT+yB)/2+4);ctx.textAlign='left';}acc+=val;});
// corr 顶标
const c=corrVal();
ctx.strokeStyle='#56d364';ctx.lineWidth=2;ctx.beginPath();ctx.moveTo(cx-bw/2-20,sy(c));ctx.lineTo(cx+bw/2+20,sy(c));ctx.stroke();
ctx.fillStyle='#56d364';ctx.font="700 14px 'JetBrains Mono',monospace";ctx.textAlign='center';ctx.fillText('corr = '+c.toFixed(3),cx,sy(c)-9);ctx.textAlign='left';
// gap 标注
const gap=rho-c;
ctx.fillStyle=gap<1e-4?'#56d364':'#f4a261';ctx.font="500 12px 'JetBrains Mono',monospace";
ctx.fillText('gap = ρ corr = '+gap.toFixed(3),x0+6,y0+16);
document.getElementById('corrTxt').textContent=c.toFixed(3);
document.getElementById('corrTxt').style.color=gap<1e-4?'#56d364':'#f4a261';
// 路线图高亮:纯线性点亮全部 6 步
const pure=w[0]>0.999;
STEPS.forEach((_,i)=>{rm.children[i].classList.toggle('on', pure ? true : i<3);});
const v=document.getElementById('verdict');
if(pure){v.className='verdict win';v.innerHTML='<b>corr = ρ,触到上界!</b> 编码器是纯线性的 h(z)=Az,叠加高斯约束 AAᵀ=I → <b>h(z)=Qz(正交)</b>。这正是<strong>线性可识别性</strong>(定理 1)。';}
else{v.className='verdict lose';v.innerHTML='存在 d≥2 的非线性权重 → corr < ρ,<b style="color:var(--nl)">gap = '+gap.toFixed(3)+'</b>。对齐损失未达最优,编码器不是最优解。把 <b style="color:var(--linear)">w₁ 拉到 1</b> 试试。';}
}
window.addEventListener('resize',draw);
readW();draw();
</script>
</body>
</html>