ntc-ai's picture
Verify static learning-curve data normalization
14fcfd7 verified
Raw History Blame Contribute Delete
6.12 kB
const $ = id => document.getElementById(id);
const node = (tag, text, cls) => {const e=document.createElement(tag); if(text!==undefined)e.textContent=text; if(cls)e.className=cls; return e;};
const name = d => d === 'nova_anima' ? 'Nova' : 'Qwen';
const num = n => Number.isFinite(n) ? n.toFixed(4) : '—';
let data;
function pictures(target, row, columns) {
$(target).replaceChildren(...columns.map(([key,label])=>{
const figure=node('figure'),link=node('a'),img=node('img');
link.href=row.images[key];link.target='_blank';link.rel='noopener';
img.src=row.images[key];img.alt=`${label}: ${row.label || row.prompt}`;img.className='sample-image';
link.append(img);figure.append(link,node('figcaption',label));return figure;
}));
}
function drawCharacter() {
const row=data.characters.cases.find(r=>r.id===$('character').value&&r.domain===$('start').value);
const p=row.policies.find(p=>p.policy===$('policy').value),kind=p.policy.replaceAll('_','-');
const route=[row.domain,...p.audit.switches.map(s=>s.target)].map(name).join(' → ');
$('prompt').textContent=row.prompt;
$('trajectory').replaceChildren(...p.domains.map((d,i)=>node('span',`${i+1} ${name(d)}`,d==='nova_anima'?'sd15':'sana')));
$('schedule').textContent=`${row.resolution}px · seed ${row.seed} · 24 denoising steps. ${p.audit.switches.map(s=>`After step ${s.after_call}: ${name(s.source)} → ${name(s.target)}`).join('. ')}.`;
pictures('pictures',row,[['shared-single',`${name(row.domain)} only · same noise grid`],[kind,`${route} · learned bridges`],[kind+'-codec',`${route} · full decode/encode reference`]]);
$('metrics').textContent=`Learned versus matching reference: LPIPS ${num(p.lpips_vs_matched_full_codec)}. Difference from ${name(row.domain)} only: learned ${num(p.lpips_vs_single_model)}; reference ${num(p.full_codec_lpips_vs_single_model)}. Exact repeat: ${p.repeat_bitwise_equal?'passed':'unverified'}.`;
}
function drawBridge() {
const row=data.reconstructions.find(r=>r.domain===$('direction').value);
$('bridge-prompt').textContent=row.prompt;
$('bridge-step').textContent=`${row.training_step.toLocaleString()} resumed updates · ${row.kind} · seed ${row.seed}`;
pictures('bridge-pictures',row,[['source',`${name(row.domain)} · source`],['prediction',`Learned bridge → ${name(row.other_domain)}`],['target',`Full-codec target → ${name(row.other_domain)}`]]);
$('bridge-metrics').textContent=`Learned LPIPS ${num(row.metrics.lpips)}; full-codec target ${num(row.metrics.teacher_lpips)}; learned versus target ${num(row.metrics.lpips_vs_teacher)}.`;
}
function chart(metric) {
const ns='http://www.w3.org/2000/svg',svg=document.createElementNS(ns,'svg');
svg.setAttribute('viewBox','0 0 620 280');svg.setAttribute('role','img');svg.setAttribute('aria-label',`Validation ${metric} versus resumed updates`);
const values=data.runs.flatMap(r=>r.validation.filter(v=>Number.isFinite(v.metrics[metric])));
if(!values.length)return;
const xmax=Math.max(...values.map(v=>v.step),1), ymax=Math.max(...values.map(v=>v.metrics[metric]),.001)*1.08;
const x=v=>52+550*v/xmax,y=v=>236-210*v/ymax;
function add(tag,attrs,text){const e=document.createElementNS(ns,tag);for(const [k,v]of Object.entries(attrs))e.setAttribute(k,v);if(text!==undefined)e.textContent=text;svg.append(e);return e;}
for(let i=0;i<=4;i++){const v=ymax*i/4;add('line',{x1:52,x2:602,y1:y(v),y2:y(v),class:'gridline'});add('text',{x:45,y:y(v)+4,'text-anchor':'end'},v.toFixed(3));const step=xmax*i/4;add('text',{x:x(step),y:259,'text-anchor':'middle'},`${Math.round(step/1000)}k`);}
data.runs.forEach((r,i)=>{const rows=r.validation.filter(v=>Number.isFinite(v.metrics[metric])),color=i?'#edb784':'#88e0c0';
add('polyline',{points:rows.map(v=>`${x(v.step)},${y(v.metrics[metric])}`).join(' '),fill:'none',stroke:color,'stroke-width':2});
rows.forEach(v=>{const c=add('circle',{cx:x(v.step),cy:y(v.metrics[metric]),r:3,fill:color}),t=document.createElementNS(ns,'title');t.textContent=`${r.label}: ${v.step} updates, ${metric} ${v.metrics[metric]}`;c.append(t);});
});$(metric).replaceChildren(svg);
}
fetch('archive.json').then(r=>{if(!r.ok)throw Error(`HTTP ${r.status}`);return r.json();}).then(value=>{
data=value;
data.runs.forEach(r=>{r.validation=r.validation.map(v=>({...v,metrics:v.metrics||v}));});
$('status').textContent=data.stopped?'Training stopped · experiment preserved':'Experiment snapshot · publication in progress';
$('snapshot').textContent=`Archived ${data.created_at}. Full-codec supervision is preserved for reference; the next experiment will investigate latent-only preparation and training.`;
if(data.stopped){const s=data.stop_record;$('snapshot').textContent+=` Stopped at ${s.checkpoints.forward.step.toLocaleString()} forward / ${s.checkpoints.reverse.step.toLocaleString()} reverse resumed updates, with ${s.total_training_prompts_prepared.toLocaleString()} / ${s.planned_training_prompts.toLocaleString()} training prompts prepared.`;}
const cps=data.characters.checkpoints;$('checkpoints').textContent=`Character snapshot: Nova → Qwen ${cps.forward.step.toLocaleString()} updates; Qwen → Nova ${cps.reverse.step.toLocaleString()} updates. Latest/best archived weights and curves are labeled separately.`;
const cases=[...new Map(data.characters.cases.map(r=>[r.id,r])).values()];$('character').replaceChildren(...cases.map(r=>{const o=node('option',r.label);o.value=r.id;return o;}));
$('summary').replaceChildren(...data.runs.map(r=>{const card=node('div',undefined,'stat'),last=r.validation.at(-1);card.append(node('label',r.label),node('strong',num(last?.metrics.lpips)),node('span',`Validation LPIPS at ${last?.step.toLocaleString()} updates. Selected best: ${num(r.best?.lpips)} at ${r.best?.step.toLocaleString()}.`));return card;}));
drawCharacter();drawBridge();chart('lpips');chart('latent_nmse');
}).catch(e=>{$('status').textContent=`Unable to load archive: ${e.message}`;});
for(const id of ['character','start','policy'])$(id).addEventListener('change',drawCharacter);
$('direction').addEventListener('change',drawBridge);