Files
worldmodel/JEPA/math/animations/ou_mehler_explainer.html
T
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

194 lines
15 KiB
HTML
Raw 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>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>