| | |
| | let trainingData = null; |
| | let charts = {}; |
| |
|
| | |
| | const colors = [ |
| | '#667eea', '#764ba2', '#f093fb', '#f5576c', '#4facfe', '#00f2fe', |
| | '#43e97b', '#38f9d7', '#fa7093', '#fee140', '#a8edea', '#fed6e3' |
| | ]; |
| |
|
| | |
| | document.addEventListener('DOMContentLoaded', function() { |
| | loadData(); |
| | setupEventListeners(); |
| | }); |
| |
|
| | |
| | async function loadData() { |
| | try { |
| | const response = await fetch('data.json'); |
| | trainingData = await response.json(); |
| | |
| | |
| | mergeContinuationLogs(); |
| | |
| | populateRunSelector(); |
| | createCharts(); |
| | updateRunSummary(); |
| | updateConfigDetails(); |
| | |
| | console.log('Data loaded and merged successfully:', trainingData); |
| | } catch (error) { |
| | console.error('Error loading data:', error); |
| | document.body.innerHTML = '<div class="loading">Error loading training data. Please check the console for details.</div>'; |
| | } |
| | } |
| |
|
| | |
| | function mergeContinuationLogs() { |
| | const runGroups = {}; |
| | |
| | |
| | trainingData.runs.forEach(run => { |
| | const baseName = run.run_name; |
| | if (!runGroups[baseName]) { |
| | runGroups[baseName] = []; |
| | } |
| | runGroups[baseName].push(run); |
| | }); |
| | |
| | |
| | const mergedRuns = []; |
| | |
| | Object.entries(runGroups).forEach(([baseName, runs]) => { |
| | if (runs.length === 1) { |
| | |
| | mergedRuns.push(runs[0]); |
| | } else { |
| | |
| | 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 || {} |
| | }; |
| | |
| | |
| | runs.forEach(run => { |
| | if (run.training_metrics) { |
| | mergedRun.training_metrics.push(...run.training_metrics); |
| | } |
| | }); |
| | |
| | |
| | runs.forEach(run => { |
| | if (run.evaluation_results) { |
| | mergedRun.evaluation_results.push(...run.evaluation_results); |
| | } |
| | }); |
| | |
| | |
| | mergedRun.training_metrics.sort((a, b) => a.step - b.step); |
| | mergedRun.evaluation_results.sort((a, b) => a.step - b.step); |
| | |
| | |
| | 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; |
| | } |
| |
|
| | |
| | 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); |
| | } |
| |
|
| | |
| | function populateRunSelector() { |
| | const select = document.getElementById('runSelect'); |
| | const runs = trainingData.runs; |
| | |
| | |
| | select.innerHTML = '<option value="all">All Runs</option>'; |
| | |
| | runs.forEach((run, index) => { |
| | const option = document.createElement('option'); |
| | option.value = index; |
| | option.textContent = run.run_name; |
| | select.appendChild(option); |
| | }); |
| | } |
| |
|
| | |
| | function createCharts() { |
| | createLossChart(); |
| | createLRChart(); |
| | createEvalChart(); |
| | createCombinedChart(); |
| | } |
| |
|
| | |
| | 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' |
| | } |
| | } |
| | }); |
| | } |
| |
|
| | |
| | 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' |
| | } |
| | } |
| | }); |
| | } |
| |
|
| | |
| | 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' |
| | } |
| | } |
| | }); |
| | } |
| |
|
| | |
| | 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' |
| | } |
| | } |
| | }); |
| | } |
| |
|
| | |
| | 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 }; |
| | } |
| |
|
| | |
| | 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]; |
| | |
| | |
| | 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 |
| | }); |
| | } |
| | |
| | |
| | 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 }; |
| | } |
| |
|
| | |
| | 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(); |
| | } |
| | } |
| |
|
| | |
| | function updateRunSummary() { |
| | const container = document.getElementById('runSummary'); |
| | const selectedRun = document.getElementById('runSelect').value; |
| | const runs = selectedRun === 'all' ? trainingData.runs : [trainingData.runs[selectedRun]]; |
| | |
| | let html = '<div class="run-grid">'; |
| | |
| | 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 += ` |
| | <div class="run-card"> |
| | <h4>${run.run_name}</h4> |
| | <p><strong>Logs:</strong> ${logFiles}</p> |
| | <div class="metric"> |
| | <span>Step Range:</span> |
| | <span class="value">${stepRange}</span> |
| | </div> |
| | <div class="metric"> |
| | <span>Training Points:</span> |
| | <span class="value">${trainingPoints}</span> |
| | </div> |
| | <div class="metric"> |
| | <span>Evaluation Points:</span> |
| | <span class="value">${evalPoints}</span> |
| | </div> |
| | <div class="metric"> |
| | <span>Final Loss:</span> |
| | <span class="value">${finalLoss}</span> |
| | </div> |
| | <div class="metric"> |
| | <span>Final LR:</span> |
| | <span class="value">${finalLR}</span> |
| | </div> |
| | <div class="metric"> |
| | <span>Final Paloma:</span> |
| | <span class="value">${finalPaloma}</span> |
| | </div> |
| | </div> |
| | `; |
| | }); |
| | |
| | html += '</div>'; |
| | container.innerHTML = html; |
| | } |
| |
|
| | |
| | function updateConfigDetails() { |
| | const container = document.getElementById('configDetails'); |
| | const selectedRun = document.getElementById('runSelect').value; |
| | const runs = selectedRun === 'all' ? trainingData.runs : [trainingData.runs[selectedRun]]; |
| | |
| | let html = '<div class="config-grid">'; |
| | |
| | |
| | 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 += ` |
| | <div class="config-item"> |
| | <div class="label">${key.replace(/_/g, ' ').toUpperCase()}</div> |
| | <div class="value">${displayValue}</div> |
| | </div> |
| | `; |
| | }); |
| | |
| | html += '</div>'; |
| | container.innerHTML = html; |
| | } |
| |
|
| | |
| | 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(); |
| | } |
| |
|