Add interactive HTML pages for planning explainer and spectral explainer
Sync to site1 / sync (push) Has been cancelled
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:
@@ -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>
|
||||
= <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>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>ρᵈ < ρ</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>
|
||||
Reference in New Issue
Block a user