on-policy-distillation / app /src /content /embeds /d3-lambda-ablations.html
cmpatino's picture
cmpatino HF Staff
Add HTML embeds
ef76aaf
<div class="d3-lambda-ablations"></div>
<style>
.d3-lambda-ablations {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
position: relative;
}
.d3-lambda-ablations svg {
display: block;
width: 100%;
}
.d3-lambda-ablations .axes path,
.d3-lambda-ablations .axes line {
stroke: var(--axis-color, #333);
}
.d3-lambda-ablations .axes text {
fill: var(--tick-color, #666);
font-size: 12px;
}
.d3-lambda-ablations .grid line {
stroke: var(--grid-color, rgba(0,0,0,.08));
}
.d3-lambda-ablations .d3-tooltip {
position: absolute;
pointer-events: none;
padding: 8px 10px;
border-radius: 8px;
font-size: 12px;
line-height: 1.35;
border: 1px solid var(--border-color, #ddd);
background: var(--surface-bg, #fff);
color: var(--text-color, #000);
box-shadow: 0 4px 24px rgba(0,0,0,.18);
opacity: 0;
transition: opacity .12s ease;
}
.d3-lambda-ablations .header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 12px;
flex-wrap: wrap;
gap: 12px;
}
.d3-lambda-ablations .legend {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 6px;
}
.d3-lambda-ablations .legend-title {
font-size: 12px;
font-weight: 700;
color: var(--text-color, #000);
}
.d3-lambda-ablations .legend .items {
display: flex;
flex-wrap: wrap;
gap: 8px 14px;
}
.d3-lambda-ablations .legend .item {
display: inline-flex;
align-items: center;
gap: 6px;
white-space: nowrap;
font-size: 12px;
color: var(--text-color, #000);
}
.d3-lambda-ablations .legend .swatch {
width: 14px;
height: 14px;
border-radius: 3px;
border: 1px solid var(--border-color, #ddd);
}
.d3-lambda-ablations .legend .swatch-line {
width: 20px;
height: 2px;
border: none;
border-radius: 0;
background: none;
border-top: 2px dashed #2ca02c;
opacity: 0.7;
}
.d3-lambda-ablations .controls {
display: flex;
gap: 16px;
align-items: center;
flex-wrap: wrap;
}
.d3-lambda-ablations .controls .control-group {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 6px;
}
.d3-lambda-ablations .controls label {
font-size: 12px;
font-weight: 700;
color: var(--text-color, #000);
}
.d3-lambda-ablations .controls select {
font-size: 12px;
padding: 8px 28px 8px 10px;
border: 1px solid var(--border-color, #ddd);
border-radius: 8px;
background: var(--surface-bg, #fff);
color: var(--text-color, #000);
cursor: pointer;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23666' d='M6 9L1 4h10z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 8px center;
}
.d3-lambda-ablations .controls select:focus {
outline: 2px solid var(--primary-color, #0066cc);
outline-offset: 2px;
}
</style>
<script>
(() => {
const ensureD3 = (cb) => {
if (window.d3 && typeof window.d3.select === 'function') return cb();
let s = document.getElementById('d3-cdn-script');
if (!s) {
s = document.createElement('script');
s.id = 'd3-cdn-script';
s.src = 'https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js';
document.head.appendChild(s);
}
const onReady = () => {
if (window.d3 && typeof window.d3.select === 'function') cb();
};
s.addEventListener('load', onReady, { once: true });
if (window.d3) onReady();
};
const bootstrap = () => {
const scriptEl = document.currentScript;
let container = scriptEl ? scriptEl.previousElementSibling : null;
if (!(container && container.classList && container.classList.contains('d3-lambda-ablations'))) {
const candidates = Array.from(document.querySelectorAll('.d3-lambda-ablations'))
.filter((el) => !(el.dataset && el.dataset.mounted === 'true'));
container = candidates[candidates.length - 1] || null;
}
if (!container) return;
if (container.dataset) {
if (container.dataset.mounted === 'true') return;
container.dataset.mounted = 'true';
}
// Tooltip
container.style.position = container.style.position || 'relative';
let tip = container.querySelector('.d3-tooltip');
let tipInner;
if (!tip) {
tip = document.createElement('div');
tip.className = 'd3-tooltip';
tipInner = document.createElement('div');
tipInner.className = 'd3-tooltip__inner';
tipInner.style.textAlign = 'left';
tip.appendChild(tipInner);
container.appendChild(tip);
} else {
tipInner = tip.querySelector('.d3-tooltip__inner') || tip;
}
// Header (legend + controls)
let header = container.querySelector('.header');
if (!header) {
header = document.createElement('div');
header.className = 'header';
container.appendChild(header);
}
// Legend
let legend = header.querySelector('.legend');
if (!legend) {
legend = document.createElement('div');
legend.className = 'legend';
header.appendChild(legend);
}
// Controls
let controls = header.querySelector('.controls');
if (!controls) {
controls = document.createElement('div');
controls.className = 'controls';
header.appendChild(controls);
}
let viewControl = controls.querySelector('.control-group');
if (!viewControl) {
viewControl = document.createElement('div');
viewControl.className = 'control-group';
const label = document.createElement('label');
label.textContent = 'View';
label.setAttribute('for', 'view-select-lambda');
const select = document.createElement('select');
select.id = 'view-select-lambda';
select.innerHTML = '<option value="line">Learning Curve</option><option value="bar-teacher">Distilled Ratio</option>';
viewControl.appendChild(label);
viewControl.appendChild(select);
controls.appendChild(viewControl);
}
const viewSelect = controls.querySelector('select');
// SVG
const svg = d3.select(container).append('svg').attr('width', '100%').style('display', 'block');
const gRoot = svg.append('g');
// State
let width = 800, height = 360;
const margin = { top: 16, right: 28, bottom: 56, left: 64 };
let currentView = 'line';
let data = [];
let lambdas = [];
let colorScale;
let teacherPerformance = 0;
function updateSize() {
width = container.clientWidth || 800;
height = Math.max(260, Math.round(width / 3));
svg.attr('width', width).attr('height', height);
gRoot.attr('transform', `translate(${margin.left},${margin.top})`);
return {
innerWidth: width - margin.left - margin.right,
innerHeight: height - margin.top - margin.bottom
};
}
function makeLegend(seriesNames, colorFor, includeTeacher = false) {
let title = legend.querySelector('.legend-title');
if (!title) {
title = document.createElement('div');
title.className = 'legend-title';
title.textContent = 'Lambda';
legend.appendChild(title);
}
let items = legend.querySelector('.items');
if (!items) {
items = document.createElement('div');
items.className = 'items';
legend.appendChild(items);
}
items.innerHTML = '';
seriesNames.forEach(name => {
const el = document.createElement('span');
el.className = 'item';
const sw = document.createElement('span');
sw.className = 'swatch';
sw.style.background = colorFor(name);
const txt = document.createElement('span');
// Strip "Lambda=" prefix from display name
const displayName = name.replace('Lambda=', '');
txt.textContent = displayName;
el.appendChild(sw);
el.appendChild(txt);
items.appendChild(el);
});
// Add teacher line to legend
if (includeTeacher) {
const el = document.createElement('span');
el.className = 'item';
const sw = document.createElement('span');
sw.className = 'swatch-line';
const txt = document.createElement('span');
txt.textContent = 'Teacher';
el.appendChild(sw);
el.appendChild(txt);
items.appendChild(el);
}
}
function renderLine() {
const { innerWidth, innerHeight } = updateSize();
gRoot.selectAll('*').remove();
const x = d3.scaleLinear()
.domain([0, d3.max(data, d => d.step) || 1])
.range([0, innerWidth]);
const y = d3.scaleLinear()
.domain([0, d3.max(data, d => Math.max(d.value, teacherPerformance)) || 1])
.range([innerHeight, 0])
.nice();
// Grid
const gridGroup = gRoot.append('g').attr('class', 'grid');
gridGroup.selectAll('line.grid-y')
.data(y.ticks(6))
.join('line')
.attr('class', 'grid-y')
.attr('x1', 0)
.attr('x2', innerWidth)
.attr('y1', d => y(d))
.attr('y2', d => y(d));
// Axes
const axesGroup = gRoot.append('g').attr('class', 'axes');
axesGroup.append('g')
.attr('transform', `translate(0,${innerHeight})`)
.call(d3.axisBottom(x).ticks(6));
axesGroup.append('g')
.call(d3.axisLeft(y).ticks(6));
// Axis labels
axesGroup.append('text')
.attr('x', innerWidth / 2)
.attr('y', innerHeight + 40)
.attr('text-anchor', 'middle')
.attr('fill', 'var(--text-color, #000)')
.style('font-size', '12px')
.style('font-weight', '700')
.text('Training Step');
axesGroup.append('text')
.attr('transform', 'rotate(-90)')
.attr('x', -innerHeight / 2)
.attr('y', -48)
.attr('text-anchor', 'middle')
.attr('fill', 'var(--text-color, #000)')
.style('font-size', '12px')
.style('font-weight', '700')
.text('Pass Rate');
// Group data by lambda
const grouped = d3.group(data, d => d.Lambda);
// Lines
const lineGroup = gRoot.append('g').attr('class', 'lines');
// Teacher performance line
lineGroup.append('line')
.attr('x1', 0)
.attr('x2', innerWidth)
.attr('y1', y(teacherPerformance))
.attr('y2', y(teacherPerformance))
.attr('stroke', '#2ca02c')
.attr('stroke-width', 2)
.attr('stroke-dasharray', '5,5')
.attr('opacity', 0.5);
lambdas.forEach((lambda, i) => {
const lambdaData = grouped.get(lambda) || [];
const sortedData = lambdaData.sort((a, b) => a.step - b.step);
const line = d3.line()
.x(d => x(d.step))
.y(d => y(d.value));
lineGroup.append('path')
.datum(sortedData)
.attr('fill', 'none')
.attr('stroke', colorScale(lambda))
.attr('stroke-width', 2)
.attr('d', line);
// Points
lineGroup.selectAll(`.point-${i}`)
.data(sortedData)
.join('circle')
.attr('class', `point-${i}`)
.attr('cx', d => x(d.step))
.attr('cy', d => y(d.value))
.attr('r', 4)
.attr('fill', colorScale(lambda))
.attr('stroke', 'var(--surface-bg, #fff)')
.attr('stroke-width', 1.5)
.style('cursor', 'pointer')
.on('mouseenter', (event, d) => {
tipInner.innerHTML = `<strong>${d.Lambda}</strong><br/>Step: ${d.step}<br/>pass@1:4: ${d.value.toFixed(4)}`;
tip.style.opacity = '1';
const [px, py] = d3.pointer(event, container);
tip.style.transform = `translate(${px + 10}px, ${py - 10}px)`;
})
.on('mouseleave', () => {
tip.style.opacity = '0';
});
});
}
function renderBarTeacher() {
const { innerWidth, innerHeight } = updateSize();
gRoot.selectAll('*').remove();
// Get best performance per lambda
const grouped = d3.group(data, d => d.Lambda);
const bestData = [];
lambdas.forEach(lambda => {
const lambdaData = grouped.get(lambda) || [];
if (lambdaData.length > 0) {
const best = lambdaData.reduce((a, b) => a.value > b.value ? a : b);
const improvement = best.value / teacherPerformance;
bestData.push({
lambda: lambda,
displayLambda: lambda.replace('Lambda=', ''),
improvement: improvement,
value: best.value
});
}
});
bestData.sort((a, b) => b.improvement - a.improvement);
const x = d3.scaleBand()
.domain(bestData.map(d => d.displayLambda))
.range([0, innerWidth])
.padding(0.2);
const y = d3.scaleLinear()
.domain([0, d3.max(bestData, d => d.improvement) || 1])
.range([innerHeight, 0])
.nice();
// Grid
const gridGroup = gRoot.append('g').attr('class', 'grid');
gridGroup.selectAll('line.grid-y')
.data(y.ticks(6))
.join('line')
.attr('class', 'grid-y')
.attr('x1', 0)
.attr('x2', innerWidth)
.attr('y1', d => y(d))
.attr('y2', d => y(d));
// Axes
const axesGroup = gRoot.append('g').attr('class', 'axes');
axesGroup.append('g')
.attr('transform', `translate(0,${innerHeight})`)
.call(d3.axisBottom(x));
axesGroup.append('g')
.call(d3.axisLeft(y).ticks(6));
// Axis labels
axesGroup.append('text')
.attr('x', innerWidth / 2)
.attr('y', innerHeight + 40)
.attr('text-anchor', 'middle')
.attr('fill', 'var(--text-color, #000)')
.style('font-size', '12px')
.style('font-weight', '700')
.text('Lambda');
axesGroup.append('text')
.attr('transform', 'rotate(-90)')
.attr('x', -innerHeight / 2)
.attr('y', -48)
.attr('text-anchor', 'middle')
.attr('fill', 'var(--text-color, #000)')
.style('font-size', '12px')
.style('font-weight', '700')
.text('Student / Teacher Performance');
// Bars
const barGroup = gRoot.append('g').attr('class', 'bars');
barGroup.selectAll('rect')
.data(bestData)
.join('rect')
.attr('x', d => x(d.displayLambda))
.attr('y', d => y(d.improvement))
.attr('width', x.bandwidth())
.attr('height', d => Math.max(0, innerHeight - y(d.improvement)))
.attr('fill', d => colorScale(d.lambda))
.style('cursor', 'pointer')
.on('mouseenter', (event, d) => {
tipInner.innerHTML = `<strong>Lambda=${d.displayLambda}</strong><br/>Student: ${d.value.toFixed(4)}<br/>Teacher: ${teacherPerformance.toFixed(4)}<br/>Ratio: ${d.improvement.toFixed(4)}`;
tip.style.opacity = '1';
const [px, py] = d3.pointer(event, container);
tip.style.transform = `translate(${px + 10}px, ${py - 10}px)`;
})
.on('mouseleave', () => {
tip.style.opacity = '0';
});
}
function render() {
if (currentView === 'line') {
renderLine();
} else if (currentView === 'bar-teacher') {
renderBarTeacher();
}
}
// Load data
const fetchFirstAvailable = async (paths) => {
for (const p of paths) {
try {
const r = await fetch(p, { cache: 'no-cache' });
if (r.ok) return await r.text();
} catch (_) {}
}
throw new Error('CSV not found');
};
const CSV_PATHS = [
'/data/lambda_ablations.csv',
'./assets/data/lambda_ablations.csv',
'../assets/data/lambda_ablations.csv',
'../../assets/data/lambda_ablations.csv'
];
fetchFirstAvailable(CSV_PATHS)
.then(csvText => {
data = d3.csvParse(csvText, d => ({
Lambda: d.Lambda,
value: +d.value,
step: +d.step,
teacher_performance: +d.teacher_performance
}));
lambdas = [...new Set(data.map(d => d.Lambda))];
teacherPerformance = data[0].teacher_performance;
// Sort lambdas by best performance
const grouped = d3.group(data, d => d.Lambda);
const lambdaPerf = lambdas.map(l => {
const lData = grouped.get(l) || [];
const maxVal = d3.max(lData, d => d.value) || 0;
return { lambda: l, maxVal };
});
lambdaPerf.sort((a, b) => b.maxVal - a.maxVal);
lambdas = lambdaPerf.map(d => d.lambda);
// Colors
const catColors = window.ColorPalettes
? window.ColorPalettes.getColors('categorical', lambdas.length)
: ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f'];
colorScale = d3.scaleOrdinal().domain(lambdas).range(catColors);
makeLegend(lambdas, colorScale, true);
render();
// Event listeners
viewSelect.addEventListener('change', (e) => {
currentView = e.target.value;
render();
});
// Resize
const rerender = () => render();
if (window.ResizeObserver) {
const ro = new ResizeObserver(() => rerender());
ro.observe(container);
} else {
window.addEventListener('resize', rerender);
}
})
.catch(err => {
const pre = document.createElement('pre');
pre.style.color = 'red';
pre.style.fontSize = '12px';
pre.textContent = `Failed to load data: ${err.message}`;
container.appendChild(pre);
});
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => ensureD3(bootstrap), { once: true });
} else {
ensureD3(bootstrap);
}
})();
</script>