smol-training-playbook / app /src /content /embeds /d3-sft-lr-scan.html
tfrere's picture
tfrere HF Staff
fix data paths
eb7bbb0
<div class="d3-sft-lr-scan"></div>
<style>
.d3-sft-lr-scan {
width: 100%;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
position: relative;
}
.d3-sft-lr-scan svg {
display: block;
overflow: visible;
}
.d3-sft-lr-scan .axes path,
.d3-sft-lr-scan .axes line {
stroke: var(--axis-color);
shape-rendering: crispEdges;
}
.d3-sft-lr-scan .axes text {
fill: var(--tick-color);
font-size: 11px;
}
.d3-sft-lr-scan .grid line {
stroke: var(--grid-color);
stroke-dasharray: 2, 2;
shape-rendering: crispEdges;
}
.d3-sft-lr-scan .axis-label {
fill: var(--text-color);
font-size: 12px;
font-weight: 600;
}
.d3-sft-lr-scan .line-think {
fill: none;
stroke-width: 2.5;
stroke-linecap: round;
stroke-linejoin: round;
}
.d3-sft-lr-scan .line-no-think {
fill: none;
stroke-width: 2.5;
stroke-linecap: round;
stroke-linejoin: round;
}
.d3-sft-lr-scan .dot {
stroke: var(--surface-bg);
stroke-width: 2;
}
.d3-sft-lr-scan .header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
margin-top: 16px;
flex-wrap: wrap;
}
.d3-sft-lr-scan .legend {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 6px;
}
.d3-sft-lr-scan .legend-title {
font-size: 12px;
font-weight: 700;
color: var(--text-color);
}
.d3-sft-lr-scan .legend .items {
display: flex;
flex-wrap: wrap;
gap: 8px 14px;
}
.d3-sft-lr-scan .legend .item {
display: inline-flex;
align-items: center;
gap: 6px;
white-space: nowrap;
font-size: 12px;
color: var(--text-color);
}
.d3-sft-lr-scan .legend .swatch {
width: 14px;
height: 14px;
border-radius: 3px;
border: 1px solid var(--border-color);
}
.d3-sft-lr-scan .legend .swatch-line {
width: 20px;
height: 2px;
border: none;
}
.d3-sft-lr-scan .controls {
display: flex;
gap: 16px;
align-items: flex-start;
justify-content: flex-end;
flex-wrap: wrap;
}
.d3-sft-lr-scan .control-group {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 6px;
}
.d3-sft-lr-scan .controls label {
font-size: 12px;
font-weight: 700;
color: var(--text-color);
}
.d3-sft-lr-scan .controls select {
font-size: 12px;
padding: 8px 28px 8px 10px;
border: 1px solid var(--border-color);
border-radius: 8px;
background: var(--surface-bg);
color: var(--text-color);
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-sft-lr-scan .controls select:focus {
outline: 2px solid var(--primary-color);
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-sft-lr-scan'))) {
const candidates = Array.from(document.querySelectorAll('.d3-sft-lr-scan'))
.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';
}
const CSV_PATHS = [
'/data/sft-lr-scan.csv',
'./assets/data/sft-lr-scan.csv',
'../assets/data/sft-lr-scan.csv',
'../../assets/data/sft-lr-scan.csv'
];
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 in any of the expected locations');
};
// 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';
Object.assign(tip.style, {
position: 'absolute',
top: '0px',
left: '0px',
transform: 'translate(-9999px, -9999px)',
pointerEvents: 'none',
padding: '8px 10px',
borderRadius: '8px',
fontSize: '12px',
lineHeight: '1.35',
border: '1px solid var(--border-color)',
background: 'var(--surface-bg)',
color: 'var(--text-color)',
boxShadow: '0 4px 24px rgba(0,0,0,.18)',
opacity: '0',
transition: 'opacity .12s ease'
});
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;
}
const showTooltip = (html, event) => {
tipInner.innerHTML = html;
const [mx, my] = d3.pointer(event, container);
const offsetX = 12, offsetY = 12;
tip.style.transform = `translate(${mx + offsetX}px, ${my + offsetY}px)`;
tip.style.opacity = '1';
};
const hideTooltip = () => {
tip.style.opacity = '0';
setTimeout(() => {
tip.style.transform = 'translate(-9999px, -9999px)';
}, 120);
};
// SVG setup
const svg = d3.select(container).append('svg').attr('width', '100%').style('display', 'block');
const gRoot = svg.append('g');
const gGrid = gRoot.append('g').attr('class', 'grid');
const gAxes = gRoot.append('g').attr('class', 'axes');
const gLines = gRoot.append('g').attr('class', 'lines');
const gDots = gRoot.append('g').attr('class', 'dots');
let width = 800, height = 360;
const margin = { top: 16, right: 28, bottom: 56, left: 64 };
// State
let data = [];
let benchmarks = [];
let currentMetric = 'Average';
const updateSize = () => {
width = container.clientWidth || 800;
height = Math.max(260, Math.round(width / 2.5));
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
};
};
// Get colors
const getColors = () => {
if (window.ColorPalettes && window.ColorPalettes.getColors) {
const colors = window.ColorPalettes.getColors('categorical', 2);
return {
think: colors[0],
noThink: colors[1]
};
}
return {
think: 'var(--primary-color)',
noThink: '#7F7F7F'
};
};
const render = () => {
if (data.length === 0) return;
const { innerWidth, innerHeight } = updateSize();
const colors = getColors();
// Separate data by mode
const thinkData = data.filter(d => d.mode === '/think');
const noThinkData = data.filter(d => d.mode === '/no_think');
// Get metric values
const getMetricValue = (d) => {
if (currentMetric === 'Average') {
return d.average;
}
return d[currentMetric];
};
// Scales - use log scale for learning rate
const learningRates = [1e-6, 3e-6, 1e-5, 3e-5, 1e-4];
const xScale = d3.scaleLog()
.domain([1e-6, 1e-4])
.range([0, innerWidth])
.nice();
const allValues = data.map(getMetricValue).filter(v => v != null);
const yScale = d3.scaleLinear()
.domain([0, d3.max(allValues) * 1.1 || 100])
.range([innerHeight, 0])
.nice();
// Grid
gGrid.selectAll('.grid-line').data(yScale.ticks(5))
.join('line')
.attr('class', 'grid-line')
.attr('x1', 0)
.attr('x2', innerWidth)
.attr('y1', d => yScale(d))
.attr('y2', d => yScale(d));
// Axes
const xAxis = d3.axisBottom(xScale)
.tickValues(learningRates)
.tickFormat(d3.format('.0e'));
const yAxis = d3.axisLeft(yScale).ticks(5);
gAxes.selectAll('.x-axis').data([0])
.join('g')
.attr('class', 'x-axis')
.attr('transform', `translate(0,${innerHeight})`)
.call(xAxis);
gAxes.selectAll('.y-axis').data([0])
.join('g')
.attr('class', 'y-axis')
.call(yAxis);
// Axis labels
gAxes.selectAll('.x-label').data([0])
.join('text')
.attr('class', 'x-label axis-label')
.attr('text-anchor', 'middle')
.attr('x', innerWidth / 2)
.attr('y', innerHeight + 40)
.text('Learning rate');
gAxes.selectAll('.y-label').data([0])
.join('text')
.attr('class', 'y-label axis-label')
.attr('text-anchor', 'middle')
.attr('transform', 'rotate(-90)')
.attr('x', -innerHeight / 2)
.attr('y', -48)
.text('Score (%)');
// Line generator
const line = d3.line()
.x(d => xScale(d.learningRate))
.y(d => yScale(getMetricValue(d)))
.curve(d3.curveMonotoneX);
// Think line
gLines.selectAll('.line-think')
.data([thinkData])
.join('path')
.attr('class', 'line-think')
.attr('d', line)
.attr('stroke', colors.think)
.attr('stroke-width', 2.5)
.attr('fill', 'none')
.attr('stroke-linecap', 'round')
.attr('stroke-linejoin', 'round');
// No-think line
gLines.selectAll('.line-no-think')
.data([noThinkData])
.join('path')
.attr('class', 'line-no-think')
.attr('d', line)
.attr('stroke', colors.noThink)
.attr('stroke-width', 2.5)
.attr('fill', 'none')
.attr('stroke-linecap', 'round')
.attr('stroke-linejoin', 'round');
// Dots for think mode
gDots.selectAll('.dot-think')
.data(thinkData)
.join('circle')
.attr('class', 'dot dot-think')
.attr('cx', d => xScale(d.learningRate))
.attr('cy', d => yScale(getMetricValue(d)))
.attr('r', 4)
.attr('fill', colors.think)
.on('mouseenter', (event, d) => {
const value = getMetricValue(d);
const html = `<strong>Learning Rate: ${d3.format('.0e')(d.learningRate)}</strong><br/>Mode: /think<br/>Score: ${value.toFixed(2)}%`;
showTooltip(html, event);
})
.on('mouseleave', hideTooltip);
// Dots for no-think mode
gDots.selectAll('.dot-no-think')
.data(noThinkData)
.join('circle')
.attr('class', 'dot dot-no-think')
.attr('cx', d => xScale(d.learningRate))
.attr('cy', d => yScale(getMetricValue(d)))
.attr('r', 4)
.attr('fill', colors.noThink)
.on('mouseenter', (event, d) => {
const value = getMetricValue(d);
const html = `<strong>Learning Rate: ${d3.format('.0e')(d.learningRate)}</strong><br/>Mode: /no_think<br/>Score: ${value.toFixed(2)}%`;
showTooltip(html, event);
})
.on('mouseleave', hideTooltip);
// Update legend
updateLegend(colors);
};
const ensureHeader = () => {
let header = container.querySelector('.header');
if (!header) {
header = document.createElement('div');
header.className = 'header';
container.appendChild(header);
// Create legend container first
const legend = document.createElement('div');
legend.className = 'legend';
header.appendChild(legend);
// Create controls container second
const controls = document.createElement('div');
controls.className = 'controls';
header.appendChild(controls);
}
return header;
};
const updateLegend = (colors) => {
const header = ensureHeader();
let legend = header.querySelector('.legend');
let title = legend.querySelector('.legend-title');
if (!title) {
title = document.createElement('div');
title.className = 'legend-title';
title.textContent = 'Legend';
legend.appendChild(title);
}
let items = legend.querySelector('.items');
if (!items) {
items = document.createElement('div');
items.className = 'items';
legend.appendChild(items);
}
items.innerHTML = '';
// Think line
const thinkItem = document.createElement('span');
thinkItem.className = 'item';
const thinkSwatch = document.createElement('span');
thinkSwatch.className = 'swatch-line';
thinkSwatch.style.background = colors.think;
const thinkText = document.createElement('span');
thinkText.textContent = '/think';
thinkItem.appendChild(thinkSwatch);
thinkItem.appendChild(thinkText);
items.appendChild(thinkItem);
// No-think line
const noThinkItem = document.createElement('span');
noThinkItem.className = 'item';
const noThinkSwatch = document.createElement('span');
noThinkSwatch.className = 'swatch-line';
noThinkSwatch.style.background = colors.noThink;
const noThinkText = document.createElement('span');
noThinkText.textContent = '/no_think';
noThinkItem.appendChild(noThinkSwatch);
noThinkItem.appendChild(noThinkText);
items.appendChild(noThinkItem);
};
const buildControls = () => {
const header = ensureHeader();
let controls = header.querySelector('.controls');
// Metric select
const metricGroup = document.createElement('div');
metricGroup.className = 'control-group';
const metricLabel = document.createElement('label');
metricLabel.setAttribute('for', 'metric-select-lr-scan');
metricLabel.textContent = 'Metric';
const metricSelect = document.createElement('select');
metricSelect.id = 'metric-select-lr-scan';
const metrics = ['Average', ...benchmarks];
metrics.forEach(m => {
const opt = document.createElement('option');
opt.value = m;
opt.textContent = m;
if (m === currentMetric) opt.selected = true;
metricSelect.appendChild(opt);
});
metricSelect.addEventListener('change', (e) => {
currentMetric = e.target.value;
render();
});
metricGroup.appendChild(metricLabel);
metricGroup.appendChild(metricSelect);
controls.appendChild(metricGroup);
};
// Load data
fetchFirstAvailable(CSV_PATHS)
.then(csvText => {
const rows = d3.csvParse(csvText);
// Get benchmark columns (all except Learning rate and Reasoning mode)
benchmarks = rows.columns.filter(c => c !== 'Learning rate' && c !== 'Reasoning mode');
// Parse data
data = rows.map(row => {
const learningRate = +row['Learning rate'];
const mode = row['Reasoning mode'];
const entry = {
learningRate: learningRate,
mode: mode
};
// Add all benchmark values
let sum = 0;
let count = 0;
benchmarks.forEach(b => {
const val = +row[b];
entry[b] = val;
sum += val;
count++;
});
// Calculate average
entry.average = sum / count;
return entry;
});
buildControls();
render();
// Resize handling
if (window.ResizeObserver) {
const ro = new ResizeObserver(() => render());
ro.observe(container);
} else {
window.addEventListener('resize', render);
}
})
.catch(err => {
const pre = document.createElement('pre');
pre.style.cssText = 'color:red;font-size:12px;padding:12px;';
pre.textContent = 'Error loading data: ' + err.message;
container.appendChild(pre);
});
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => ensureD3(bootstrap), { once: true });
} else {
ensureD3(bootstrap);
}
})();
</script>