// Global variables let trainingData = null; let charts = {}; // Color palette for different runs const colors = [ '#667eea', '#764ba2', '#f093fb', '#f5576c', '#4facfe', '#00f2fe', '#43e97b', '#38f9d7', '#fa7093', '#fee140', '#a8edea', '#fed6e3' ]; // Initialize the dashboard document.addEventListener('DOMContentLoaded', function() { loadData(); setupEventListeners(); }); // Load training data from JSON file async function loadData() { try { const response = await fetch('data.json'); trainingData = await response.json(); // Merge continuation logs from the same model run mergeContinuationLogs(); populateRunSelector(); createCharts(); updateRunSummary(); updateConfigDetails(); console.log('Data loaded and merged successfully:', trainingData); } catch (error) { console.error('Error loading data:', error); document.body.innerHTML = '
Error loading training data. Please check the console for details.
'; } } // Merge continuation logs from the same model run function mergeContinuationLogs() { const runGroups = {}; // Group runs by base model name trainingData.runs.forEach(run => { const baseName = run.run_name; if (!runGroups[baseName]) { runGroups[baseName] = []; } runGroups[baseName].push(run); }); // Merge runs with the same base name const mergedRuns = []; Object.entries(runGroups).forEach(([baseName, runs]) => { if (runs.length === 1) { // Single run, no merging needed mergedRuns.push(runs[0]); } else { // Multiple runs to merge console.log(`Merging ${runs.length} continuation logs for ${baseName}`); const mergedRun = { run_name: baseName, log_files: runs.map(r => r.log_file), training_metrics: [], evaluation_results: [], config: runs[0].config || {} }; // Merge training metrics (they should be continuous) runs.forEach(run => { if (run.training_metrics) { mergedRun.training_metrics.push(...run.training_metrics); } }); // Merge evaluation results (they should be continuous) runs.forEach(run => { if (run.evaluation_results) { mergedRun.evaluation_results.push(...run.evaluation_results); } }); // Sort by step number to ensure proper ordering mergedRun.training_metrics.sort((a, b) => a.step - b.step); mergedRun.evaluation_results.sort((a, b) => a.step - b.step); // Remove duplicates based on step number mergedRun.training_metrics = mergedRun.training_metrics.filter((metric, index, self) => index === 0 || metric.step !== self[index - 1].step ); mergedRun.evaluation_results = mergedRun.evaluation_results.filter((result, index, self) => index === 0 || result.step !== self[index - 1].step ); console.log(`Merged ${baseName}: ${mergedRun.training_metrics.length} training points, ${mergedRun.evaluation_results.length} eval points`); mergedRuns.push(mergedRun); } }); trainingData.runs = mergedRuns; } // Setup event listeners for controls function setupEventListeners() { document.getElementById('runSelect').addEventListener('change', function() { updateCharts(); updateRunSummary(); updateConfigDetails(); }); document.getElementById('showTraining').addEventListener('change', updateCharts); document.getElementById('showLearningRate').addEventListener('change', updateCharts); document.getElementById('showEvaluation').addEventListener('change', updateCharts); } // Populate run selector dropdown function populateRunSelector() { const select = document.getElementById('runSelect'); const runs = trainingData.runs; // Clear existing options select.innerHTML = ''; runs.forEach((run, index) => { const option = document.createElement('option'); option.value = index; option.textContent = run.run_name; select.appendChild(option); }); } // Create all charts function createCharts() { createLossChart(); createLRChart(); createEvalChart(); createCombinedChart(); } // Create training loss chart function createLossChart() { const ctx = document.getElementById('lossChart').getContext('2d'); charts.loss = new Chart(ctx, { type: 'line', data: getChartData('loss'), options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'Training Loss Over Time' }, legend: { position: 'top' } }, scales: { x: { type: 'linear', title: { display: true, text: 'Training Step' } }, y: { title: { display: true, text: 'Loss' }, beginAtZero: false } }, interaction: { intersect: false, mode: 'index' } } }); } // Create learning rate chart function createLRChart() { const ctx = document.getElementById('lrChart').getContext('2d'); charts.lr = new Chart(ctx, { type: 'line', data: getChartData('lr'), options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'Learning Rate Schedule' }, legend: { position: 'top' } }, scales: { x: { type: 'linear', title: { display: true, text: 'Training Step' } }, y: { title: { display: true, text: 'Learning Rate' }, type: 'logarithmic' } }, interaction: { intersect: false, mode: 'index' } } }); } // Create evaluation chart function createEvalChart() { const ctx = document.getElementById('evalChart').getContext('2d'); charts.eval = new Chart(ctx, { type: 'line', data: getChartData('eval'), options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'Paloma Evaluation Metrics' }, legend: { position: 'top' } }, scales: { x: { type: 'linear', title: { display: true, text: 'Training Step' } }, y: { title: { display: true, text: 'Perplexity' }, type: 'logarithmic' } }, interaction: { intersect: false, mode: 'index' } } }); } // Create combined chart function createCombinedChart() { const ctx = document.getElementById('combinedChart').getContext('2d'); charts.combined = new Chart(ctx, { type: 'line', data: getCombinedChartData(), options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'Combined Training Metrics' }, legend: { position: 'top' } }, scales: { x: { type: 'linear', title: { display: true, text: 'Training Step' } }, y: { title: { display: true, text: 'Value' } } }, interaction: { intersect: false, mode: 'index' } } }); } // Get chart data for specific metric type function getChartData(metricType) { const selectedRun = document.getElementById('runSelect').value; const runs = selectedRun === 'all' ? trainingData.runs : [trainingData.runs[selectedRun]]; const datasets = []; console.log(`Getting ${metricType} data for ${runs.length} runs:`, runs.map(r => r.run_name)); runs.forEach((run, runIndex) => { const color = colors[runIndex % colors.length]; if (metricType === 'loss') { if (run.training_metrics && run.training_metrics.length > 0) { const data = run.training_metrics.map(m => ({ x: m.step, y: m.loss })); console.log(`Loss data for ${run.run_name}:`, data.slice(0, 5), '...', data.slice(-5)); datasets.push({ label: run.run_name, data: data, borderColor: color, backgroundColor: color + '20', borderWidth: 2, fill: false, tension: 0.1 }); } } else if (metricType === 'lr') { if (run.training_metrics && run.training_metrics.length > 0) { const data = run.training_metrics.map(m => ({ x: m.step, y: m.learning_rate })); console.log(`LR data for ${run.run_name}:`, data.slice(0, 5), '...', data.slice(-5)); datasets.push({ label: run.run_name, data: data, borderColor: color, backgroundColor: color + '20', borderWidth: 2, fill: false, tension: 0.1 }); } } else if (metricType === 'eval') { if (run.evaluation_results && run.evaluation_results.length > 0) { const data = run.evaluation_results.map(m => ({ x: m.step, y: m.paloma })); console.log(`Eval data for ${run.run_name}:`, data.slice(0, 5), '...', data.slice(-5)); datasets.push({ label: run.run_name, data: data, borderColor: color, backgroundColor: color + '20', borderWidth: 2, fill: false, tension: 0.1 }); } } }); console.log(`Final ${metricType} datasets:`, datasets); return { datasets }; } // Get combined chart data function getCombinedChartData() { const selectedRun = document.getElementById('runSelect').value; const runs = selectedRun === 'all' ? trainingData.runs : [trainingData.runs[selectedRun]]; const datasets = []; runs.forEach((run, runIndex) => { const color = colors[runIndex % colors.length]; // Training loss if (run.training_metrics && run.training_metrics.length > 0) { datasets.push({ label: `${run.run_name} - Loss`, data: run.training_metrics.map(m => ({ x: m.step, y: m.loss })), borderColor: color, backgroundColor: color + '20', borderWidth: 2, fill: false, tension: 0.1 }); } // Learning rate (scaled) if (run.training_metrics && run.training_metrics.length > 0) { const maxLR = Math.max(...run.training_metrics.map(m => m.learning_rate)); const maxLoss = Math.max(...run.training_metrics.map(m => m.loss)); const scaleFactor = maxLoss / maxLR; datasets.push({ label: `${run.run_name} - LR (scaled)`, data: run.training_metrics.map(m => ({ x: m.step, y: m.learning_rate * scaleFactor })), borderColor: color + '80', backgroundColor: color + '10', borderWidth: 1, fill: false, tension: 0.1 }); } }); return { datasets }; } // Update all charts based on current selection function updateCharts() { if (charts.loss) { charts.loss.data = getChartData('loss'); charts.loss.update(); } if (charts.lr) { charts.lr.data = getChartData('lr'); charts.lr.update(); } if (charts.eval) { charts.eval.data = getChartData('eval'); charts.eval.update(); } if (charts.combined) { charts.combined.data = getCombinedChartData(); charts.combined.update(); } } // Update run summary section function updateRunSummary() { const container = document.getElementById('runSummary'); const selectedRun = document.getElementById('runSelect').value; const runs = selectedRun === 'all' ? trainingData.runs : [trainingData.runs[selectedRun]]; let html = '
'; runs.forEach(run => { const trainingPoints = run.training_metrics ? run.training_metrics.length : 0; const evalPoints = run.evaluation_results ? run.evaluation_results.length : 0; let finalLoss = 'N/A'; let finalLR = 'N/A'; let finalPaloma = 'N/A'; let stepRange = 'N/A'; if (run.training_metrics && run.training_metrics.length > 0) { const first = run.training_metrics[0]; const last = run.training_metrics[run.training_metrics.length - 1]; finalLoss = last.loss.toFixed(4); finalLR = last.learning_rate.toExponential(2); stepRange = `${first.step} → ${last.step}`; } if (run.evaluation_results && run.evaluation_results.length > 0) { const last = run.evaluation_results[run.evaluation_results.length - 1]; if (isFinite(last.paloma)) { finalPaloma = last.paloma.toExponential(2); } else { finalPaloma = '∞'; } } const logFiles = run.log_files ? run.log_files.join(', ') : run.log_file; html += `

${run.run_name}

Logs: ${logFiles}

Step Range: ${stepRange}
Training Points: ${trainingPoints}
Evaluation Points: ${evalPoints}
Final Loss: ${finalLoss}
Final LR: ${finalLR}
Final Paloma: ${finalPaloma}
`; }); html += '
'; container.innerHTML = html; } // Update configuration details section function updateConfigDetails() { const container = document.getElementById('configDetails'); const selectedRun = document.getElementById('runSelect').value; const runs = selectedRun === 'all' ? trainingData.runs : [trainingData.runs[selectedRun]]; let html = '
'; // Get unique config keys const allKeys = new Set(); runs.forEach(run => { if (run.config) { Object.keys(run.config).forEach(key => allKeys.add(key)); } }); allKeys.forEach(key => { const values = runs.map(run => run.config && run.config[key] !== undefined ? run.config[key] : 'N/A'); const uniqueValues = [...new Set(values)]; const displayValue = uniqueValues.length === 1 ? uniqueValues[0] : `${uniqueValues.join(' / ')}`; html += `
${key.replace(/_/g, ' ').toUpperCase()}
${displayValue}
`; }); html += '
'; container.innerHTML = html; } // Utility function to format large numbers function formatNumber(num) { if (num >= 1e9) return (num / 1e9).toFixed(2) + 'B'; if (num >= 1e6) return (num / 1e6).toFixed(2) + 'M'; if (num >= 1e3) return (num / 1e3).toFixed(2) + 'K'; return num.toString(); }