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
+2
View File
@@ -3,6 +3,8 @@
> 本目录将论文 *When Does LeJEPA Learn a World Model?* 的数学证明拆分为 6 个独立 topic,每个 topic 专注一个概念,循序渐进。
>
> **建议阅读顺序:** Topic 1 → 2 → 3 → 4 → 5 → 6
>
> 🎬 **交互式动画:** 每个 topic 都配有可拖动参数的交互式可视化,见 [`animations/`](animations/README.md)。
---
+53
View File
@@ -0,0 +1,53 @@
# LeJEPA 数学原理 · 交互式动画
> 配套 [`JEPA/math/`](../) 六个 topic 的交互式可视化。每个页面均为**单文件 HTML**,无需构建,双击或在浏览器打开即可;自带 ρ/α/θ 等滑块、自动演示与键盘热键。
---
## 📺 动画清单
| # | 动画 | 对应 topic | 核心交互 |
|---|------|-----------|---------|
| 1 | [Hermite 多项式](hermite_explainer.html) | [01](../01_hermite_polynomials.md) | 拖 ρ、叠加阶数,看 Heₙ(z) 曲线与 ρᵈ 衰减条 |
| 2 | [OU 过程与 Mehler](ou_mehler_explainer.html) | [02](../02_ou_process_mehler.md) | 拖 ρ 看 (z,z′) 联合散点 + 平稳性统计 + ρᵈ 条 |
| 3 | [谱分解(定理1](spectral_explainer.html) | [03](../03_spectral_identifiability.md) | 调谱权重 w₁/w₂/w₃,看 corr=Σwᵈρᵈ 触界 + 6 步路线图 |
| 4 | [高斯唯一性(定理2](gaussian_uniqueness_explainer.html) | [04](../04_sturm_liouville_uniqueness.md) | 扫描 α,看密度/得分线性度/R²(α) 在 α=2 尖峰 |
| 5 | [近似界(定理3](approx_bound_explainer.html) | [05](../05_approximate_identifiability.md) | 拖 δ/ε/ρ,看界 D+(ε+D)² 的一阶/二阶构成 |
| 6 | [规划等价(定理4](planning_explainer.html) | [06](../06_planning_equivalence.md) | 旋转 θ,双空间路径 + 切换 O(n)-不变/非不变代价 |
---
## ⌨️ 通用操作
| 操作 | 作用 |
|------|------|
| 拖动滑块 | 调节核心参数(ρ / α / δ,ε,ρ / θ),实时重绘 |
| `空格` | 播放/暂停自动演示 |
| `← / →` | 微调当前主参数 |
| `0` | 复位(部分页面) |
| 底部胶囊导航 | 在 6 个动画之间跳转 |
---
## 🎨 设计说明
- **调性**:科学可视化工具风(暗色绘图区 + 学术冷色),非营销页。
- **语义配色**:青蓝 = 线性/想要的成分(d=1)、暖橙 = 高阶非线性(被衰减)、绿色 = 成立/最优。
- **字体**`Spectral`(标题)/`Sora`(界面)/`JetBrains Mono`(公式与数据)。
- 所有曲线/散点用 Canvas 实时绘制,适配高分屏(devicePixelRatio)。
---
## 🧩 一条主线串起六个动画
```
01 Hermite 分解 ─→ 02 OU 衰减 ρᵈ ─→ 03 corr≤ρ 等号⟺线性(定理1)
04 只有高斯让得分线性(定理2,逆向)
05 近似时误差 ≤ D+(ε+D)²(定理3)
06 正交等价 → 规划等价(定理4,应用)
```
> 核心信息:**LeJEPA 在高斯世界中可证明地学到世界模型**——线性可识别性使学到的潜空间在任意正交不变代价下与真实世界等价。
@@ -0,0 +1,176 @@
<!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:4px;}
.srow .nm{font:500 12px 'JetBrains Mono',monospace;width:96px;}
.srow .nm.dl{color:var(--linear);}.srow .nm.dn{color:var(--nl);}
.srow .vv{font:600 12px 'JetBrains Mono',monospace;color:var(--txt-2);width:46px;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.dn::-webkit-slider-thumb{border-color:var(--nl);}input.dn::-moz-range-thumb{border-color:var(--nl);}
.hint{font-size:12px;color:var(--txt-3);line-height:1.55;margin-top:8px;}
.bigbound{font:700 30px/1 'JetBrains Mono',monospace;color:var(--nl);}
.preset{display:flex;gap:6px;flex-wrap:wrap;margin-top:8px;}
.preset button{flex:1 1 45%;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);}
.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-wrap .cap{position:absolute;top:9px;left:13px;font:400 12px 'JetBrains Mono',monospace;color:var(--txt-3);}
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 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);}
.legend{display:flex;flex-direction:column;gap:8px;}
.legend div{display:flex;align-items:center;gap:9px;font-size:12.5px;color:var(--txt-2);}
.legend i{width:13px;height:13px;border-radius:3px;flex:none;}
.verdict{margin-top:auto;padding:16px;border-radius:10px;background:linear-gradient(135deg,var(--linear-soft),transparent 70%);border:1px solid color-mix(in oklch,var(--linear) 40%,var(--line));font-family:'Spectral',serif;font-size:14px;line-height:1.6;}
.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:320px;flex:none;}}
</style>
</head>
<body>
<div class="app">
<header>
<div style="display:flex;flex-direction:column;gap:6px;">
<span class="kicker">LeJEPA · Topic 5 · 定理 3</span>
<h1>近似可识别性界 <em>优雅降级</em></h1>
</div>
<p class="sub">当对齐间隙 <b style="color:var(--nl)">δ</b> 与白化误差 <b style="color:var(--nl)">ε</b> 不为零时,恢复误差被界 <b>D + (ε+D)²</b> 控制(D = δ/(2ρ(1−ρ)))。拖动三个参数,观察 δ 是主因、ε 仅二阶。</p>
</header>
<section class="card controls">
<div class="block">
<div class="label">误差参数</div>
<div class="srow"><span class="nm dn">δ 对齐间隙</span><input id="delta" class="dn" type="range" min="0" max="0.3" step="0.002" value="0.018" aria-label="delta" title="delta"/><span class="vv" id="vd">0.018</span></div>
<div class="srow"><span class="nm dn">ε 白化误差</span><input id="eps" class="dn" type="range" min="0" max="1" step="0.01" value="0.1" aria-label="epsilon" title="epsilon"/><span class="vv" id="ve">0.10</span></div>
<div class="srow"><span class="nm dl">ρ 相关性</span><input id="rho" type="range" min="0.5" max="0.95" step="0.01" value="0.90" aria-label="rho" title="rho"/><span class="vv" id="vr">0.90</span></div>
<div class="preset">
<button data-p="0,0,0.9">完美 (δ=ε=0)</button>
<button data-p="0.018,0.1,0.9">典型</button>
<button data-p="0.018,0.5,0.9">大白化误差</button>
<button data-p="0.18,0,0.9">大对齐间隙</button>
</div>
<div class="hint">δ=0 且 ε=0 时界=0,退化为定理1的精确恢复 h(z)=Qz。</div>
</div>
<div class="block">
<div class="label">归一化量 D = δ / (2ρ(1−ρ))</div>
<div class="srow"><span class="nm">谱间隙 2ρ(1−ρ)</span><span class="vv" id="sg" style="width:auto;">0.18</span></div>
<div class="srow"><span class="nm">D</span><span class="vv" id="dD" style="width:auto;color:var(--nl);">0.10</span></div>
<div class="hint">ρ 越大,2ρ(1−ρ) 越小,D 对 δ 越敏感。</div>
</div>
<div class="block">
<div class="label">误差上界</div>
<div class="bigbound" id="boundTxt">0.110</div>
<div class="hint">E[‖h(z)Qz‖²] ≤ D + (ε+D)²</div>
</div>
</section>
<section class="card stage">
<div class="ttl"><h2>误差界的构成:D(一阶)+ (ε+D)²(二阶)</h2></div>
<div class="canvas-wrap"><div class="cap">堆叠条 = 界的两部分;右侧曲面 = 界随 δ 的增长</div><canvas id="cv"></canvas></div>
</section>
<section class="card reading">
<h3>定理 3 的界</h3>
<div class="formula">E[‖h(z)Qz‖²]<br>&nbsp;<span class="hl">D</span> + (<span class="hl">ε</span>+<span class="hl">D</span><br><br>D = <span class="hl">δ</span> / (2<span class="hi">ρ</span>(1<span class="hi">ρ</span>))</div>
<div class="legend">
<div><i style="background:#f4a261"></i>D:来自对齐间隙 δ(线性主导项)</div>
<div><i style="background:#e76f51"></i>(ε+D)²:白化与 Procrustes 误差(二阶)</div>
</div>
<div class="verdict" id="verdict"></div>
<div class="src">来源:JEPA/math/05_approximate_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==='approx_bound_explainer.html')a.className='on';nav.appendChild(a);});
let delta=0.018, eps=0.1, rho=0.90;
function D(){return delta/(2*rho*(1-rho));}
function bound(){const d=D();return d+(eps+d)*(eps+d);}
const map={delta:['vd',v=>v.toFixed(3),x=>delta=x],eps:['ve',v=>v.toFixed(2),x=>eps=x],rho:['vr',v=>v.toFixed(2),x=>rho=x]};
Object.keys(map).forEach(id=>{const[vid,fmt,set]=map[id];document.getElementById(id).addEventListener('input',e=>{set(+e.target.value);document.getElementById(vid).textContent=fmt(+e.target.value);draw();});});
document.querySelectorAll('.preset button').forEach(b=>b.addEventListener('click',()=>{const[d,e,r]=b.dataset.p.split(',').map(Number);delta=d;eps=e;rho=r;document.getElementById('delta').value=d;document.getElementById('eps').value=e;document.getElementById('rho').value=r;document.getElementById('vd').textContent=d.toFixed(3);document.getElementById('ve').textContent=e.toFixed(2);document.getElementById('vr').textContent=r.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 draw(){
const {w:W,h:H,dpr}=fit();ctx.setTransform(dpr,0,0,dpr,0,0);ctx.clearRect(0,0,W,H);
const d=D(), b=bound(), quad=(eps+d)*(eps+d);
const sg=2*rho*(1-rho);
document.getElementById('sg').textContent=sg.toFixed(3);
document.getElementById('dD').textContent=d.toFixed(3);
document.getElementById('boundTxt').textContent=b.toFixed(3);
// 左:堆叠条
const maxV=Math.max(2,b*1.15);
const padT=40,padB=46,y0=padT,y1=H-padB;
const sy=v=>y1-Math.min(v,maxV)/maxV*(y1-y0);
// y axis
ctx.strokeStyle='#1c2530';ctx.lineWidth=1;ctx.font="11px 'JetBrains Mono',monospace";ctx.fillStyle='#5c6672';
const ticks=4;for(let i=0;i<=ticks;i++){const v=maxV*i/ticks,py=sy(v);ctx.beginPath();ctx.moveTo(70,py);ctx.lineTo(W-30,py);ctx.stroke();ctx.fillText(v.toFixed(2),36,py+4);}
const cx=W*0.30, bw=130;
// D 段
ctx.fillStyle='#f4a261';ctx.globalAlpha=.92;ctx.fillRect(cx-bw/2,sy(d),bw,y1-sy(d));ctx.globalAlpha=1;
// (ε+D)² 段
ctx.fillStyle='#e76f51';ctx.globalAlpha=.92;ctx.fillRect(cx-bw/2,sy(d+quad),bw,sy(d)-sy(d+quad));ctx.globalAlpha=1;
// 标注
ctx.fillStyle='#0b0e13';ctx.font="700 12px 'JetBrains Mono',monospace";ctx.textAlign='center';
if(y1-sy(d)>18)ctx.fillText('D = '+d.toFixed(3),cx,(y1+sy(d))/2+4);
if(sy(d)-sy(d+quad)>18)ctx.fillText('(ε+D)² = '+quad.toFixed(3),cx,(sy(d)+sy(d+quad))/2+4);
ctx.fillStyle='#f4a261';ctx.font="700 13px 'JetBrains Mono',monospace";ctx.fillText('界 = '+b.toFixed(3),cx,sy(b)-9);
ctx.fillStyle='#8b949e';ctx.font="400 12px 'JetBrains Mono',monospace";ctx.fillText('误差上界构成',cx,y1+20);ctx.textAlign='left';
// 右:界随 δ 的曲线
const rx0=W*0.52,rx1=W-40,ry0=padT,ry1=y1;
const dMax=0.3;
const px=dd=>rx0+dd/dMax*(rx1-rx0);
ctx.strokeStyle='#1c2530';for(let i=0;i<=3;i++){const dd=dMax*i/3;ctx.beginPath();ctx.moveTo(px(dd),ry0);ctx.lineTo(px(dd),ry1);ctx.stroke();ctx.fillStyle='#5c6672';ctx.font="10px 'JetBrains Mono',monospace";ctx.fillText('δ='+dd.toFixed(2),px(dd)-14,ry1+16);}
ctx.strokeStyle='#4cc9f0';ctx.lineWidth=2;ctx.beginPath();
for(let i=0;i<=160;i++){const dd=dMax*i/160;const DD=dd/sg;const bb=DD+(eps+DD)*(eps+DD);const py=sy(bb);i?ctx.lineTo(px(dd),py):ctx.moveTo(px(dd),py);}
ctx.stroke();
ctx.fillStyle='#4cc9f0';ctx.font="500 11px 'JetBrains Mono',monospace";ctx.fillText('界(δ) @ 当前 ε,ρ',rx0+8,ry0+14);
// 当前点
ctx.fillStyle='#f4a261';ctx.beginPath();ctx.arc(px(delta),sy(b),5,0,7);ctx.fill();
const v=document.getElementById('verdict');
const dominPct=b>1e-6?(d/b*100).toFixed(0):'—';
v.innerHTML=(delta<1e-6&&eps<1e-6)
? '<b>δ = ε = 0:界 = 0</b>,精确恢复 h(z)=Qz,退化为<strong>定理 1</strong>。'
: '当前界 = <b>'+b.toFixed(3)+'</b>,其中 D(来自 δ)占约 <b>'+dominPct+'%</b>。<br>这说明<strong>对齐间隙 δ 是误差主因</strong>,白化误差 ε 仅二阶——训练时应优先压低对齐损失。';
}
window.addEventListener('resize',draw);
draw();
</script>
</body>
</html>
@@ -0,0 +1,197 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>高斯唯一性(Sturm-Liouville)— 交互式探索</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,40%),#2a323d var(--fill,40%));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;}
.preset{display:flex;gap:6px;flex-wrap:wrap;margin-top:8px;}
.preset button{flex:1 1 45%;padding:9px 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);}
.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);}
.badge{display:inline-block;padding:4px 10px;border-radius:20px;font:600 12px 'JetBrains Mono',monospace;}
.stage{grid-area:stage;padding:18px 20px 16px;display:flex;flex-direction:column;gap:14px;min-height:0;}
.stage .ttl{display:flex;align-items:baseline;justify-content:space-between;}
.stage .ttl h2{margin:0;font-family:'Spectral',serif;font-weight:600;font-size:18px;}
.stage .ttl span{font:400 12px 'JetBrains Mono',monospace;color:var(--txt-3);}
.grid2{flex:1;display:grid;grid-template-rows: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:400 12px 'JetBrains Mono',monospace;color:var(--txt-3);}
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;}
.r2box{position:relative;height:150px;border-radius:10px;overflow:hidden;background:#0b0e13;border:1px solid var(--line);}
.formula{font:500 13.5px 'JetBrains Mono',monospace;background:#0b0e13;border:1px solid var(--line);border-radius:8px;padding:13px 15px;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;}.grid2{height:520px;flex:none;}}
</style>
</head>
<body>
<div class="app">
<header>
<div style="display:flex;flex-direction:column;gap:6px;">
<span class="kicker">LeJEPA · Topic 4 · 定理 2</span>
<h1>高斯唯一性 <em>Sturm-Liouville</em></h1>
</div>
<p class="sub">扫描广义正态分布的形状参数 <b style="color:var(--linear)">α</b>,看密度 p(z;α) 的<strong>得分函数 (log p)</strong> 是否线性——只有 α=2(高斯)时得分线性,线性可识别性才成立。</p>
</header>
<section class="card controls">
<div class="block">
<div class="label">形状参数 α</div>
<div class="aval"><span id="aTxt">2.00</span></div>
<div id="aName" style="margin-top:8px;"></div>
<input id="alpha" type="range" min="0.6" max="6" step="0.05" value="2" aria-label="形状参数 α" title="形状参数 α" />
<div class="hint">广义正态 p(z;α) ∝ exp(−|z/β|^α),β 调成单位方差。α=1 拉普拉斯,α=2 高斯,α→∞ 均匀。</div>
<div class="preset">
<button data-a="1">α=1 拉普拉斯</button>
<button data-a="2">α=2 高斯 ✓</button>
<button data-a="3">α=3 超高斯</button>
<button data-a="6">α=6 ≈均匀</button>
</div>
</div>
<div class="block">
<div class="label">得分函数线性度</div>
<div id="scoreBadge" class="badge"></div>
<div class="hint">得分 (log p) = (α/βᵅ)·|z|^{α−1}·sign(z)。仅 α=2 时退化为线性的 −z。</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>密度与得分函数</h2><span>α=2 是唯一让得分变直线的点</span></div>
<div class="grid2">
<div class="canvas-wrap"><div class="cap">密度 p(z; α)</div><canvas id="density"></canvas></div>
<div class="canvas-wrap"><div class="cap">得分函数 (log p)′(z) — 直线 = 高斯</div><canvas id="score"></canvas></div>
</div>
</section>
<section class="card reading">
<h3>充要链条</h3>
<div class="formula">p 高斯<br>&nbsp;⟺ 得分 (log p) <span class="hi">线性</span><br>&nbsp;⟺ 第一特征函数仿射<br>&nbsp;<span class="hi">线性可识别性成立</span></div>
<h3 style="margin-top:2px;">线性可识别性 R²(α)</h3>
<div class="r2box"><canvas id="r2"></canvas></div>
<div class="verdict win" id="verdict"></div>
<div class="src">来源:JEPA/math/04_sturm_liouville_uniqueness.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==='gaussian_uniqueness_explainer.html')a.className='on';nav.appendChild(a);});
let alpha=2, auto=true, dir=1, lastTick=performance.now();
function lgamma(x){const g=7,c=[0.99999999999980993,676.5203681218851,-1259.1392167224028,771.32342877765313,-176.61502916214059,12.507343278686905,-0.13857109526572012,9.9843695780195716e-6,1.5056327351493116e-7];if(x<0.5)return Math.log(Math.PI/Math.sin(Math.PI*x))-lgamma(1-x);x-=1;let a=c[0];const t=x+g+0.5;for(let i=1;i<g+2;i++)a+=c[i]/(x+i);return 0.5*Math.log(2*Math.PI)+(x+0.5)*Math.log(t)-t+Math.log(a);}
function beta_(a){return Math.exp(0.5*(lgamma(1/a)-lgamma(3/a)));}// unit variance scale
function density(z,a){const b=beta_(a);const norm=a/(2*b*Math.exp(lgamma(1/a)));return norm*Math.exp(-Math.pow(Math.abs(z/b),a));}
function score(z,a){const b=beta_(a);if(Math.abs(z)<1e-6)return 0;return -(a/Math.pow(b,a))*Math.pow(Math.abs(z),a-1)*Math.sign(z);}
// R²(α): 启发式建模——得分线性度。线性回归 score≈k·z 的 R²
function r2OfAlpha(a){
let sx=0,sy=0,sxx=0,sxy=0,syy=0,n=0;
for(let z=-3;z<=3;z+=0.05){const s=score(z,a);const wgt=density(z,a);sx+=z*wgt;sy+=s*wgt;sxx+=z*z*wgt;sxy+=z*s*wgt;syy+=s*s*wgt;n+=wgt;}
const mx=sx/n,my=sy/n;const cov=sxy/n-mx*my,vx=sxx/n-mx*mx,vy=syy/n-my*my;
const r=cov/Math.sqrt(vx*vy+1e-12);return Math.max(0,Math.min(1,r*r));
}
const alphaEl=document.getElementById('alpha'),aTxt=document.getElementById('aTxt'),aName=document.getElementById('aName');
function nameOf(a){if(a<0.9)return['重尾','#e76f51'];if(a<1.2)return['拉普拉斯 (α≈1)','#f4a261'];if(a<1.7)return['亚高斯','#f4a261'];if(a<2.3)return['高斯 (α=2) ✓','#56d364'];if(a<4)return['超高斯','#f4a261'];return['≈ 均匀','#e76f51'];}
function setAlpha(v,fromUser){alpha=Math.max(0.6,Math.min(6,v));alphaEl.value=alpha;aTxt.textContent=alpha.toFixed(2);alphaEl.style.setProperty('--fill',((alpha-0.6)/(6-0.6)*100).toFixed(1)+'%');const[nm,col]=nameOf(alpha);aName.innerHTML='<span class="badge" style="background:'+col+'22;color:'+col+'">'+nm+'</span>';if(fromUser){auto=false;const b=document.getElementById('autoBtn');b.classList.remove('live');b.textContent='▶ 自动扫描 α';}draw();}
alphaEl.addEventListener('input',e=>setAlpha(+e.target.value,true));
document.querySelectorAll('.preset button').forEach(b=>b.addEventListener('click',()=>setAlpha(+b.dataset.a,true)));
document.getElementById('autoBtn').addEventListener('click',e=>{auto=!auto;e.target.classList.toggle('live',auto);e.target.textContent=auto?'⏸ 自动扫描 α':'▶ 自动扫描 α';lastTick=performance.now();});
const dC=document.getElementById('density'),dx=dC.getContext('2d');
const sC=document.getElementById('score'),sx2=sC.getContext('2d');
const rC=document.getElementById('r2'),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 drawDensity(){const {w,h,dpr}=fit(dC);dx.setTransform(dpr,0,0,dpr,0,0);dx.clearRect(0,0,w,h);
const padL=20,padR=16,padT=22,padB=22,x0=padL,x1=w-padR,y0=padT,y1=h-padB,lim=3.4;
const sx=z=>x0+(z+lim)/(2*lim)*(x1-x0);
let mx=0;for(let z=-lim;z<=lim;z+=0.05)mx=Math.max(mx,density(z,alpha));
const sy=v=>y1-v/(mx*1.1)*(y1-y0);
dx.strokeStyle='#1c2530';dx.lineWidth=1;for(let g=-3;g<=3;g++){dx.beginPath();dx.moveTo(sx(g),y0);dx.lineTo(sx(g),y1);dx.stroke();}
// 高斯参考
dx.strokeStyle='rgba(86,211,100,.35)';dx.setLineDash([5,4]);dx.beginPath();for(let i=0,z=-lim;i<=200;i++,z=-lim+2*lim*i/200){const py=sy(density(z,2));i?dx.lineTo(sx(z),py):dx.moveTo(sx(z),py);}dx.stroke();dx.setLineDash([]);
// 当前密度
const isG=Math.abs(alpha-2)<0.08;dx.strokeStyle=isG?'#56d364':'#4cc9f0';dx.lineWidth=2.5;dx.shadowColor=dx.strokeStyle;dx.shadowBlur=8;dx.beginPath();for(let i=0,z=-lim;i<=240;i++,z=-lim+2*lim*i/240){const py=sy(density(z,alpha));i?dx.lineTo(sx(z),py):dx.moveTo(sx(z),py);}dx.stroke();dx.shadowBlur=0;
}
function drawScore(){const {w,h,dpr}=fit(sC);sx2.setTransform(dpr,0,0,dpr,0,0);sx2.clearRect(0,0,w,h);
const padL=20,padR=16,padT=18,padB=20,x0=padL,x1=w-padR,y0=padT,y1=h-padB,lim=2.8,vlim=4;
const sx=z=>x0+(z+lim)/(2*lim)*(x1-x0),sy=v=>y1-(Math.max(-vlim,Math.min(vlim,v))+vlim)/(2*vlim)*(y1-y0);
sx2.strokeStyle='#1c2530';sx2.lineWidth=1;for(let g=-2;g<=2;g++){sx2.beginPath();sx2.moveTo(sx(g),y0);sx2.lineTo(sx(g),y1);sx2.stroke();}
sx2.strokeStyle='#33404e';sx2.beginPath();sx2.moveTo(x0,sy(0));sx2.lineTo(x1,sy(0));sx2.stroke();
// 高斯参考 -z(线性)
sx2.strokeStyle='rgba(86,211,100,.4)';sx2.setLineDash([5,4]);sx2.lineWidth=1.5;sx2.beginPath();sx2.moveTo(sx(-lim),sy(lim));sx2.lineTo(sx(lim),sy(-lim));sx2.stroke();sx2.setLineDash([]);
sx2.fillStyle='rgba(86,211,100,.7)';sx2.font="11px 'JetBrains Mono',monospace";sx2.fillText('z (线性)',x1-78,sy(-lim)+14);
// 当前 score
const isG=Math.abs(alpha-2)<0.08;sx2.strokeStyle=isG?'#56d364':'#f4a261';sx2.lineWidth=2.5;sx2.shadowColor=sx2.strokeStyle;sx2.shadowBlur=7;sx2.beginPath();let first=true;for(let i=0,z=-lim;i<=240;i++,z=-lim+2*lim*i/240){const v=score(z,alpha);const py=sy(v);if(first){sx2.moveTo(sx(z),py);first=false;}else sx2.lineTo(sx(z),py);}sx2.stroke();sx2.shadowBlur=0;
}
function drawR2(){const {w,h,dpr}=fit(rC);rx.setTransform(dpr,0,0,dpr,0,0);rx.clearRect(0,0,w,h);
const padL=30,padR=14,padT=16,padB=22,x0=padL,x1=w-padR,y0=padT,y1=h-padB,aMin=0.6,aMax=6;
const sx=a=>x0+(a-aMin)/(aMax-aMin)*(x1-x0),sy=v=>y1-v*(y1-y0);
rx.strokeStyle='#1c2530';rx.lineWidth=1;rx.font="10px 'JetBrains Mono',monospace";rx.fillStyle='#5c6672';
[0,0.5,1].forEach(v=>{rx.beginPath();rx.moveTo(x0,sy(v));rx.lineTo(x1,sy(v));rx.stroke();rx.fillText(v.toFixed(1),4,sy(v)+3);});
[1,2,3,4,5,6].forEach(a=>{rx.fillText(a,sx(a)-3,y1+15);});
// 曲线
rx.strokeStyle='#4cc9f0';rx.lineWidth=2;rx.beginPath();for(let i=0;i<=160;i++){const a=aMin+(aMax-aMin)*i/160;const v=r2OfAlpha(a);i?rx.lineTo(sx(a),sy(v)):rx.moveTo(sx(a),sy(v));}rx.stroke();
// α=2 峰标
rx.strokeStyle='rgba(86,211,100,.5)';rx.setLineDash([4,4]);rx.beginPath();rx.moveTo(sx(2),y0);rx.lineTo(sx(2),y1);rx.stroke();rx.setLineDash([]);
rx.fillStyle='#56d364';rx.font="600 10px 'JetBrains Mono',monospace";rx.fillText('α=2 峰值',sx(2)+4,y0+10);
// 当前 α 标记
const cv=r2OfAlpha(alpha);rx.fillStyle=Math.abs(alpha-2)<0.08?'#56d364':'#f4a261';rx.beginPath();rx.arc(sx(alpha),sy(cv),4.5,0,7);rx.fill();
}
function draw(){
drawDensity();drawScore();drawR2();
const r2=r2OfAlpha(alpha), isG=Math.abs(alpha-2)<0.08;
const sb=document.getElementById('scoreBadge');
if(isG){sb.style.background='rgba(86,211,100,.18)';sb.style.color='#56d364';sb.textContent='线性 ✓(−z';}
else{sb.style.background='var(--nl-soft)';sb.style.color='#f4a261';sb.textContent='非线性 ✗';}
const v=document.getElementById('verdict');
if(isG){v.className='verdict win';v.innerHTML='<b>α = 2:得分函数 = z(线性)</b><br>第一特征函数仿射 → 线性可识别性成立,R² ≈ '+r2.toFixed(3)+'。高斯是<strong>唯一</strong>满足此条件的分布。';}
else{v.className='verdict lose';v.innerHTML='当前得分函数<b style="color:var(--nl)">非线性</b>(α≠2),第一特征函数不再仿射 → 线性可识别性失败,R² ≈ <b style="color:var(--nl)">'+r2.toFixed(3)+'</b>。把 α 拨回 <b style="color:var(--ok)">2</b> 看 R² 冲上峰值。';}
}
function loop(now){if(auto){const dt=(now-lastTick)/1000;lastTick=now;let v=alpha+dir*dt*0.9;if(v<=0.6){v=0.6;dir=1;}if(v>=6){v=6;dir=-1;}setAlpha(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')setAlpha(alpha-0.1,true);else if(e.key==='ArrowRight')setAlpha(alpha+0.1,true);});
window.addEventListener('resize',draw);
setAlpha(2,false);draw();requestAnimationFrame(loop);
</script>
</body>
</html>
+428
View File
@@ -0,0 +1,428 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Hermite 多项式 × OU 衰减 — 交互式探索</title>
<!--
设计系统决策(合同)
─────────────────────────────────────────────
调性:科学可视化 / 学术交互工具(非营销页)
背景:石墨黑 #0e1116 面板 #161b22 描边 #232b36
语义色:
· 线性 d=1"想要的成分" → 青蓝 var(--linear) #4cc9f0
· 高阶非线性 d≥2(被衰减) → 暖橙 var(--nl) #f4a261
· 高斯密度底纹 → 冷灰青 半透明
文字:主 #e6edf3 次 #8b949e 弱 #5c6672
字体:正文衬线 'Spectral',界面无衬线 'Sora',数据/公式等宽 'JetBrains Mono'
原则:无启动页、自动演示 + 可手动接管、ρ 滑块与阶数为核心交互
-->
<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);
--gauss:rgba(150,170,190,.12);
--grid:#1c2530;
--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 320px;
grid-template-rows: auto 1fr;
grid-template-areas:
"head head head"
"ctrl stage read";
gap:18px; padding:22px 26px;
}
/* ── header ── */
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; letter-spacing:.01em;
}
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);}
/* ── left controls ── */
.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;
}
/* rho slider */
.rho-val{font:700 40px/1 'JetBrains Mono',monospace; color:var(--linear); letter-spacing:.02em;}
.rho-val 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,70%), #2a323d var(--fill,70%));
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); cursor:grab;
}
input[type=range]::-moz-range-thumb{
width:18px;height:18px;border-radius:50%;background:#eaf6fb;border:3px solid var(--linear);cursor:grab;
}
.hint{font-size:12px; color:var(--txt-3); line-height:1.55; margin-top:10px;}
/* degree toggles */
.degrees{display:flex; flex-direction:column; gap:8px;}
.deg{
display:flex; align-items:center; gap:12px; padding:10px 12px;
border:1px solid var(--line); border-radius:10px; cursor:pointer;
background:var(--panel-2); transition:.18s; -webkit-user-select:none; user-select:none;
}
.deg:hover{border-color:#36404d;}
.deg.on{border-color:currentColor; background:color-mix(in oklch, currentColor 12%, var(--panel-2));}
.deg .dot{width:11px;height:11px;border-radius:3px;background:currentColor; flex:none;}
.deg .nm{font:500 13px 'JetBrains Mono',monospace; color:var(--txt);}
.deg .eq{margin-left:auto; font:400 12px 'JetBrains Mono',monospace; color:var(--txt-2);}
.deg.d1{color:var(--linear);}
.deg.dN{color:var(--nl);}
.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; transition:.16s;
}
.play button:hover{border-color:var(--linear); color:var(--linear);}
.play button.live{background:var(--linear-soft); border-color:var(--linear); color:var(--linear);}
/* ── center stage ── */
.stage{grid-area:stage; padding:18px 20px 16px; display:flex; flex-direction:column; gap:14px; min-height:0;}
.stage .ttl{display:flex; align-items:baseline; justify-content:space-between;}
.stage .ttl h2{margin:0; font-family:'Spectral',serif; font-weight:600; font-size:18px;}
.stage .ttl span{font:400 12px 'JetBrains Mono',monospace; color:var(--txt-3);}
.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%;}
.corr-wrap{height:150px; flex:none; border-radius:10px; overflow:hidden; background:#0b0e13; border:1px solid var(--line); position:relative;}
.corr-cap{position:absolute; top:10px; left:14px; font:400 12px 'JetBrains Mono',monospace; color:var(--txt-3);}
/* ── right reading ── */
.reading{grid-area:read; padding:22px; display:flex; flex-direction:column; gap:18px; overflow-y:auto;}
.reading h3{margin:0; font-family:'Spectral',serif; font-weight:600; font-size:17px;}
.takeaway{
background:var(--panel-2); border:1px solid var(--line); border-left:none;
border-radius:10px; padding:16px; font-size:13.5px; line-height:1.62; color:var(--txt);
}
.takeaway .big{font-family:'JetBrains Mono',monospace; color:var(--linear); font-weight:700;}
.formula{
font:500 15px 'JetBrains Mono',monospace; color:var(--txt);
background:#0b0e13; border:1px solid var(--line); border-radius:8px;
padding:14px 16px; line-height:1.7; letter-spacing:.01em;
}
.formula .hi{color:var(--linear);} .formula .hl{color:var(--nl);}
.verdict{
margin-top:auto; padding:16px; border-radius:10px;
background:linear-gradient(135deg, var(--linear-soft), transparent 70%);
border:1px solid color-mix(in oklch,var(--linear) 40%, var(--line));
font-family:'Spectral',serif; font-size:14px; line-height:1.6;
}
.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:auto auto auto auto; grid-template-areas:"head" "ctrl" "stage" "read"; overflow:auto; height:auto;}
body{overflow:auto;}
.canvas-wrap{height:340px;flex:none;}
}
</style>
</head>
<body>
<div class="app">
<header>
<div style="display:flex;flex-direction:column;gap:6px;">
<span class="kicker">LeJEPA · Topic 1</span>
<h1>Hermite 多项式 <em>与 OU 过程的 ρᵈ 衰减</em></h1>
</div>
<p class="sub">拖动相关性 <b style="color:var(--linear)">ρ</b>、切换不同阶数的 Hermite 成分,实时观察:为什么 LeJEPA 的对齐损失会<strong>只青睐线性成分</strong>——这正是「线性可识别性」定理 1 的核心直觉。</p>
</header>
<!-- 左:控制 -->
<section class="card controls">
<div class="block">
<div class="label">相关性 ρ(OU 过程)</div>
<div class="rho-val"><span id="rhoTxt">0.70</span><small> ∈ (0, 1)</small></div>
<input id="rho" type="range" min="0.05" max="0.97" step="0.01" value="0.70" aria-label="相关性 ρ" title="相关性 ρ" />
<div class="hint">ρ 越大,正样本对 (z, z′) 越相似。第 d 阶成分的时间相关性为 <b style="color:var(--txt)">ρᵈ</b>,高阶随 d 指数衰减。</div>
</div>
<div class="block">
<div class="label">显示哪些 Hermite 成分</div>
<div class="degrees" id="degList"></div>
</div>
<div class="block">
<div class="label">演示</div>
<div class="play">
<button id="autoBtn" class="live">⏸ 自动扫描 ρ</button>
<button id="resetBtn">↺ 复位</button>
</div>
<div class="hint">自动模式下 ρ 在 0.05↔0.97 之间往返,观察条形图的相对高度如何变化。</div>
</div>
</section>
<!-- 中:画布 -->
<section class="card stage">
<div class="ttl">
<h2>前几阶 Hermite 多项式 Heₙ(z)</h2>
<span>底纹 = 高斯密度 N(0,1),概率质量集中在 [-3, 3]</span>
</div>
<div class="canvas-wrap"><canvas id="curve"></canvas></div>
<div class="corr-wrap">
<div class="corr-cap">各阶成分对正样本对的相关性 = ρᵈ · (n! 归一化后)</div>
<canvas id="bars"></canvas>
</div>
</section>
<!-- 右:解读 -->
<section class="card reading">
<h3>实时解读</h3>
<div class="formula">
E[Heₙ(z)·Heₙ(z)]<br>
&nbsp;&nbsp;= <span class="hi">ρ</span><sup>n</sup> · n!
</div>
<div class="takeaway" id="takeaway"></div>
<h3 style="margin-top:4px;">谱权重的含义</h3>
<div class="formula" style="font-size:13.5px;">
<span class="hi">w₁ = 1</span> → 100% 线性(想要的)<br>
<span class="hl">w_d&gt;0 (d≥2)</span> → 残留非线性
</div>
<div class="verdict" id="verdict"></div>
<div class="src">来源:JEPA/math/01_hermite_polynomials.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 navEl=document.getElementById('nav');NAV.forEach(([n,t,f])=>{const a=document.createElement('a');a.href=f;a.textContent=n+' '+t;if(f==='hermite_explainer.html')a.className='on';navEl.appendChild(a);});
/* ───────── Hermite(概率论版 Heₙ)系数与求值 ───────── */
// 递推: He_{n+1} = z·He_n n·He_{n-1}
function hermiteVal(n, z){
let h0 = 1, h1 = z;
if(n===0) return 1;
if(n===1) return z;
for(let k=1;k<n;k++){ const h2 = z*h1 - k*h0; h0=h1; h1=h2; }
return h1;
}
function factorial(n){ let f=1; for(let i=2;i<=n;i++) f*=i; return f; }
const DEGREES = [
{d:1, name:'He₁(z) = z', cls:'d1', color:'#4cc9f0', on:true, eq:'线性 ✓'},
{d:2, name:'He₂(z) = z²−1', cls:'dN', color:'#f4a261', on:true, eq:'二次'},
{d:3, name:'He₃(z) = z³−3z', cls:'dN', color:'#e76f51', on:false, eq:'三次'},
{d:4, name:'He₄(z) = z⁴−6z²+3', cls:'dN', color:'#bc6c8f', on:false, eq:'四次'},
];
/* ───────── 状态 ───────── */
let rho = 0.70;
let auto = true, autoDir = -1, lastTick = performance.now();
/* ───────── 构建阶数开关 ───────── */
const degList = document.getElementById('degList');
DEGREES.forEach((g,i)=>{
const el = document.createElement('div');
el.className = 'deg '+g.cls+(g.on?' on':'');
el.style.color = g.color;
el.innerHTML = `<span class="dot"></span><span class="nm">${g.name}</span><span class="eq">${g.eq}</span>`;
el.addEventListener('click', ()=>{ g.on=!g.on; el.classList.toggle('on',g.on); draw(); });
degList.appendChild(el);
});
/* ───────── 滑块 ───────── */
const rhoEl = document.getElementById('rho');
const rhoTxt = document.getElementById('rhoTxt');
function setRho(v, fromUser){
rho = Math.max(0.05, Math.min(0.97, v));
rhoEl.value = rho;
rhoTxt.textContent = rho.toFixed(2);
rhoEl.style.setProperty('--fill', ((rho-0.05)/(0.97-0.05)*100).toFixed(1)+'%');
if(fromUser){ auto=false; document.getElementById('autoBtn').classList.remove('live'); document.getElementById('autoBtn').textContent='▶ 自动扫描 ρ'; }
draw();
}
rhoEl.addEventListener('input', e=> setRho(+e.target.value, true));
document.getElementById('autoBtn').addEventListener('click', e=>{
auto = !auto;
e.target.classList.toggle('live', auto);
e.target.textContent = auto ? '⏸ 自动扫描 ρ' : '▶ 自动扫描 ρ';
lastTick = performance.now();
});
document.getElementById('resetBtn').addEventListener('click', ()=>{
DEGREES.forEach((g,i)=>{ g.on = (g.d<=2); degList.children[i].classList.toggle('on',g.on); });
setRho(0.70,false);
});
/* ───────── Canvas ───────── */
const curve = document.getElementById('curve'), cctx = curve.getContext('2d');
const bars = document.getElementById('bars'), bctx = bars.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 drawCurves(){
const {w,h,dpr}=fit(curve); cctx.setTransform(dpr,0,0,dpr,0,0); cctx.clearRect(0,0,w,h);
const padL=44, padR=16, padT=14, padB=26;
const x0=padL, x1=w-padR, y0=padT, y1=h-padB;
const zMin=-3.2, zMax=3.2, vMin=-6, vMax=6;
const sx = z => x0 + (z-zMin)/(zMax-zMin)*(x1-x0);
const sy = v => y1 - (Math.max(vMin,Math.min(vMax,v))-vMin)/(vMax-vMin)*(y1-y0);
// 高斯密度底纹
cctx.beginPath();
for(let i=0;i<=240;i++){ const z=zMin+(zMax-zMin)*i/240; const g=Math.exp(-z*z/2); const v=g*9-0.2;
const px=sx(z), py=sy(v); i?cctx.lineTo(px,py):cctx.moveTo(px,py); }
cctx.lineTo(sx(zMax),sy(vMin)); cctx.lineTo(sx(zMin),sy(vMin)); cctx.closePath();
cctx.fillStyle='rgba(150,170,190,.10)'; cctx.fill();
// 网格 + 轴
cctx.strokeStyle='#1c2530'; cctx.lineWidth=1; cctx.font="11px 'JetBrains Mono',monospace"; cctx.fillStyle='#5c6672';
for(let v=vMin; v<=vMax; v+=3){ const py=sy(v); cctx.beginPath(); cctx.moveTo(x0,py); cctx.lineTo(x1,py); cctx.stroke(); cctx.fillText(v.toString().padStart(2,' '), 8, py+4); }
for(let z=-3; z<=3; z++){ const px=sx(z); cctx.beginPath(); cctx.moveTo(px,y0); cctx.lineTo(px,y1); cctx.strokeStyle='#161d27'; cctx.stroke(); cctx.fillText(z.toString(), px-4, y1+18); }
// 零轴
cctx.strokeStyle='#33404e'; cctx.lineWidth=1.5; cctx.beginPath(); cctx.moveTo(x0,sy(0)); cctx.lineTo(x1,sy(0)); cctx.stroke();
// 各阶曲线
DEGREES.filter(g=>g.on).forEach(g=>{
cctx.beginPath();
for(let i=0;i<=320;i++){ const z=zMin+(zMax-zMin)*i/320; const v=hermiteVal(g.d,z);
const px=sx(z), py=sy(v); i?cctx.lineTo(px,py):cctx.moveTo(px,py); }
cctx.lineWidth = g.d===1?3:2; cctx.strokeStyle=g.color;
cctx.shadowColor=g.color; cctx.shadowBlur = g.d===1?10:5; cctx.stroke(); cctx.shadowBlur=0;
// 标签
const lz=2.6, lv=hermiteVal(g.d,lz);
cctx.fillStyle=g.color; cctx.font="600 12px 'JetBrains Mono',monospace";
cctx.fillText('He'+g.d, sx(lz)+4, sy(lv));
});
}
function drawBars(){
const {w,h,dpr}=fit(bars); bctx.setTransform(dpr,0,0,dpr,0,0); bctx.clearRect(0,0,w,h);
const items = DEGREES.filter(g=>g.on);
const padT=34, padB=24, padL=18, padR=18;
const y0=padT, y1=h-padB;
const n=items.length || 1;
const gap=22, bw=Math.min(90,(w-padL-padR-gap*(n-1))/n);
const groupW=bw*n+gap*(n-1);
const startX=(w-groupW)/2;
// 基线(=ρ,线性成分的相关性,作为参考)
const refY = y1 - (rho)*(y1-y0);
bctx.strokeStyle='rgba(76,201,240,.45)'; bctx.setLineDash([5,4]); bctx.lineWidth=1.5;
bctx.beginPath(); bctx.moveTo(padL,refY); bctx.lineTo(w-padR,refY); bctx.stroke(); bctx.setLineDash([]);
bctx.fillStyle='rgba(76,201,240,.8)'; bctx.font="500 11px 'JetBrains Mono',monospace";
bctx.fillText('ρ¹ = '+rho.toFixed(2), w-padR-70, refY-6);
items.forEach((g,i)=>{
const val=Math.pow(rho,g.d); // ρ^d
const x=startX+i*(bw+gap);
const bh=val*(y1-y0);
const yTop=y1-bh;
// 柱
const grd=bctx.createLinearGradient(0,yTop,0,y1);
grd.addColorStop(0,g.color); grd.addColorStop(1,'color-mix' in CSS ? g.color : g.color);
bctx.fillStyle=g.color; bctx.globalAlpha = g.d===1?1:0.85;
bctx.fillRect(x,yTop,bw,bh); bctx.globalAlpha=1;
// 数值
bctx.fillStyle=g.color; bctx.font="700 13px 'JetBrains Mono',monospace"; bctx.textAlign='center';
bctx.fillText('ρ'+g.d+' = '+val.toFixed(3), x+bw/2, yTop-7);
bctx.fillStyle='#8b949e'; bctx.font="400 11px 'JetBrains Mono',monospace";
bctx.fillText('He'+g.d, x+bw/2, y1+15);
bctx.textAlign='left';
});
}
/* ───────── 文案更新 ───────── */
function updateText(){
const r=rho;
const r1=r, r2=r*r, r3=r*r*r;
const ratio=(r2/r1*100).toFixed(0); // ρ²/ρ
const take=document.getElementById('takeaway');
take.innerHTML =
`当前 ρ = <span class="big">${r.toFixed(2)}</span>`+
`线性成分相关性 = ρ¹ = <b style="color:var(--linear)">${r1.toFixed(3)}</b>`+
`二次成分仅 = ρ² = <b style="color:var(--nl)">${r2.toFixed(3)}</b>`+
`(保留约 <b>${ratio}%</b>),三次成分 ρ³ = <b style="color:var(--nl)">${r3.toFixed(3)}</b>。`+
`<br><br>阶数越高,<b style="color:var(--nl)">衰减越快</b>——对齐损失要<strong>最大化</strong>相关性,自然只会保留 <b style="color:var(--linear)">d=1 的线性成分</b>。`;
const v=document.getElementById('verdict');
v.innerHTML =
`因为非线性成分(d≥2)的贡献 <b>ρᵈ &lt; ρ</b> 恒成立,`+
`LeJEPA 的唯一最优解就是 <b>h(z) = Q·z</b>(正交线性映射)。`+
`<br>这就是<b>线性可识别性(定理 1</b>的全部直觉。`;
}
/* ───────── 主绘制 ───────── */
function draw(){ drawCurves(); drawBars(); updateText(); }
/* ───────── 自动扫描动画 ───────── */
function loop(now){
if(auto){
const dt=(now-lastTick)/1000; lastTick=now;
let v=rho + autoDir*dt*0.22;
if(v<=0.05){ v=0.05; autoDir=1; }
if(v>=0.97){ v=0.97; autoDir=-1; }
setRho(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'){ setRho(rho-0.03,true); }
else if(e.key==='ArrowRight'){ setRho(rho+0.03,true); }
else if(e.key==='0'){ document.getElementById('resetBtn').click(); }
});
window.addEventListener('resize', draw);
/* ───────── 初始化 ───────── */
setRho(0.70,false);
draw();
requestAnimationFrame(loop);
</script>
</body>
</html>
@@ -0,0 +1,193 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OU 过程与 Mehler 公式 — 交互式探索</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); --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 320px;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;}
.rho-val{font:700 40px/1 'JetBrains Mono',monospace;color:var(--linear);}
.rho-val 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,70%),#2a323d var(--fill,70%));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;}
.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;transition:.16s;}
.play button:hover{border-color:var(--linear);color:var(--linear);}
.play button.live{background:var(--linear-soft);border-color:var(--linear);color:var(--linear);}
.stat{display:flex;justify-content:space-between;align-items:baseline;padding:8px 0;border-bottom:1px solid var(--line);font:400 13px 'JetBrains Mono',monospace;}
.stat b{color:var(--linear);font-weight:700;}
.stage{grid-area:stage;padding:18px 20px 16px;display:flex;flex-direction:column;gap:14px;min-height:0;}
.stage .ttl{display:flex;align-items:baseline;justify-content:space-between;}
.stage .ttl h2{margin:0;font-family:'Spectral',serif;font-weight:600;font-size:18px;}
.stage .ttl span{font:400 12px 'JetBrains Mono',monospace;color:var(--txt-3);}
.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%;}
.corr-wrap{height:150px;flex:none;border-radius:10px;overflow:hidden;background:#0b0e13;border:1px solid var(--line);position:relative;}
.corr-cap{position:absolute;top:10px;left:14px;font:400 12px 'JetBrains Mono',monospace;color:var(--txt-3);}
.reading{grid-area:read;padding:22px;display:flex;flex-direction:column;gap:18px;overflow-y:auto;}
.reading h3{margin:0;font-family:'Spectral',serif;font-weight:600;font-size:17px;}
.takeaway{background:var(--panel-2);border:1px solid var(--line);border-radius:10px;padding:16px;font-size:13.5px;line-height:1.62;}
.takeaway .big{font-family:'JetBrains Mono',monospace;color:var(--linear);font-weight:700;}
.formula{font:500 14px 'JetBrains Mono',monospace;color:var(--txt);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;background:linear-gradient(135deg,var(--linear-soft),transparent 70%);border:1px solid color-mix(in oklch,var(--linear) 40%,var(--line));font-family:'Spectral',serif;font-size:14px;line-height:1.6;}
.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:auto auto auto auto;grid-template-areas:"head" "ctrl" "stage" "read";overflow:auto;height:auto;}body{overflow:auto;}.canvas-wrap{height:340px;flex:none;}}
</style>
</head>
<body>
<div class="app">
<header>
<div style="display:flex;flex-direction:column;gap:6px;">
<span class="kicker">LeJEPA · Topic 2</span>
<h1>OU 过程 <em>与 Mehler 公式</em></h1>
</div>
<p class="sub">正样本对 <b style="color:var(--linear)">(z, z)</b> 由 OU 过程 <b>z = ρz + √(1−ρ²)·η</b> 生成。拖动 ρ 观察散点相关性与 ρᵈ 衰减——这正是「高阶成分被更强惩罚」的来源。</p>
</header>
<section class="card controls">
<div class="block">
<div class="label">相关性 ρ</div>
<div class="rho-val"><span id="rhoTxt">0.90</span><small> ∈ (0,1)</small></div>
<input id="rho" type="range" min="0.05" max="0.97" step="0.01" value="0.90" aria-label="相关性 ρ" title="相关性 ρ" />
<div class="hint">物理图像:弹簧拴住的粒子(均值回归 ρz)+ 随机扰动(√(1−ρ²)η)。ρ→1 视图几乎相同,ρ→0 趋于独立。</div>
</div>
<div class="block">
<div class="label">平稳性 & 相关性(实时统计)</div>
<div class="stat"><span>样本数 N</span><b id="sN">1500</b></div>
<div class="stat"><span>Var(z)</span><b id="sVz">1.00</b></div>
<div class="stat"><span>Var(z)</span><b id="sVzp">1.00</b></div>
<div class="stat"><span>Cov(z,z)</span><b id="sCov">0.90</b></div>
<div class="hint">无论 ρ 多少,z 与 z′ 都保持单位方差(平稳性);协方差 ≈ ρ。</div>
</div>
<div class="block">
<div class="label">演示</div>
<div class="play">
<button id="autoBtn" class="live">⏸ 自动扫描 ρ</button>
<button id="reseedBtn">⟲ 重采样</button>
</div>
</div>
</section>
<section class="card stage">
<div class="ttl"><h2>正样本对联合散点 (z, z)</h2><span>每点一个样本;越靠对角线 = 越相关</span></div>
<div class="canvas-wrap"><canvas id="scatter"></canvas></div>
<div class="corr-wrap">
<div class="corr-cap">第 d 阶 Hermite 成分的相关性 = ρᵈ(线性 d=1 最大)</div>
<canvas id="bars"></canvas>
</div>
</section>
<section class="card reading">
<h3>Mehler 公式</h3>
<div class="formula">E[h(z)·h(z)]<br>&nbsp;= Σ<sub>d≥1</sub> w<sub>d</sub>·<span class="hi">ρ</span><sup>d</sup><span class="hi">ρ</span></div>
<div class="takeaway" id="takeaway"></div>
<h3 style="margin-top:4px;">对齐损失下界</h3>
<div class="formula">L<sub>align</sub> = 2n 2Σ corr<sub>i</sub><br>&nbsp;&nbsp;<span class="hi">2(1−ρ)n</span></div>
<div class="verdict" id="verdict"></div>
<div class="src">来源:JEPA/math/02_ou_process_mehler.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==='ou_mehler_explainer.html')a.className='on';nav.appendChild(a);});
let rho=0.90, auto=true, autoDir=-1, lastTick=performance.now();
const N=1500;
let zSamples=null, etaSamples=null;
function randn(){let u=0,v=0;while(!u)u=Math.random();while(!v)v=Math.random();return Math.sqrt(-2*Math.log(u))*Math.cos(2*Math.PI*v);}
function reseed(){zSamples=new Float32Array(N);etaSamples=new Float32Array(N);for(let i=0;i<N;i++){zSamples[i]=randn();etaSamples[i]=randn();}}
reseed();
const rhoEl=document.getElementById('rho'),rhoTxt=document.getElementById('rhoTxt');
function setRho(v,fromUser){rho=Math.max(0.05,Math.min(0.97,v));rhoEl.value=rho;rhoTxt.textContent=rho.toFixed(2);rhoEl.style.setProperty('--fill',((rho-0.05)/(0.97-0.05)*100).toFixed(1)+'%');if(fromUser){auto=false;const b=document.getElementById('autoBtn');b.classList.remove('live');b.textContent='▶ 自动扫描 ρ';}draw();}
rhoEl.addEventListener('input',e=>setRho(+e.target.value,true));
document.getElementById('autoBtn').addEventListener('click',e=>{auto=!auto;e.target.classList.toggle('live',auto);e.target.textContent=auto?'⏸ 自动扫描 ρ':'▶ 自动扫描 ρ';lastTick=performance.now();});
document.getElementById('reseedBtn').addEventListener('click',()=>{reseed();draw();});
const scatter=document.getElementById('scatter'),sctx=scatter.getContext('2d');
const bars=document.getElementById('bars'),bctx=bars.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 drawScatter(){
const {w,h,dpr}=fit(scatter);sctx.setTransform(dpr,0,0,dpr,0,0);sctx.clearRect(0,0,w,h);
const pad=40,x0=pad,x1=w-pad,y0=pad,y1=h-pad,lim=3.4;
const sx=z=>x0+(z+lim)/(2*lim)*(x1-x0), sy=z=>y1-(z+lim)/(2*lim)*(y1-y0);
// grid
sctx.strokeStyle='#1c2530';sctx.lineWidth=1;sctx.font="11px 'JetBrains Mono',monospace";sctx.fillStyle='#5c6672';
for(let g=-3;g<=3;g++){sctx.beginPath();sctx.moveTo(sx(g),y0);sctx.lineTo(sx(g),y1);sctx.stroke();sctx.beginPath();sctx.moveTo(x0,sy(g));sctx.lineTo(x1,sy(g));sctx.stroke();}
// 对角线 z'=z
sctx.strokeStyle='rgba(76,201,240,.4)';sctx.setLineDash([6,5]);sctx.beginPath();sctx.moveTo(sx(-lim),sy(-lim));sctx.lineTo(sx(lim),sy(lim));sctx.stroke();sctx.setLineDash([]);
sctx.fillStyle='#5c6672';sctx.fillText('z',x1-12,sy(0)-6);sctx.fillText("z",sx(0)+6,y0+14);
// 回归线 z'=ρz
sctx.strokeStyle='var(--nl)';sctx.strokeStyle='#f4a261';sctx.lineWidth=2;sctx.beginPath();sctx.moveTo(sx(-lim),sy(-rho*lim));sctx.lineTo(sx(lim),sy(rho*lim));sctx.stroke();
// points
const fac=Math.sqrt(1-rho*rho);
let sz=0,szp=0,scov=0;
for(let i=0;i<N;i++){const z=zSamples[i],zp=rho*z+fac*etaSamples[i];sz+=z*z;szp+=zp*zp;scov+=z*zp;
sctx.fillStyle='rgba(76,201,240,.5)';sctx.beginPath();sctx.arc(sx(z),sy(zp),2,0,7);sctx.fill();}
document.getElementById('sVz').textContent=(sz/N).toFixed(2);
document.getElementById('sVzp').textContent=(szp/N).toFixed(2);
document.getElementById('sCov').textContent=(scov/N).toFixed(2);
}
const DEG=[1,2,3,4,5];
function drawBars(){
const {w,h,dpr}=fit(bars);bctx.setTransform(dpr,0,0,dpr,0,0);bctx.clearRect(0,0,w,h);
const padT=34,padB=24,padL=18,padR=18,y0=padT,y1=h-padB,n=DEG.length;
const gap=18,bw=Math.min(72,(w-padL-padR-gap*(n-1))/n),groupW=bw*n+gap*(n-1),startX=(w-groupW)/2;
const refY=y1-rho*(y1-y0);
bctx.strokeStyle='rgba(76,201,240,.45)';bctx.setLineDash([5,4]);bctx.lineWidth=1.5;bctx.beginPath();bctx.moveTo(padL,refY);bctx.lineTo(w-padR,refY);bctx.stroke();bctx.setLineDash([]);
bctx.fillStyle='rgba(76,201,240,.8)';bctx.font="500 11px 'JetBrains Mono',monospace";bctx.fillText('ρ¹='+rho.toFixed(2),w-padR-66,refY-6);
DEG.forEach((d,i)=>{const val=Math.pow(rho,d),x=startX+i*(bw+gap),bh=val*(y1-y0),yTop=y1-bh;
bctx.fillStyle=d===1?'#4cc9f0':'#f4a261';bctx.globalAlpha=d===1?1:0.82;bctx.fillRect(x,yTop,bw,bh);bctx.globalAlpha=1;
bctx.fillStyle=d===1?'#4cc9f0':'#f4a261';bctx.font="700 12px 'JetBrains Mono',monospace";bctx.textAlign='center';bctx.fillText(val.toFixed(3),x+bw/2,yTop-7);
bctx.fillStyle='#8b949e';bctx.font="400 11px 'JetBrains Mono',monospace";bctx.fillText('d='+d,x+bw/2,y1+15);bctx.textAlign='left';});
}
function updateText(){
const r=rho;
document.getElementById('takeaway').innerHTML=`当前 ρ = <span class="big">${r.toFixed(2)}</span>:线性成分 corr = ρ¹ = <b style="color:var(--linear)">${r.toFixed(3)}</b>,二次仅 ρ² = <b style="color:var(--nl)">${(r*r).toFixed(3)}</b>,五次 ρ⁵ = <b style="color:var(--nl)">${Math.pow(r,5).toFixed(3)}</b>。<br><br>任意混合的相关性是 ρᵈ 的<strong>加权平均</strong>,因 ρᵈ≤ρ,总和必 ≤ ρ,等号仅当 <b style="color:var(--linear)">w₁=1(纯线性)</b>。`;
document.getElementById('verdict').innerHTML=`最小化 L<sub>align</sub> ⟺ 最大化 Σ corr<sub>i</sub>。下界 <b>2(1−ρ)n</b> 当前 = <b>${(2*(1-r)).toFixed(3)}·n</b>,仅当每个 h<sub>i</sub> 线性时取到。<br>→ <b>最优编码器必须是线性的</b>。`;
}
function draw(){drawScatter();drawBars();updateText();}
function loop(now){if(auto){const dt=(now-lastTick)/1000;lastTick=now;let v=rho+autoDir*dt*0.18;if(v<=0.05){v=0.05;autoDir=1;}if(v>=0.97){v=0.97;autoDir=-1;}setRho(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')setRho(rho-0.03,true);else if(e.key==='ArrowRight')setRho(rho+0.03,true);});
window.addEventListener('resize',draw);
setRho(0.90,false);draw();requestAnimationFrame(loop);
</script>
</body>
</html>
@@ -0,0 +1,176 @@
<!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>
@@ -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>