ConfidenceManifold / app /src /content /embeds /generation-gap.html
seonglae-holistic's picture
remove all em dashes from embeds
7bef06a
Raw
History Blame Contribute Delete
6.89 kB
<!-- Generation Gap: Teacher-forced vs model-generated probe performance -->
<div class="gengap-viz">
<svg id="gengap-svg"></svg>
</div>
<style>
.gengap-viz { position: relative; width: 100%; }
#gengap-svg { width: 100%; height: 300px; }
.gengap-tip {
position: absolute; background: var(--surface-bg); border: 1px solid var(--border-color);
border-radius: 8px; padding: 8px 12px; font-size: 11px; pointer-events: none;
box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 10; color: var(--text-color); line-height: 1.5;
}
@media (max-width: 640px) { #gengap-svg { height: 260px; } }
</style>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
(function(){
const container = document.querySelector('.gengap-viz');
const svg = d3.select('#gengap-svg');
const tip = d3.select(container).append('div').attr('class','gengap-tip').style('display','none');
// Model parameter sizes for sorting/annotation
const paramMap = {
'GPT-2': 0.124, 'GPT-2 Medium': 0.345, 'GPT-2 Large': 0.774,
'Gemma-2-2B': 2, 'Llama-3.2-1B': 1, 'Qwen2-1.5B': 1.5,
'Qwen2-7B': 7, 'Mistral-7B': 7
};
function fmtParams(b) { return b >= 1 ? b+'B' : Math.round(b*1000)+'M'; }
let gapData = null;
fetch('data/generation_gap.json').then(r => r.json()).then(data => { gapData = data; draw(); });
function draw() {
svg.selectAll('*').remove();
if (!gapData) return;
const data = gapData;
const models = data.models, tf = data.teacher_forced, gen = data.generated;
// Filter out models with anomalous tf (< 0.55, data quality issues), sort by param count
const valid = models.map((m,i) => ({m, tf: tf[i], gen: gen[i], gap: tf[i]-gen[i], params: paramMap[m]||0}))
.filter(d => d.tf > 0.55)
.sort((a,b) => a.params - b.params);
const rect = document.getElementById('gengap-svg').getBoundingClientRect();
const W = rect.width, H = rect.height;
const mg = {top:20,right:16,bottom:50,left:50};
const w = W-mg.left-mg.right, h = H-mg.top-mg.bottom;
svg.attr('viewBox',`0 0 ${W} ${H}`);
const g = svg.append('g').attr('transform',`translate(${mg.left},${mg.top})`);
const xBand = d3.scaleBand().domain(valid.map(d=>d.m)).range([0,w]).padding(0.25);
const y = d3.scaleLinear().domain([0.4, 1.0]).range([h,0]).nice();
// Grid
y.ticks(4).forEach(v => {
g.append('line').attr('x1',0).attr('x2',w).attr('y1',y(v)).attr('y2',y(v))
.attr('stroke','var(--grid-color)').attr('stroke-dasharray','2,3');
});
// Chance line
g.append('line').attr('x1',0).attr('x2',w).attr('y1',y(0.5)).attr('y2',y(0.5))
.attr('stroke','var(--border-color)').attr('stroke-dasharray','6,4');
valid.forEach((d,i) => {
const cx = xBand(d.m) + xBand.bandwidth()/2;
const yTF = y(Math.max(d.tf,0.4)), yGen = y(Math.max(d.gen,0.4));
// Dumbbell connector line
const conn = g.append('line').attr('x1',cx).attr('x2',cx)
.attr('y1',yTF).attr('y2',yTF)
.attr('stroke','var(--border-color)').attr('stroke-width',2).attr('opacity',0.4);
conn.transition().duration(500).delay(i*80).attr('y2',yGen);
// Gap fill - capsule shape (rect with semicircle caps)
const gapW = xBand.bandwidth()*0.3;
const gapR = gapW/2;
const gapH = Math.max(0, yGen - yTF);
if (gapH > 0) {
const capsule = g.append('path')
.attr('d', `M${cx-gapR},${yTF} A${gapR},${gapR} 0 0,1 ${cx+gapR},${yTF}
L${cx+gapR},${yGen} A${gapR},${gapR} 0 0,1 ${cx-gapR},${yGen} Z`)
.attr('fill','#ef4444').attr('opacity',0);
capsule.transition().duration(500).delay(i*80).attr('opacity',0.1);
}
// Teacher-forced dot (filled)
const dotTF = g.append('circle').attr('cx',cx).attr('cy',yTF)
.attr('r',0).attr('fill','#ea580c').attr('stroke','var(--surface-bg)').attr('stroke-width',2);
dotTF.transition().duration(400).delay(i*80+200).attr('r',6);
// Generated dot (outlined)
const dotGen = g.append('circle').attr('cx',cx).attr('cy',yGen)
.attr('r',0).attr('fill','var(--surface-bg)').attr('stroke','#a8a29e').attr('stroke-width',2.5);
dotGen.transition().duration(400).delay(i*80+300).attr('r',5);
// Gap annotation
if (d.gap > 0.05) {
g.append('text').attr('x',cx+xBand.bandwidth()*0.3+4).attr('y',(yTF+yGen)/2+3)
.attr('text-anchor','start').attr('fill','#ef4444').attr('font-size',9).attr('font-weight',700)
.attr('opacity',0).transition().duration(300).delay(i*80+500).attr('opacity',0.8)
.text('-'+(d.gap*100).toFixed(0)+'pp');
}
// Hover area
g.append('rect').attr('x',xBand(d.m)).attr('y',0).attr('width',xBand.bandwidth()).attr('height',h)
.attr('fill','transparent').attr('cursor','pointer')
.on('mouseenter', function(e) {
tip.style('display','block')
.html(`<strong>${d.m}</strong> <span style="opacity:0.5">${fmtParams(d.params)}</span><br/>`+
`Teacher-forced: <span style="color:#ea580c;font-weight:700">${d.tf.toFixed(3)}</span><br/>`+
`Generated: <span style="color:#a8a29e;font-weight:700">${d.gen.toFixed(3)}</span><br/>`+
`Gap: <strong style="color:#ef4444">${(d.gap*100).toFixed(1)}pp</strong>`)
.style('left',Math.min(e.offsetX+12,W-180)+'px').style('top',(e.offsetY-70)+'px');
}).on('mouseleave',()=>tip.style('display','none'));
});
// Legend
const lg = g.append('g').attr('transform',`translate(${w-140},2)`);
lg.append('circle').attr('cx',5).attr('cy',5).attr('r',5).attr('fill','#ea580c');
lg.append('text').attr('x',14).attr('y',9).attr('fill','var(--muted-color)').attr('font-size',10).text('Teacher-forced');
lg.append('circle').attr('cx',5).attr('cy',21).attr('r',4).attr('fill','var(--surface-bg)').attr('stroke','#a8a29e').attr('stroke-width',2);
lg.append('text').attr('x',14).attr('y',25).attr('fill','var(--muted-color)').attr('font-size',10).text('Generated');
// Axes - x-axis with model name + param size
const xAxis = g.append('g').attr('transform',`translate(0,${h})`).call(d3.axisBottom(xBand).tickSize(0));
xAxis.selectAll('text').attr('fill','var(--text-color)').attr('font-size',9)
.attr('transform','rotate(-25)').attr('text-anchor','end');
g.append('g').call(d3.axisLeft(y).ticks(4).tickFormat(d3.format('.2f')))
.selectAll('text').attr('fill','var(--tick-color)');
g.append('text').attr('transform','rotate(-90)').attr('x',-h/2).attr('y',-38)
.attr('text-anchor','middle').attr('fill','var(--muted-color)').attr('font-size',12).text('AUC');
svg.selectAll('.domain').attr('stroke','var(--axis-color)');
svg.selectAll('.tick line').attr('stroke','var(--axis-color)');
}
new ResizeObserver(draw).observe(document.getElementById('gengap-svg'));
})();
</script>