Created
February 18, 2025 23:27
-
-
Save jxmorris12/1e234474ee3df6586cca29536534079e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
from flask import Flask, render_template_string | |
app = Flask(__name__) | |
@app.route('/') | |
def dashboard(): | |
return render_template_string(r''' | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Analog Training Dashboard</title> | |
<!-- Import a retro, monospaced font from Google Fonts --> | |
<link href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet"> | |
<!-- Include Chart.js for our charts --> | |
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
<style> | |
/* Global style with larger font and custom wood background */ | |
body { | |
margin: 0; | |
padding: 0; | |
font-family: 'VT323', monospace; | |
font-size: 24px; | |
color: #E0E0E0; | |
background-color: #8B4513; | |
background-image: repeating-linear-gradient( | |
45deg, | |
rgba(255,255,255,0.1), | |
rgba(255,255,255,0.1) 2px, | |
transparent 2px, | |
transparent 10px | |
); | |
background-size: 50px 50px; | |
} | |
/* Dashboard grid: 3 rows, 4 columns */ | |
#dashboard { | |
padding: 20px; | |
display: grid; | |
grid-template-areas: | |
"gpu metrics tokens skills" | |
"loss loss loss loss" | |
"gradient bits dial controls"; | |
grid-template-columns: repeat(4, 1fr); | |
grid-gap: 20px; | |
} | |
.panel { | |
background-color: rgba(30, 30, 30, 0.9); | |
padding: 15px; | |
border: 3px solid #777777; | |
border-radius: 5px; | |
box-shadow: 0 0 10px #222222; | |
} | |
/* Grid area assignments */ | |
.gpu { grid-area: gpu; } | |
.metrics { grid-area: metrics; } | |
.tokens { grid-area: tokens; } | |
.skills { grid-area: skills; } | |
.loss { grid-area: loss; } | |
.gradient { grid-area: gradient; } | |
.bits { grid-area: bits; } | |
.dial { grid-area: dial; } | |
.controls { grid-area: controls; } | |
h2 { | |
margin-top: 0; | |
text-align: center; | |
font-size: 36px; | |
} | |
h3 { | |
margin-top: 0; | |
text-align: center; | |
font-size: 32px; | |
} | |
/* Bold and enlarge metric texts */ | |
.metrics p { | |
font-size: 28px; | |
font-weight: bold; | |
margin: 5px 0; | |
} | |
label { | |
font-size: 28px; | |
font-weight: bold; | |
} | |
canvas { | |
display: block; | |
margin: 0 auto; | |
background-color: #000; | |
} | |
.control { | |
margin: 10px 0; | |
text-align: center; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="dashboard"> | |
<!-- Top Row Panels --> | |
<div class="panel gpu"> | |
<h3>GPU Utilization (1024 GPUs)</h3> | |
<canvas id="gpuChart" width="300" height="200"></canvas> | |
</div> | |
<div class="panel metrics"> | |
<h3>Metrics</h3> | |
<p>Perplexity: <span id="perplexityVal">--</span></p> | |
<p>Per-Token Accuracy: <span id="accuracyVal">--</span></p> | |
<p>Learning Rate: <span id="lrVal">--</span></p> | |
<p>Gradient Norm: <span id="gradNormVal">--</span></p> | |
<p>Bits Memorized: <span id="bitsMemVal">--</span></p> | |
<p>Bits per Second: <span id="bpsVal">--</span></p> | |
</div> | |
<div class="panel tokens"> | |
<h3>Tokens Correct</h3> | |
<canvas id="tokensChart" width="300" height="200"></canvas> | |
</div> | |
<div class="panel skills"> | |
<h3>Skills Growth</h3> | |
<canvas id="skillsChart" width="300" height="200"></canvas> | |
</div> | |
<!-- Middle Row: Main Training Loss Chart --> | |
<div class="panel loss"> | |
<h2>Training Loss</h2> | |
<canvas id="lossChart" width="900" height="300"></canvas> | |
</div> | |
<!-- Bottom Row Panels --> | |
<div class="panel gradient"> | |
<h3>Gradient Flow</h3> | |
<canvas id="gradientFlow" width="300" height="200"></canvas> | |
</div> | |
<div class="panel bits"> | |
<h3>Bits Memorized Over Time</h3> | |
<canvas id="bitsChart" width="300" height="200"></canvas> | |
</div> | |
<div class="panel dial"> | |
<h3>Gradient Norm</h3> | |
<canvas id="gradDial" width="250" height="150"></canvas> | |
</div> | |
<div class="panel controls"> | |
<h3>Controls</h3> | |
<div class="control"> | |
<label for="speedSlider">Simulation Speed:</label> | |
<input type="range" id="speedSlider" min="0.5" max="5" step="0.1" value="1"> | |
</div> | |
<div class="control"> | |
<label for="toggleFlow">Show Gradient Flow:</label> | |
<input type="checkbox" id="toggleFlow" checked> | |
</div> | |
</div> | |
</div> | |
<script> | |
// --- Simulation State Variables --- | |
let iteration = 0; | |
// Initialize 1024 GPUs with low utilizations (0-10%) | |
const gpuUtilizations = new Array(1024).fill(0).map(() => Math.random() * 0.1); | |
// Arrays for charts (now showing all points) | |
let lossData = [], lossLabels = []; | |
let tokensData = [], tokensLabels = []; | |
let bitsData = [], bitsLabels = []; | |
// Skills data (percentages) | |
let skills = { | |
"Translation": 20, | |
"Summarization": 15, | |
"Reasoning": 10, | |
"Coding": 5, | |
"Comprehension": 25 | |
}; | |
// Access simulation controls | |
const speedSlider = document.getElementById('speedSlider'); | |
// --- Main Training Loss Chart (Center) --- | |
const lossChartCtx = document.getElementById('lossChart').getContext('2d'); | |
const lossChart = new Chart(lossChartCtx, { | |
type: 'line', | |
data: { | |
labels: lossLabels, | |
datasets: [{ | |
label: 'Training Loss', | |
data: lossData, | |
borderColor: '#CCCCCC', | |
backgroundColor: 'rgba(204,204,204,0.1)', | |
fill: true, | |
tension: 0.2, | |
pointRadius: 2, | |
}] | |
}, | |
options: { | |
animation: false, | |
responsive: false, | |
scales: { | |
y: { beginAtZero: true, ticks: { color: '#E0E0E0' }, grid: { color: '#444444' } }, | |
x: { ticks: { color: '#E0E0E0' }, grid: { color: '#444444' } } | |
}, | |
plugins: { legend: { labels: { color: '#E0E0E0' } } } | |
} | |
}); | |
// --- Tokens Correct Chart --- | |
const tokensChartCtx = document.getElementById('tokensChart').getContext('2d'); | |
const tokensChart = new Chart(tokensChartCtx, { | |
type: 'line', | |
data: { | |
labels: tokensLabels, | |
datasets: [{ | |
label: 'Tokens Correct (%)', | |
data: tokensData, | |
borderColor: '#CCCCCC', | |
backgroundColor: 'rgba(204,204,204,0.1)', | |
fill: true, | |
tension: 0.2, | |
pointRadius: 2, | |
}] | |
}, | |
options: { | |
animation: false, | |
responsive: false, | |
scales: { | |
y: { | |
beginAtZero: true, | |
max: 1, | |
ticks: { | |
color: '#E0E0E0', | |
callback: value => (value * 100).toFixed(0) + '%' | |
}, | |
grid: { color: '#444444' } | |
}, | |
x: { ticks: { color: '#E0E0E0' }, grid: { color: '#444444' } } | |
}, | |
plugins: { legend: { labels: { color: '#E0E0E0' } } } | |
} | |
}); | |
// --- Bits Memorized Chart --- | |
const bitsChartCtx = document.getElementById('bitsChart').getContext('2d'); | |
const bitsChart = new Chart(bitsChartCtx, { | |
type: 'line', | |
data: { | |
labels: bitsLabels, | |
datasets: [{ | |
label: 'Bits Memorized', | |
data: bitsData, | |
borderColor: '#CCCCCC', | |
backgroundColor: 'rgba(204,204,204,0.1)', | |
fill: true, | |
tension: 0.2, | |
pointRadius: 2, | |
}] | |
}, | |
options: { | |
animation: false, | |
responsive: false, | |
scales: { | |
y: { beginAtZero: true, ticks: { color: '#E0E0E0' }, grid: { color: '#444444' } }, | |
x: { ticks: { color: '#E0E0E0' }, grid: { color: '#444444' } } | |
}, | |
plugins: { legend: { labels: { color: '#E0E0E0' } } } | |
} | |
}); | |
// --- Skills Bar Chart --- | |
const skillsChartCtx = document.getElementById('skillsChart').getContext('2d'); | |
const skillsChart = new Chart(skillsChartCtx, { | |
type: 'bar', | |
data: { | |
labels: Object.keys(skills), | |
datasets: [{ | |
label: 'Skill Level (%)', | |
data: Object.values(skills), | |
backgroundColor: 'rgba(170,170,170,0.8)', | |
borderColor: '#AAAAAA', | |
borderWidth: 2, | |
}] | |
}, | |
options: { | |
scales: { | |
y: { | |
beginAtZero: true, | |
max: 100, | |
ticks: { color: '#E0E0E0', callback: value => value + '%' }, | |
grid: { color: '#444444' } | |
}, | |
x: { ticks: { color: '#E0E0E0' }, grid: { color: '#444444' } } | |
}, | |
plugins: { legend: { labels: { color: '#E0E0E0' } } }, | |
animation: false, | |
responsive: false | |
} | |
}); | |
// --- GPU Utilization Panel --- | |
function updateGPUChart() { | |
const canvas = document.getElementById('gpuChart'); | |
const ctx = canvas.getContext('2d'); | |
const cols = 32, rows = 32; | |
const cellWidth = canvas.width / cols; | |
const cellHeight = canvas.height / rows; | |
for (let i = 0; i < 1024; i++) { | |
// Update each GPU's utilization with a very small random change | |
gpuUtilizations[i] = Math.min(0.3, Math.max(0, gpuUtilizations[i] + (Math.random() - 0.5) * 0.02)); | |
let col = i % cols; | |
let row = Math.floor(i / cols); | |
// Map utilization: low values yield green; nearly all GPUs are low. | |
let util = gpuUtilizations[i]; | |
let r = Math.floor(util * 255); | |
let g = Math.floor((1 - util) * 255); | |
ctx.fillStyle = `rgb(${r},${g},0)`; | |
ctx.fillRect(col * cellWidth, row * cellHeight, cellWidth, cellHeight); | |
} | |
} | |
// --- Update Simulation Metrics --- | |
function updateMetrics() { | |
const speed = parseFloat(speedSlider.value); | |
iteration += speed; | |
// Simulate a realistic negative exponential loss curve: | |
// Loss = plateau + (initial - plateau)*exp(-iteration/scale) + noise | |
// (initial=5.0, plateau=0.5, scale=10) | |
let simulatedLoss = 0.5 + (5.0 - 0.5) * Math.exp(-iteration / 10) + (Math.random() - 0.5) * 0.05; | |
lossData.push(simulatedLoss); | |
lossLabels.push(iteration.toFixed(1)); | |
lossChart.update(); | |
// Other simulated metrics: | |
let perplexity = Math.exp(simulatedLoss); | |
let accuracy = Math.min(1, (1 - simulatedLoss / 5)); | |
let lr = (0.001 * Math.pow(0.99, iteration)).toFixed(6); | |
let gradNorm = (Math.random() * 5).toFixed(2); | |
let bitsMem = (iteration * 0.5).toFixed(2); | |
let bps = (Math.random() * 10).toFixed(2); | |
document.getElementById('perplexityVal').innerText = perplexity.toFixed(2); | |
document.getElementById('accuracyVal').innerText = accuracy.toFixed(2); | |
document.getElementById('lrVal').innerText = lr; | |
document.getElementById('gradNormVal').innerText = gradNorm; | |
document.getElementById('bitsMemVal').innerText = bitsMem; | |
document.getElementById('bpsVal').innerText = bps; | |
// Update Tokens Correct Chart (using the same "accuracy" metric) | |
tokensData.push(accuracy); | |
tokensLabels.push(iteration.toFixed(1)); | |
tokensChart.update(); | |
// Update Bits Memorized Chart | |
bitsData.push(parseFloat(bitsMem)); | |
bitsLabels.push(iteration.toFixed(1)); | |
bitsChart.update(); | |
// Gradually increase each skill value (capped at 100%) | |
for (let key in skills) { | |
skills[key] = Math.min(100, skills[key] + Math.random() * 0.05 * speed); | |
} | |
skillsChart.data.datasets[0].data = Object.values(skills); | |
skillsChart.update(); | |
return parseFloat(gradNorm); | |
} | |
// --- Gradient Flow Animation --- | |
const gradientFlowCanvas = document.getElementById('gradientFlow'); | |
const gradientCtx = gradientFlowCanvas.getContext('2d'); | |
function drawGradientFlow() { | |
if (!document.getElementById('toggleFlow').checked) { | |
gradientCtx.clearRect(0, 0, gradientFlowCanvas.width, gradientFlowCanvas.height); | |
return; | |
} | |
gradientCtx.clearRect(0, 0, gradientFlowCanvas.width, gradientFlowCanvas.height); | |
gradientCtx.strokeStyle = '#AAAAAA'; | |
gradientCtx.lineWidth = 2; | |
gradientCtx.beginPath(); | |
for (let i = 0; i < gradientFlowCanvas.width; i += 10) { | |
let y = 100 + 40 * Math.sin((i + iteration) * 0.05); | |
if (i === 0) { gradientCtx.moveTo(i, y); } | |
else { gradientCtx.lineTo(i, y); } | |
} | |
gradientCtx.stroke(); | |
} | |
// --- Enhanced Dial Drawing (for Gradient Norm) --- | |
// This draws a semi-circular gauge with tick marks, a filled arc, and a value label. | |
function drawDial(canvasId, value, min, max) { | |
const canvas = document.getElementById(canvasId); | |
const ctx = canvas.getContext('2d'); | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
const centerX = canvas.width / 2; | |
const centerY = canvas.height; // bottom center for semicircular gauge | |
const radius = Math.min(centerX, centerY) - 10; | |
// Define gauge angles (in radians) | |
const startAngle = Math.PI; // left (180°) | |
const endAngle = 0; // right (0°) | |
// Map value to an angle along the semicircle | |
let ratio = (value - min) / (max - min); | |
let valueAngle = startAngle + (endAngle - startAngle) * ratio; | |
// Draw gauge background arc | |
ctx.beginPath(); | |
ctx.lineWidth = 15; | |
ctx.strokeStyle = '#444444'; | |
ctx.arc(centerX, centerY, radius, startAngle, endAngle, false); | |
ctx.stroke(); | |
// Draw filled arc (using a blue-to-red gradient) | |
ctx.beginPath(); | |
ctx.lineWidth = 15; | |
let grad = ctx.createLinearGradient(centerX - radius, centerY, centerX + radius, centerY); | |
grad.addColorStop(0, '#00BFFF'); // DeepSkyBlue | |
grad.addColorStop(1, '#FF4500'); // OrangeRed | |
ctx.strokeStyle = grad; | |
ctx.arc(centerX, centerY, radius, startAngle, valueAngle, false); | |
ctx.stroke(); | |
// Draw tick marks and numeric labels | |
ctx.lineWidth = 3; | |
ctx.strokeStyle = '#E0E0E0'; | |
ctx.fillStyle = '#E0E0E0'; | |
ctx.font = '18px VT323'; | |
const numTicks = 6; | |
for (let i = 0; i <= numTicks; i++) { | |
let tickValue = min + i * ((max - min) / numTicks); | |
let tickRatio = (tickValue - min) / (max - min); | |
let tickAngle = startAngle + (endAngle - startAngle) * tickRatio; | |
let innerRadius = radius - 10; | |
let outerRadius = radius + 5; | |
let x1 = centerX + innerRadius * Math.cos(tickAngle); | |
let y1 = centerY + innerRadius * Math.sin(tickAngle); | |
let x2 = centerX + outerRadius * Math.cos(tickAngle); | |
let y2 = centerY + outerRadius * Math.sin(tickAngle); | |
ctx.beginPath(); | |
ctx.moveTo(x1, y1); | |
ctx.lineTo(x2, y2); | |
ctx.stroke(); | |
// Label for the tick mark | |
let labelX = centerX + (outerRadius + 20) * Math.cos(tickAngle); | |
let labelY = centerY + (outerRadius + 20) * Math.sin(tickAngle) + 6; | |
ctx.fillText(tickValue.toFixed(1), labelX, labelY); | |
} | |
// Draw the current gradient norm value in the center of the gauge | |
ctx.fillStyle = '#E0E0E0'; | |
ctx.font = '28px VT323'; | |
ctx.textAlign = 'center'; | |
ctx.fillText(value.toFixed(2), centerX, centerY - radius/2); | |
} | |
// --- Main Update Loop --- | |
function update() { | |
const gradNorm = updateMetrics(); | |
drawGradientFlow(); | |
drawDial('gradDial', gradNorm, 0, 5); | |
updateGPUChart(); | |
requestAnimationFrame(update); | |
} | |
update(); | |
</script> | |
</body> | |
</html> | |
''') | |
if __name__ == '__main__': | |
# Run the dashboard on http://localhost:5000 | |
app.run(debug=True) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment