Files
gaojie 5de86388db
Sync to site1 / sync (push) Has been cancelled
Add interactive HTML pages for planning explainer and spectral explainer
- 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.
2026-06-02 05:05:39 +08:00

177 lines
14 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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:300px 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:22px;overflow-y:auto;}
.block .label{font:600 11px/1 'JetBrains Mono',monospace;letter-spacing:.18em;color:var(--txt-3);text-transform:uppercase;margin-bottom:12px;}
.aval{font:700 40px/1 'JetBrains Mono',monospace;color:var(--linear);}
.aval small{font-size:14px;color:var(--txt-2);font-weight:400;}
input[type=range]{-webkit-appearance:none;width:100%;height:4px;border-radius:4px;background:linear-gradient(90deg,var(--linear) 0%,var(--linear) var(--fill,25%),#2a323d var(--fill,25%));margin-top:14px;cursor:pointer;}
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:18px;height:18px;border-radius:50%;background:#eaf6fb;border:3px solid var(--linear);box-shadow:0 0 0 4px var(--linear-soft);}
input[type=range]::-moz-range-thumb{width:18px;height:18px;border-radius:50%;background:#eaf6fb;border:3px solid var(--linear);}
.hint{font-size:12px;color:var(--txt-3);line-height:1.55;margin-top:10px;}
.toggle{display:flex;gap:8px;}
.toggle button{flex:1;padding:11px 8px;border-radius:10px;border:1px solid var(--line);background:var(--panel-2);color:var(--txt-2);font:500 12px 'Sora';cursor:pointer;transition:.16s;line-height:1.3;}
.toggle button.on{border-color:var(--linear);color:var(--linear);background:var(--linear-soft);}
.play{margin-top:4px;display:flex;gap:8px;}
.play button{flex:1;padding:10px;border-radius:10px;border:1px solid var(--line);background:var(--panel-2);color:var(--txt);font:500 13px 'Sora';cursor:pointer;}
.play button.live{background:var(--linear-soft);border-color:var(--linear);color:var(--linear);}
.costcmp{display:flex;flex-direction:column;gap:8px;}
.costcmp .row{display:flex;justify-content:space-between;align-items:baseline;padding:9px 12px;border-radius:9px;border:1px solid var(--line);background:var(--panel-2);font:500 13px 'JetBrains Mono',monospace;}
.costcmp .row b{font-weight:700;}
.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;}
.twin{flex:1;display:grid;grid-template-columns:1fr 1fr;gap:14px;min-height:0;}
.canvas-wrap{position:relative;min-height:0;border-radius:10px;overflow:hidden;background:#0b0e13;border:1px solid var(--line);}
.canvas-wrap .cap{position:absolute;top:9px;left:13px;font:500 12px 'JetBrains Mono',monospace;color:var(--txt-2);}
canvas{display:block;width:100%;height:100%;}
.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 13.5px '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(--ok);}
.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;}.twin{height:360px;flex:none;}}
</style>
</head>
<body>
<div class="app">
<header>
<div style="display:flex;flex-direction:column;gap:6px;">
<span class="kicker">LeJEPA · Topic 6 · 定理 4</span>
<h1>正交不变性 <em>与最优规划</em></h1>
</div>
<p class="sub">学到的潜空间 <b style="color:var(--nl)">ẑ = Qz</b> 只是真实空间 <b style="color:var(--linear)">z</b> 的旋转。旋转角 θ 任意,只要代价函数是 <b>O(n)-不变的</b>,两边规划的总代价就完全相同。</p>
</header>
<section class="card controls">
<div class="block">
<div class="label">旋转角 θ(Q ∈ O(n)</div>
<div class="aval"><span id="thTxt">45</span><small>°</small></div>
<input id="theta" type="range" min="0" max="180" step="1" value="45" aria-label="旋转角 θ" title="旋转角 θ" />
<div class="hint">ẑ = Qz,Q 是旋转 θ 的正交矩阵。无论 θ 多少,点云形状不变(保距离、保角度)。</div>
</div>
<div class="block">
<div class="label">代价函数类型</div>
<div class="toggle">
<button id="btnInv" class="on">O(n)-不变<br>‖zg‖²(距离)</button>
<button id="btnNon">非不变<br>z₁²(坐标)</button>
</div>
<div class="hint">距离类代价旋转不变 → 定理4成立;坐标类代价依赖方向 → 不适用。</div>
</div>
<div class="block">
<div class="label">总规划代价对比</div>
<div class="costcmp">
<div class="row"><span style="color:var(--linear)">真实空间 J(z)</span><b id="cz" style="color:var(--linear)"></b></div>
<div class="row"><span style="color:var(--nl)">潜空间 J(ẑ)</span><b id="czh" style="color:var(--nl)"></b></div>
</div>
</div>
<div class="block">
<div class="label">演示</div>
<div class="play"><button id="autoBtn" class="live">⏸ 自动旋转 θ</button></div>
</div>
</section>
<section class="card stage">
<div class="ttl"><h2>两个空间中的最优路径(start → goal)</h2></div>
<div class="twin">
<div class="canvas-wrap"><div class="cap" style="color:var(--linear)">真实潜空间 z</div><canvas id="left"></canvas></div>
<div class="canvas-wrap"><div class="cap" style="color:var(--nl)">学到的潜空间 ẑ = Qz</div><canvas id="right"></canvas></div>
</div>
</section>
<section class="card reading">
<h3>代价等价(核心)</h3>
<div class="formula">(<span class="hl">Qz</span>, a) = (z, a) <span style="color:var(--txt-3)">O(n)-不变)</span><br><br>J(a; <span class="hl">Qz₀</span>) = J(a; z₀)<br><span class="hi">â* = a* 且 V̂* = V*</span></div>
<h3 style="margin-top:2px;">为什么足够?</h3>
<div class="formula">正交变换保持:<br>‖Qz₁−Qz₂‖ = ‖z₁−z₂‖<br>⟨Qz₁,Qz₂⟩ = ⟨z₁,z₂⟩</div>
<div class="verdict win" id="verdict"></div>
<div class="src">来源:JEPA/math/06_planning_equivalence.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==='planning_explainer.html')a.className='on';nav.appendChild(a);});
let theta=45, invariant=true, auto=true, lastTick=performance.now();
// 真实空间的 start/goal(固定)
const start=[-1.6,-1.1], goal=[1.7,1.3];
function rot(p,deg){const r=deg*Math.PI/180,c=Math.cos(r),s=Math.sin(r);return [c*p[0]-s*p[1], s*p[0]+c*p[1]];}
// O(n)-不变代价:路径长度 ‖goal-start‖(直线);非不变:沿路径 z₁² 积分(近似端点平方和)
function pathCost(s,g,inv){
if(inv){const dx=g[0]-s[0],dy=g[1]-s[1];return Math.sqrt(dx*dx+dy*dy);}
// 非不变:对 z₁ 维度的惩罚,沿直线采样 z₁² 平均
let sum=0,n=20;for(let i=0;i<=n;i++){const t=i/n;const x=s[0]+(g[0]-s[0])*t;sum+=x*x;}return sum/(n+1);
}
const thEl=document.getElementById('theta'),thTxt=document.getElementById('thTxt');
function setTheta(v,fromUser){theta=((v%360)+360)%360;if(theta>180)theta=180;thEl.value=theta;thTxt.textContent=Math.round(theta);thEl.style.setProperty('--fill',(theta/180*100).toFixed(1)+'%');if(fromUser){auto=false;const b=document.getElementById('autoBtn');b.classList.remove('live');b.textContent='▶ 自动旋转 θ';}draw();}
thEl.addEventListener('input',e=>setTheta(+e.target.value,true));
document.getElementById('btnInv').addEventListener('click',()=>{invariant=true;document.getElementById('btnInv').classList.add('on');document.getElementById('btnNon').classList.remove('on');draw();});
document.getElementById('btnNon').addEventListener('click',()=>{invariant=false;document.getElementById('btnNon').classList.add('on');document.getElementById('btnInv').classList.remove('on');draw();});
document.getElementById('autoBtn').addEventListener('click',e=>{auto=!auto;e.target.classList.toggle('live',auto);e.target.textContent=auto?'⏸ 自动旋转 θ':'▶ 自动旋转 θ';lastTick=performance.now();});
const lC=document.getElementById('left'),lx=lC.getContext('2d');
const rC=document.getElementById('right'),rx=rC.getContext('2d');
function fit(cv){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 drawSpace(ctx,cv,s,g,col,showAxis){
const {w,h,dpr}=fit(cv);ctx.setTransform(dpr,0,0,dpr,0,0);ctx.clearRect(0,0,w,h);
const pad=34,x0=pad,x1=w-pad,y0=pad,y1=h-pad,lim=2.6;
const sx=z=>x0+(z+lim)/(2*lim)*(x1-x0),sy=z=>y1-(z+lim)/(2*lim)*(y1-y0);
ctx.strokeStyle='#1c2530';ctx.lineWidth=1;for(let gd=-2;gd<=2;gd++){ctx.beginPath();ctx.moveTo(sx(gd),y0);ctx.lineTo(sx(gd),y1);ctx.stroke();ctx.beginPath();ctx.moveTo(x0,sy(gd));ctx.lineTo(x1,sy(gd));ctx.stroke();}
ctx.strokeStyle='#33404e';ctx.beginPath();ctx.moveTo(x0,sy(0));ctx.lineTo(x1,sy(0));ctx.moveTo(sx(0),y0);ctx.lineTo(sx(0),y1);ctx.stroke();
// 非不变代价的方向场(z₁² 等高线 = 竖条阴影)
if(!invariant){for(let xx=-2;xx<=2;xx+=0.25){const a=Math.min(.22,xx*xx*0.06);ctx.fillStyle='rgba(244,162,97,'+a+')';ctx.fillRect(sx(xx),y0,(x1-x0)/(2*lim)*0.25,y1-y0);}}
// 最优路径直线
ctx.strokeStyle=col;ctx.lineWidth=3;ctx.setLineDash([]);ctx.beginPath();ctx.moveTo(sx(s[0]),sy(s[1]));ctx.lineTo(sx(g[0]),sy(g[1]));ctx.stroke();
// start/goal
ctx.fillStyle='#56d364';ctx.beginPath();ctx.arc(sx(s[0]),sy(s[1]),7,0,7);ctx.fill();ctx.fillStyle='#0b0e13';ctx.font="700 10px 'JetBrains Mono'";ctx.textAlign='center';ctx.fillText('S',sx(s[0]),sy(s[1])+3);
ctx.fillStyle=col;ctx.beginPath();ctx.arc(sx(g[0]),sy(g[1]),7,0,7);ctx.fill();ctx.fillStyle='#0b0e13';ctx.fillText('G',sx(g[0]),sy(g[1])+3);ctx.textAlign='left';
}
function draw(){
const sR=rot(start,theta), gR=rot(goal,theta);
drawSpace(lx,lC,start,goal,'#4cc9f0',true);
drawSpace(rx,rC,sR,gR,'#f4a261',true);
const cz=pathCost(start,goal,invariant), czh=pathCost(sR,gR,invariant);
document.getElementById('cz').textContent=cz.toFixed(3);
document.getElementById('czh').textContent=czh.toFixed(3);
const eq=Math.abs(cz-czh)<1e-3;
const v=document.getElementById('verdict');
if(invariant){v.className='verdict win';v.innerHTML='<b>O(n)-不变代价:J(z) = J(ẑ) = '+cz.toFixed(3)+'</b><br>无论旋转角 θ 多少,两空间总代价完全相等 → 最优动作序列 â* = a*。<strong>线性可识别性足以支持最优规划</strong>。';}
else{v.className='verdict lose';v.innerHTML='非不变代价 z₁²:J(z) = <b style="color:var(--linear)">'+cz.toFixed(3)+'</b> ≠ J(ẑ) = <b style="color:var(--nl)">'+czh.toFixed(3)+'</b>。<br>代价依赖坐标方向 → 旋转后改变 → <b style="color:var(--nl)">定理4 不适用</b>。这类代价超出 O(n)-不变范围。';}
}
function loop(now){if(auto){const dt=(now-lastTick)/1000;lastTick=now;let v=theta+dt*30;if(v>180)v=0;setTheta(v,false);}else lastTick=now;requestAnimationFrame(loop);}
document.addEventListener('keydown',e=>{if(e.code==='Space'){e.preventDefault();document.getElementById('autoBtn').click();}else if(e.key==='ArrowLeft')setTheta(theta-5,true);else if(e.key==='ArrowRight')setTheta(theta+5,true);});
window.addEventListener('resize',draw);
setTheta(45,false);draw();requestAnimationFrame(loop);
</script>
</body>
</html>