Spaces:
Running
Running
Update index.html
Browse files- index.html +889 -37
index.html
CHANGED
@@ -630,30 +630,31 @@
|
|
630 |
left: 0;
|
631 |
width: 100%;
|
632 |
height: 100%;
|
633 |
-
background: rgba(0, 0, 0, 0.
|
634 |
z-index: 9999;
|
635 |
display: none;
|
636 |
-
backdrop-filter: blur(
|
637 |
}
|
638 |
|
639 |
.walkthrough-highlight {
|
640 |
position: absolute;
|
641 |
-
border:
|
642 |
border-radius: 0.5rem;
|
643 |
-
box-shadow: 0 0 0
|
644 |
pointer-events: none;
|
645 |
animation: pulse 2s infinite;
|
646 |
z-index: 10000;
|
|
|
647 |
}
|
648 |
|
649 |
.walkthrough-popup {
|
650 |
position: absolute;
|
651 |
background: #1f2937;
|
652 |
-
border:
|
653 |
border-radius: 1rem;
|
654 |
padding: 1.5rem;
|
655 |
-
max-width:
|
656 |
-
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.
|
657 |
z-index: 10001;
|
658 |
color: white;
|
659 |
}
|
@@ -773,7 +774,7 @@
|
|
773 |
}
|
774 |
|
775 |
.walkthrough-task-menu {
|
776 |
-
display:
|
777 |
padding: 2rem;
|
778 |
max-width: 800px;
|
779 |
margin: 0 auto;
|
@@ -821,6 +822,119 @@
|
|
821 |
display: none;
|
822 |
}
|
823 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
824 |
/* Mobile-friendly adjustments */
|
825 |
@media (max-width: 640px) {
|
826 |
.walkthrough-popup {
|
@@ -846,6 +960,15 @@
|
|
846 |
font-size: 0.8125rem;
|
847 |
padding: 0.4rem 0.8rem;
|
848 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
849 |
}
|
850 |
</style>
|
851 |
</head>
|
@@ -956,25 +1079,48 @@
|
|
956 |
<p class="task-subtitle">Learn step-by-step how neural networks work</p>
|
957 |
</div>
|
958 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
959 |
<div class="walkthrough-task-menu">
|
960 |
<div class="walkthrough-task-card" onclick="startWalkthrough('basics')">
|
961 |
<h3 class="walkthrough-task-title">🧠 Neural Network Basics</h3>
|
962 |
<p class="walkthrough-task-description">Learn what neurons, layers, and connections are. Understand how information flows through the network.</p>
|
|
|
963 |
</div>
|
964 |
|
965 |
<div class="walkthrough-task-card" onclick="startWalkthrough('training')">
|
966 |
<h3 class="walkthrough-task-title">🎯 How Training Works</h3>
|
967 |
<p class="walkthrough-task-description">Discover how neural networks learn from data through forward propagation, loss calculation, and backpropagation.</p>
|
|
|
968 |
</div>
|
969 |
|
970 |
<div class="walkthrough-task-card" onclick="startWalkthrough('visualization')">
|
971 |
<h3 class="walkthrough-task-title">📊 Understanding the Visualizations</h3>
|
972 |
<p class="walkthrough-task-description">Learn to read the network diagram, loss chart, and output predictions to understand what's happening.</p>
|
|
|
973 |
</div>
|
974 |
|
975 |
<div class="walkthrough-task-card" onclick="startWalkthrough('logic')">
|
976 |
<h3 class="walkthrough-task-title">🔗 Logic Gates Tutorial</h3>
|
977 |
<p class="walkthrough-task-description">See how neural networks can learn simple AND, OR, and complex XOR logic gates step by step.</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
978 |
</div>
|
979 |
</div>
|
980 |
</div>
|
@@ -1097,6 +1243,48 @@
|
|
1097 |
</div>
|
1098 |
</div>
|
1099 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1100 |
<div class="card">
|
1101 |
<h3 class="card-title">
|
1102 |
<svg class="icon" fill="currentColor" viewBox="0 0 24 24">
|
@@ -1694,6 +1882,21 @@
|
|
1694 |
let trainInterval = null;
|
1695 |
let animationId = null;
|
1696 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1697 |
// DOM elements
|
1698 |
const mainMenu = document.getElementById('mainMenu');
|
1699 |
const taskSelection = document.getElementById('taskSelection');
|
@@ -1725,6 +1928,15 @@
|
|
1725 |
const vizTitle = document.getElementById('vizTitle');
|
1726 |
const babyViz = document.getElementById('babyViz');
|
1727 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1728 |
// Developer mode elements
|
1729 |
const devTaskName = document.getElementById('devTaskName');
|
1730 |
const devArchitecture = document.getElementById('devArchitecture');
|
@@ -2067,19 +2279,63 @@
|
|
2067 |
ctx.lineTo(x2, y2);
|
2068 |
ctx.stroke();
|
2069 |
|
2070 |
-
//
|
2071 |
-
if (isTraining && Math.abs(weight) > 0.
|
2072 |
-
|
2073 |
-
const
|
2074 |
-
const flowY = y1 + (y2 - y1) * flowProgress;
|
2075 |
|
2076 |
-
|
2077 |
-
|
2078 |
-
|
2079 |
-
|
2080 |
-
|
2081 |
-
|
2082 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2083 |
}
|
2084 |
}
|
2085 |
}
|
@@ -2097,9 +2353,47 @@
|
|
2097 |
const saturation = 50;
|
2098 |
let lightness = 35 + activation * 25;
|
2099 |
|
2100 |
-
|
2101 |
-
|
2102 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2103 |
}
|
2104 |
|
2105 |
ctx.fillStyle = `hsl(${hue}, ${saturation}%, ${lightness}%)`;
|
@@ -2111,13 +2405,36 @@
|
|
2111 |
ctx.lineWidth = 1.5;
|
2112 |
ctx.stroke();
|
2113 |
|
|
|
2114 |
ctx.fillStyle = '#ffffff';
|
2115 |
-
ctx.font = `${Math.max(
|
2116 |
ctx.textAlign = 'center';
|
2117 |
ctx.textBaseline = 'middle';
|
2118 |
ctx.shadowColor = 'rgba(0, 0, 0, 0.9)';
|
2119 |
ctx.shadowBlur = 2;
|
2120 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2121 |
ctx.shadowBlur = 0;
|
2122 |
}
|
2123 |
});
|
@@ -2202,6 +2519,310 @@
|
|
2202 |
lossArea.setAttribute('points', points + ' 100,100 0,100');
|
2203 |
}
|
2204 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2205 |
// Training step
|
2206 |
function trainStep() {
|
2207 |
const result = network.trainBatch(currentTask.data);
|
@@ -2216,6 +2837,21 @@
|
|
2216 |
lossHistory.push(result.loss);
|
2217 |
if (lossHistory.length > 100) lossHistory.shift();
|
2218 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2219 |
const newPredictions = currentTask.data.map(data => {
|
2220 |
const output = network.forward(data.input);
|
2221 |
const rawOutput = output[0];
|
@@ -2279,6 +2915,7 @@
|
|
2279 |
|
2280 |
updateLossChart();
|
2281 |
drawNetwork();
|
|
|
2282 |
if (currentTask.hasVisualization) {
|
2283 |
drawDataVisualization();
|
2284 |
}
|
@@ -2315,6 +2952,7 @@
|
|
2315 |
if (currentTask.isBabyMode) {
|
2316 |
updateBabyVisualization();
|
2317 |
}
|
|
|
2318 |
}
|
2319 |
animationId = requestAnimationFrame(animate);
|
2320 |
}
|
@@ -2344,6 +2982,12 @@
|
|
2344 |
accuracy = 0;
|
2345 |
animationTime = 0;
|
2346 |
|
|
|
|
|
|
|
|
|
|
|
|
|
2347 |
trainBtn.innerHTML = `
|
2348 |
<svg class="icon" fill="currentColor" viewBox="0 0 24 24">
|
2349 |
<path d="M8 5v14l11-7z"/>
|
@@ -2368,8 +3012,7 @@
|
|
2368 |
`;
|
2369 |
trainBtn.className = 'btn btn-pause';
|
2370 |
|
2371 |
-
|
2372 |
-
const trainingSpeed = walkthroughActive ? walkthroughTrainingSpeed : 100;
|
2373 |
trainInterval = setInterval(trainStep, trainingSpeed);
|
2374 |
} else {
|
2375 |
trainBtn.innerHTML = `
|
@@ -2390,6 +3033,79 @@
|
|
2390 |
// Developer mode event listeners
|
2391 |
devArchitecture.addEventListener('input', updateParameterCount);
|
2392 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2393 |
// Walkthrough Mode functionality
|
2394 |
let walkthroughActive = false;
|
2395 |
let walkthroughStep = 0;
|
@@ -2411,21 +3127,21 @@
|
|
2411 |
content: 'This is the input layer (left side). It receives the raw data - like numbers, images, or text. Each circle represents one input neuron that holds a piece of information.',
|
2412 |
element: '#networkCanvas',
|
2413 |
position: 'right',
|
2414 |
-
highlight: {x: 0, y: 0, width:
|
2415 |
},
|
2416 |
{
|
2417 |
title: 'Hidden Layers',
|
2418 |
content: 'These middle layers are where the "magic" happens! They transform the input data through mathematical operations, finding patterns and relationships.',
|
2419 |
element: '#networkCanvas',
|
2420 |
position: 'top',
|
2421 |
-
highlight: {x:
|
2422 |
},
|
2423 |
{
|
2424 |
title: 'Output Layer',
|
2425 |
content: 'The final layer gives us the result - a prediction, classification, or decision based on what the network learned from the input.',
|
2426 |
element: '#networkCanvas',
|
2427 |
position: 'left',
|
2428 |
-
highlight: {x:
|
2429 |
},
|
2430 |
{
|
2431 |
title: 'Connections (Weights)',
|
@@ -2546,6 +3262,94 @@
|
|
2546 |
position: 'right'
|
2547 |
}
|
2548 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2549 |
}
|
2550 |
};
|
2551 |
|
@@ -2563,6 +3367,20 @@
|
|
2563 |
taskSelection.style.display = 'block';
|
2564 |
currentCategory = 'fundamentals';
|
2565 |
showCategory('fundamentals');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2566 |
}
|
2567 |
|
2568 |
showWalkthroughStep();
|
@@ -2579,9 +3397,13 @@
|
|
2579 |
document.getElementById('walkthroughStep').textContent = walkthroughStep + 1;
|
2580 |
document.getElementById('walkthroughTotal').textContent = walkthroughTutorial.steps.length;
|
2581 |
|
2582 |
-
// Show overlay
|
2583 |
overlay.style.display = 'block';
|
2584 |
progress.style.display = 'block';
|
|
|
|
|
|
|
|
|
2585 |
|
2586 |
// Update popup content
|
2587 |
document.getElementById('walkthroughTitle').textContent = step.title;
|
@@ -2593,7 +3415,7 @@
|
|
2593 |
if (element) {
|
2594 |
const rect = element.getBoundingClientRect();
|
2595 |
|
2596 |
-
// Highlight element
|
2597 |
if (step.highlight) {
|
2598 |
const canvasRect = element.getBoundingClientRect();
|
2599 |
highlight.style.left = (canvasRect.left + step.highlight.x) + 'px';
|
@@ -2601,12 +3423,18 @@
|
|
2601 |
highlight.style.width = step.highlight.width + 'px';
|
2602 |
highlight.style.height = step.highlight.height + 'px';
|
2603 |
} else {
|
2604 |
-
highlight.style.left = rect.left -
|
2605 |
-
highlight.style.top = rect.top -
|
2606 |
-
highlight.style.width = rect.width +
|
2607 |
-
highlight.style.height = rect.height +
|
2608 |
}
|
2609 |
highlight.style.display = 'block';
|
|
|
|
|
|
|
|
|
|
|
|
|
2610 |
|
2611 |
// Position popup
|
2612 |
positionPopup(popup, rect, step.position);
|
@@ -2695,6 +3523,20 @@
|
|
2695 |
document.getElementById('walkthroughPopup').style.display = 'none';
|
2696 |
document.getElementById('walkthroughProgress').style.display = 'none';
|
2697 |
document.getElementById('walkthroughIndicator').style.display = 'none';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2698 |
}
|
2699 |
|
2700 |
// Walkthrough event listeners
|
@@ -2704,7 +3546,17 @@
|
|
2704 |
|
2705 |
// Initialize
|
2706 |
startAnimation();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2707 |
</script>
|
2708 |
-
<script src="walkthrough-enhancement.js"></script>
|
2709 |
</body>
|
2710 |
</html>
|
|
|
630 |
left: 0;
|
631 |
width: 100%;
|
632 |
height: 100%;
|
633 |
+
background: rgba(0, 0, 0, 0.3);
|
634 |
z-index: 9999;
|
635 |
display: none;
|
636 |
+
backdrop-filter: blur(2px);
|
637 |
}
|
638 |
|
639 |
.walkthrough-highlight {
|
640 |
position: absolute;
|
641 |
+
border: 4px solid #10b981;
|
642 |
border-radius: 0.5rem;
|
643 |
+
box-shadow: 0 0 0 8px rgba(16, 185, 129, 0.3), 0 0 40px rgba(16, 185, 129, 0.6);
|
644 |
pointer-events: none;
|
645 |
animation: pulse 2s infinite;
|
646 |
z-index: 10000;
|
647 |
+
background: rgba(16, 185, 129, 0.1);
|
648 |
}
|
649 |
|
650 |
.walkthrough-popup {
|
651 |
position: absolute;
|
652 |
background: #1f2937;
|
653 |
+
border: 3px solid #10b981;
|
654 |
border-radius: 1rem;
|
655 |
padding: 1.5rem;
|
656 |
+
max-width: 400px;
|
657 |
+
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.8), 0 0 0 2px rgba(16, 185, 129, 0.2);
|
658 |
z-index: 10001;
|
659 |
color: white;
|
660 |
}
|
|
|
774 |
}
|
775 |
|
776 |
.walkthrough-task-menu {
|
777 |
+
display: block;
|
778 |
padding: 2rem;
|
779 |
max-width: 800px;
|
780 |
margin: 0 auto;
|
|
|
822 |
display: none;
|
823 |
}
|
824 |
|
825 |
+
/* Loss Landscape Visualization Styles */
|
826 |
+
.loss-landscape-container {
|
827 |
+
background: #1f2937;
|
828 |
+
border: 1px solid #374151;
|
829 |
+
border-radius: 0.5rem;
|
830 |
+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
831 |
+
padding: 1.5rem;
|
832 |
+
margin-bottom: 1.5rem;
|
833 |
+
}
|
834 |
+
|
835 |
+
.loss-landscape-title {
|
836 |
+
font-size: 1.125rem;
|
837 |
+
font-weight: 600;
|
838 |
+
margin-bottom: 1rem;
|
839 |
+
color: white;
|
840 |
+
display: flex;
|
841 |
+
align-items: center;
|
842 |
+
gap: 0.5rem;
|
843 |
+
}
|
844 |
+
|
845 |
+
.loss-landscape-canvas {
|
846 |
+
width: 100%;
|
847 |
+
height: 400px;
|
848 |
+
border: 1px solid #4b5563;
|
849 |
+
border-radius: 0.5rem;
|
850 |
+
background: #111827;
|
851 |
+
cursor: grab;
|
852 |
+
}
|
853 |
+
|
854 |
+
.loss-landscape-canvas:active {
|
855 |
+
cursor: grabbing;
|
856 |
+
}
|
857 |
+
|
858 |
+
.landscape-controls {
|
859 |
+
display: flex;
|
860 |
+
flex-wrap: wrap;
|
861 |
+
gap: 1rem;
|
862 |
+
margin-top: 1rem;
|
863 |
+
align-items: center;
|
864 |
+
}
|
865 |
+
|
866 |
+
.landscape-control-group {
|
867 |
+
display: flex;
|
868 |
+
flex-direction: column;
|
869 |
+
gap: 0.25rem;
|
870 |
+
}
|
871 |
+
|
872 |
+
.landscape-control-label {
|
873 |
+
font-size: 0.875rem;
|
874 |
+
color: #9ca3af;
|
875 |
+
font-weight: 500;
|
876 |
+
}
|
877 |
+
|
878 |
+
.landscape-slider {
|
879 |
+
width: 100px;
|
880 |
+
height: 6px;
|
881 |
+
border-radius: 3px;
|
882 |
+
background: #4b5563;
|
883 |
+
outline: none;
|
884 |
+
cursor: pointer;
|
885 |
+
}
|
886 |
+
|
887 |
+
.landscape-toggle {
|
888 |
+
display: flex;
|
889 |
+
align-items: center;
|
890 |
+
gap: 0.5rem;
|
891 |
+
font-size: 0.875rem;
|
892 |
+
color: #d1d5db;
|
893 |
+
}
|
894 |
+
|
895 |
+
.landscape-checkbox {
|
896 |
+
width: 16px;
|
897 |
+
height: 16px;
|
898 |
+
border-radius: 3px;
|
899 |
+
border: 2px solid #4b5563;
|
900 |
+
background: #111827;
|
901 |
+
cursor: pointer;
|
902 |
+
}
|
903 |
+
|
904 |
+
.landscape-checkbox:checked {
|
905 |
+
background: #06b6d4;
|
906 |
+
border-color: #06b6d4;
|
907 |
+
}
|
908 |
+
|
909 |
+
.gradient-legend {
|
910 |
+
display: flex;
|
911 |
+
align-items: center;
|
912 |
+
gap: 1rem;
|
913 |
+
margin-top: 0.5rem;
|
914 |
+
font-size: 0.75rem;
|
915 |
+
color: #9ca3af;
|
916 |
+
}
|
917 |
+
|
918 |
+
.gradient-arrow {
|
919 |
+
display: inline-block;
|
920 |
+
width: 20px;
|
921 |
+
height: 3px;
|
922 |
+
background: linear-gradient(90deg, #ef4444, #f59e0b);
|
923 |
+
position: relative;
|
924 |
+
}
|
925 |
+
|
926 |
+
.gradient-arrow::after {
|
927 |
+
content: '';
|
928 |
+
position: absolute;
|
929 |
+
right: -3px;
|
930 |
+
top: -2px;
|
931 |
+
width: 0;
|
932 |
+
height: 0;
|
933 |
+
border-left: 5px solid #f59e0b;
|
934 |
+
border-top: 4px solid transparent;
|
935 |
+
border-bottom: 4px solid transparent;
|
936 |
+
}
|
937 |
+
|
938 |
/* Mobile-friendly adjustments */
|
939 |
@media (max-width: 640px) {
|
940 |
.walkthrough-popup {
|
|
|
960 |
font-size: 0.8125rem;
|
961 |
padding: 0.4rem 0.8rem;
|
962 |
}
|
963 |
+
|
964 |
+
.loss-landscape-canvas {
|
965 |
+
height: 300px;
|
966 |
+
}
|
967 |
+
|
968 |
+
.landscape-controls {
|
969 |
+
flex-direction: column;
|
970 |
+
align-items: flex-start;
|
971 |
+
}
|
972 |
}
|
973 |
</style>
|
974 |
</head>
|
|
|
1079 |
<p class="task-subtitle">Learn step-by-step how neural networks work</p>
|
1080 |
</div>
|
1081 |
|
1082 |
+
<div class="walkthrough-intro">
|
1083 |
+
<div style="text-align: center; margin-bottom: 2rem; padding: 1.5rem; background: linear-gradient(135deg, #10b981, #059669); border-radius: 1rem; color: white;">
|
1084 |
+
<h3 style="margin: 0 0 0.5rem 0; font-size: 1.5rem;">🎓 Interactive AI Learning Journey</h3>
|
1085 |
+
<p style="margin: 0; opacity: 0.9;">Choose a tutorial below to start your guided exploration of neural networks. Each tutorial includes interactive demonstrations and step-by-step explanations.</p>
|
1086 |
+
</div>
|
1087 |
+
</div>
|
1088 |
+
|
1089 |
<div class="walkthrough-task-menu">
|
1090 |
<div class="walkthrough-task-card" onclick="startWalkthrough('basics')">
|
1091 |
<h3 class="walkthrough-task-title">🧠 Neural Network Basics</h3>
|
1092 |
<p class="walkthrough-task-description">Learn what neurons, layers, and connections are. Understand how information flows through the network.</p>
|
1093 |
+
<div style="font-size: 0.75rem; color: #9ca3af; margin-top: 0.5rem;">⏱️ 5 steps • 🎯 Beginner</div>
|
1094 |
</div>
|
1095 |
|
1096 |
<div class="walkthrough-task-card" onclick="startWalkthrough('training')">
|
1097 |
<h3 class="walkthrough-task-title">🎯 How Training Works</h3>
|
1098 |
<p class="walkthrough-task-description">Discover how neural networks learn from data through forward propagation, loss calculation, and backpropagation.</p>
|
1099 |
+
<div style="font-size: 0.75rem; color: #9ca3af; margin-top: 0.5rem;">⏱️ 5 steps • 🎯 Beginner</div>
|
1100 |
</div>
|
1101 |
|
1102 |
<div class="walkthrough-task-card" onclick="startWalkthrough('visualization')">
|
1103 |
<h3 class="walkthrough-task-title">📊 Understanding the Visualizations</h3>
|
1104 |
<p class="walkthrough-task-description">Learn to read the network diagram, loss chart, and output predictions to understand what's happening.</p>
|
1105 |
+
<div style="font-size: 0.75rem; color: #9ca3af; margin-top: 0.5rem;">⏱️ 4 steps • 🎯 Beginner</div>
|
1106 |
</div>
|
1107 |
|
1108 |
<div class="walkthrough-task-card" onclick="startWalkthrough('logic')">
|
1109 |
<h3 class="walkthrough-task-title">🔗 Logic Gates Tutorial</h3>
|
1110 |
<p class="walkthrough-task-description">See how neural networks can learn simple AND, OR, and complex XOR logic gates step by step.</p>
|
1111 |
+
<div style="font-size: 0.75rem; color: #9ca3af; margin-top: 0.5rem;">⏱️ 5 steps • 📈 Intermediate</div>
|
1112 |
+
</div>
|
1113 |
+
|
1114 |
+
<div class="walkthrough-task-card" onclick="startWalkthrough('landscape')">
|
1115 |
+
<h3 class="walkthrough-task-title">🗻 Loss Landscape & Gradients</h3>
|
1116 |
+
<p class="walkthrough-task-description">Explore the 3D loss landscape, understand gradients, and see how training navigates through parameter space.</p>
|
1117 |
+
<div style="font-size: 0.75rem; color: #9ca3af; margin-top: 0.5rem;">⏱️ 6 steps • 🔥 Advanced</div>
|
1118 |
+
</div>
|
1119 |
+
|
1120 |
+
<div class="walkthrough-task-card" onclick="startWalkthrough('advanced')">
|
1121 |
+
<h3 class="walkthrough-task-title">⚡ Advanced Visualizations</h3>
|
1122 |
+
<p class="walkthrough-task-description">Master gradient flows, weight changes, and understand what makes neural networks tick under the hood.</p>
|
1123 |
+
<div style="font-size: 0.75rem; color: #9ca3af; margin-top: 0.5rem;">⏱️ 7 steps • 🔥 Advanced</div>
|
1124 |
</div>
|
1125 |
</div>
|
1126 |
</div>
|
|
|
1243 |
</div>
|
1244 |
</div>
|
1245 |
|
1246 |
+
<!-- Loss Landscape Visualization -->
|
1247 |
+
<div class="loss-landscape-container">
|
1248 |
+
<h3 class="loss-landscape-title">
|
1249 |
+
<svg class="icon icon-stroke" viewBox="0 0 24 24">
|
1250 |
+
<path d="M8 3l4 8 5-5 5 15H2L8 3z"/>
|
1251 |
+
</svg>
|
1252 |
+
Loss Landscape & Gradients
|
1253 |
+
</h3>
|
1254 |
+
<canvas id="lossLandscapeCanvas" class="loss-landscape-canvas" width="800" height="400"></canvas>
|
1255 |
+
<div class="landscape-controls">
|
1256 |
+
<div class="landscape-control-group">
|
1257 |
+
<label class="landscape-control-label">Rotation X</label>
|
1258 |
+
<input type="range" id="rotationX" class="landscape-slider" min="-90" max="90" value="-20">
|
1259 |
+
</div>
|
1260 |
+
<div class="landscape-control-group">
|
1261 |
+
<label class="landscape-control-label">Rotation Z</label>
|
1262 |
+
<input type="range" id="rotationZ" class="landscape-slider" min="0" max="360" value="45">
|
1263 |
+
</div>
|
1264 |
+
<div class="landscape-control-group">
|
1265 |
+
<label class="landscape-control-label">Zoom</label>
|
1266 |
+
<input type="range" id="zoomLevel" class="landscape-slider" min="0.5" max="3" step="0.1" value="1">
|
1267 |
+
</div>
|
1268 |
+
<div class="landscape-toggle">
|
1269 |
+
<input type="checkbox" id="showGradients" class="landscape-checkbox" checked>
|
1270 |
+
<label for="showGradients">Show Gradients</label>
|
1271 |
+
</div>
|
1272 |
+
<div class="landscape-toggle">
|
1273 |
+
<input type="checkbox" id="showContours" class="landscape-checkbox" checked>
|
1274 |
+
<label for="showContours">Show Contours</label>
|
1275 |
+
</div>
|
1276 |
+
<div class="landscape-toggle">
|
1277 |
+
<input type="checkbox" id="showPath" class="landscape-checkbox" checked>
|
1278 |
+
<label for="showPath">Show Training Path</label>
|
1279 |
+
</div>
|
1280 |
+
</div>
|
1281 |
+
<div class="gradient-legend">
|
1282 |
+
<span>Gradient Direction:</span>
|
1283 |
+
<span class="gradient-arrow"></span>
|
1284 |
+
<span>High → Low Loss</span>
|
1285 |
+
</div>
|
1286 |
+
</div>
|
1287 |
+
|
1288 |
<div class="card">
|
1289 |
<h3 class="card-title">
|
1290 |
<svg class="icon" fill="currentColor" viewBox="0 0 24 24">
|
|
|
1882 |
let trainInterval = null;
|
1883 |
let animationId = null;
|
1884 |
|
1885 |
+
// Loss landscape variables
|
1886 |
+
let lossLandscape = [];
|
1887 |
+
let gradientField = [];
|
1888 |
+
let trainingPath = [];
|
1889 |
+
let landscapeResolution = 30;
|
1890 |
+
let rotationX = -20;
|
1891 |
+
let rotationZ = 45;
|
1892 |
+
let zoomLevel = 1;
|
1893 |
+
let showGradients = true;
|
1894 |
+
let showContours = true;
|
1895 |
+
let showPath = true;
|
1896 |
+
let mouseDown = false;
|
1897 |
+
let lastMouseX = 0;
|
1898 |
+
let lastMouseY = 0;
|
1899 |
+
|
1900 |
// DOM elements
|
1901 |
const mainMenu = document.getElementById('mainMenu');
|
1902 |
const taskSelection = document.getElementById('taskSelection');
|
|
|
1928 |
const vizTitle = document.getElementById('vizTitle');
|
1929 |
const babyViz = document.getElementById('babyViz');
|
1930 |
|
1931 |
+
// Loss landscape elements
|
1932 |
+
const lossLandscapeCanvas = document.getElementById('lossLandscapeCanvas');
|
1933 |
+
const rotationXSlider = document.getElementById('rotationX');
|
1934 |
+
const rotationZSlider = document.getElementById('rotationZ');
|
1935 |
+
const zoomSlider = document.getElementById('zoomLevel');
|
1936 |
+
const gradientsCheckbox = document.getElementById('showGradients');
|
1937 |
+
const contoursCheckbox = document.getElementById('showContours');
|
1938 |
+
const pathCheckbox = document.getElementById('showPath');
|
1939 |
+
|
1940 |
// Developer mode elements
|
1941 |
const devTaskName = document.getElementById('devTaskName');
|
1942 |
const devArchitecture = document.getElementById('devArchitecture');
|
|
|
2279 |
ctx.lineTo(x2, y2);
|
2280 |
ctx.stroke();
|
2281 |
|
2282 |
+
// Enhanced gradient flow visualization
|
2283 |
+
if (isTraining && Math.abs(weight) > 0.1) {
|
2284 |
+
// Multiple flowing particles for stronger gradients
|
2285 |
+
const numParticles = Math.min(3, Math.floor(Math.abs(weight) * 4));
|
|
|
2286 |
|
2287 |
+
for (let p = 0; p < numParticles; p++) {
|
2288 |
+
const offset = (p / numParticles) * 120;
|
2289 |
+
const flowProgress = ((animationTime * 0.8 + offset) % 120) / 120;
|
2290 |
+
const flowX = x1 + (x2 - x1) * flowProgress;
|
2291 |
+
const flowY = y1 + (y2 - y1) * flowProgress;
|
2292 |
+
|
2293 |
+
const particleSize = 2 + Math.abs(weight) * 2;
|
2294 |
+
const flowIntensity = intensity * 0.4;
|
2295 |
+
|
2296 |
+
// Gradient direction indicator
|
2297 |
+
const gradientStrength = weightChanges && weightChanges[i] && weightChanges[i][k] && weightChanges[i][k][j]
|
2298 |
+
? Math.min(weightChanges[i][k][j] * 5, 1) : 0;
|
2299 |
+
|
2300 |
+
ctx.fillStyle = weight > 0 ?
|
2301 |
+
`rgba(34, 197, 94, ${flowIntensity + gradientStrength * 0.3})` :
|
2302 |
+
`rgba(239, 68, 68, ${flowIntensity + gradientStrength * 0.3})`;
|
2303 |
+
|
2304 |
+
ctx.beginPath();
|
2305 |
+
ctx.arc(flowX, flowY, particleSize, 0, 2 * Math.PI);
|
2306 |
+
ctx.fill();
|
2307 |
+
|
2308 |
+
// Add gradient direction arrow for strong changes
|
2309 |
+
if (gradientStrength > 0.5) {
|
2310 |
+
const arrowLength = 8;
|
2311 |
+
const angle = Math.atan2(y2 - y1, x2 - x1);
|
2312 |
+
|
2313 |
+
ctx.strokeStyle = weight > 0 ?
|
2314 |
+
`rgba(34, 197, 94, ${gradientStrength})` :
|
2315 |
+
`rgba(239, 68, 68, ${gradientStrength})`;
|
2316 |
+
ctx.lineWidth = 2;
|
2317 |
+
|
2318 |
+
ctx.beginPath();
|
2319 |
+
ctx.moveTo(flowX - arrowLength * Math.cos(angle),
|
2320 |
+
flowY - arrowLength * Math.sin(angle));
|
2321 |
+
ctx.lineTo(flowX + arrowLength * Math.cos(angle),
|
2322 |
+
flowY + arrowLength * Math.sin(angle));
|
2323 |
+
ctx.stroke();
|
2324 |
+
|
2325 |
+
// Arrow head
|
2326 |
+
const headSize = 3;
|
2327 |
+
ctx.beginPath();
|
2328 |
+
ctx.moveTo(flowX + arrowLength * Math.cos(angle),
|
2329 |
+
flowY + arrowLength * Math.sin(angle));
|
2330 |
+
ctx.lineTo(flowX + (arrowLength - headSize) * Math.cos(angle - 0.3),
|
2331 |
+
flowY + (arrowLength - headSize) * Math.sin(angle - 0.3));
|
2332 |
+
ctx.moveTo(flowX + arrowLength * Math.cos(angle),
|
2333 |
+
flowY + arrowLength * Math.sin(angle));
|
2334 |
+
ctx.lineTo(flowX + (arrowLength - headSize) * Math.cos(angle + 0.3),
|
2335 |
+
flowY + (arrowLength - headSize) * Math.sin(angle + 0.3));
|
2336 |
+
ctx.stroke();
|
2337 |
+
}
|
2338 |
+
}
|
2339 |
}
|
2340 |
}
|
2341 |
}
|
|
|
2353 |
const saturation = 50;
|
2354 |
let lightness = 35 + activation * 25;
|
2355 |
|
2356 |
+
// Enhanced neuron visualization with gradient information
|
2357 |
+
if (isTraining) {
|
2358 |
+
if (activation > 0.8) {
|
2359 |
+
const pulse = Math.sin(animationTime * 0.05) * 0.1;
|
2360 |
+
lightness += pulse * 10;
|
2361 |
+
}
|
2362 |
+
|
2363 |
+
// Show gradient magnitude on neurons
|
2364 |
+
if (weightChanges && weightChanges.length > 0) {
|
2365 |
+
let neuronGradient = 0;
|
2366 |
+
|
2367 |
+
// Calculate average gradient affecting this neuron
|
2368 |
+
if (layerIndex < network.layers.length - 1 && weightChanges[layerIndex]) {
|
2369 |
+
for (let outIdx = 0; outIdx < weightChanges[layerIndex].length; outIdx++) {
|
2370 |
+
if (weightChanges[layerIndex][outIdx] && weightChanges[layerIndex][outIdx][nodeIndex]) {
|
2371 |
+
neuronGradient += Math.abs(weightChanges[layerIndex][outIdx][nodeIndex]);
|
2372 |
+
}
|
2373 |
+
}
|
2374 |
+
neuronGradient /= weightChanges[layerIndex].length || 1;
|
2375 |
+
}
|
2376 |
+
|
2377 |
+
// Add gradient ring around active neurons
|
2378 |
+
if (neuronGradient > 0.1) {
|
2379 |
+
const ringIntensity = Math.min(neuronGradient * 3, 1);
|
2380 |
+
ctx.strokeStyle = `rgba(255, 255, 0, ${ringIntensity * 0.7})`;
|
2381 |
+
ctx.lineWidth = 3;
|
2382 |
+
ctx.beginPath();
|
2383 |
+
ctx.arc(x, y, nodeRadius + 2, 0, 2 * Math.PI);
|
2384 |
+
ctx.stroke();
|
2385 |
+
|
2386 |
+
// Pulsing effect for high gradients
|
2387 |
+
if (neuronGradient > 0.5) {
|
2388 |
+
const gradientPulse = Math.sin(animationTime * 0.1) * 0.2 + 0.8;
|
2389 |
+
ctx.strokeStyle = `rgba(255, 255, 0, ${ringIntensity * gradientPulse * 0.3})`;
|
2390 |
+
ctx.lineWidth = 5;
|
2391 |
+
ctx.beginPath();
|
2392 |
+
ctx.arc(x, y, nodeRadius + 4, 0, 2 * Math.PI);
|
2393 |
+
ctx.stroke();
|
2394 |
+
}
|
2395 |
+
}
|
2396 |
+
}
|
2397 |
}
|
2398 |
|
2399 |
ctx.fillStyle = `hsl(${hue}, ${saturation}%, ${lightness}%)`;
|
|
|
2405 |
ctx.lineWidth = 1.5;
|
2406 |
ctx.stroke();
|
2407 |
|
2408 |
+
// Enhanced text with gradient information
|
2409 |
ctx.fillStyle = '#ffffff';
|
2410 |
+
ctx.font = `${Math.max(8, nodeRadius / 2)}px monospace`;
|
2411 |
ctx.textAlign = 'center';
|
2412 |
ctx.textBaseline = 'middle';
|
2413 |
ctx.shadowColor = 'rgba(0, 0, 0, 0.9)';
|
2414 |
ctx.shadowBlur = 2;
|
2415 |
+
|
2416 |
+
// Show activation value
|
2417 |
+
ctx.fillText(activation.toFixed(2), x, y - 2);
|
2418 |
+
|
2419 |
+
// Show gradient information for training neurons
|
2420 |
+
if (isTraining && weightChanges && weightChanges.length > 0) {
|
2421 |
+
let neuronGradient = 0;
|
2422 |
+
if (layerIndex < network.layers.length - 1 && weightChanges[layerIndex]) {
|
2423 |
+
for (let outIdx = 0; outIdx < weightChanges[layerIndex].length; outIdx++) {
|
2424 |
+
if (weightChanges[layerIndex][outIdx] && weightChanges[layerIndex][outIdx][nodeIndex]) {
|
2425 |
+
neuronGradient += Math.abs(weightChanges[layerIndex][outIdx][nodeIndex]);
|
2426 |
+
}
|
2427 |
+
}
|
2428 |
+
neuronGradient /= weightChanges[layerIndex].length || 1;
|
2429 |
+
}
|
2430 |
+
|
2431 |
+
if (neuronGradient > 0.01) {
|
2432 |
+
ctx.fillStyle = neuronGradient > 0.1 ? '#ffff00' : '#ffffff';
|
2433 |
+
ctx.font = `${Math.max(6, nodeRadius / 3)}px monospace`;
|
2434 |
+
ctx.fillText(`Δ${neuronGradient.toFixed(2)}`, x, y + 8);
|
2435 |
+
}
|
2436 |
+
}
|
2437 |
+
|
2438 |
ctx.shadowBlur = 0;
|
2439 |
}
|
2440 |
});
|
|
|
2519 |
lossArea.setAttribute('points', points + ' 100,100 0,100');
|
2520 |
}
|
2521 |
|
2522 |
+
// Loss landscape functions
|
2523 |
+
function calculateLossLandscape() {
|
2524 |
+
if (!network || !currentTask) return;
|
2525 |
+
|
2526 |
+
const range = 2;
|
2527 |
+
lossLandscape = [];
|
2528 |
+
gradientField = [];
|
2529 |
+
|
2530 |
+
// Sample key weights from the first layer for visualization
|
2531 |
+
const originalWeights = JSON.parse(JSON.stringify(network.weights));
|
2532 |
+
|
2533 |
+
for (let i = 0; i < landscapeResolution; i++) {
|
2534 |
+
lossLandscape[i] = [];
|
2535 |
+
gradientField[i] = [];
|
2536 |
+
|
2537 |
+
for (let j = 0; j < landscapeResolution; j++) {
|
2538 |
+
// Perturb the first two weights
|
2539 |
+
const w1 = (i / (landscapeResolution - 1) - 0.5) * range;
|
2540 |
+
const w2 = (j / (landscapeResolution - 1) - 0.5) * range;
|
2541 |
+
|
2542 |
+
if (network.weights[0] && network.weights[0][0]) {
|
2543 |
+
network.weights[0][0][0] = originalWeights[0][0][0] + w1;
|
2544 |
+
if (network.weights[0][0][1] !== undefined) {
|
2545 |
+
network.weights[0][0][1] = originalWeights[0][0][1] + w2;
|
2546 |
+
}
|
2547 |
+
}
|
2548 |
+
|
2549 |
+
// Calculate loss at this point
|
2550 |
+
let totalLoss = 0;
|
2551 |
+
let gradX = 0, gradY = 0;
|
2552 |
+
|
2553 |
+
const epsilon = 0.001;
|
2554 |
+
|
2555 |
+
// Calculate loss
|
2556 |
+
for (const sample of currentTask.data) {
|
2557 |
+
const output = network.forward(sample.input);
|
2558 |
+
const loss = sample.target.reduce((sum, t, idx) =>
|
2559 |
+
sum + Math.pow(t - output[idx], 2), 0) / sample.target.length;
|
2560 |
+
totalLoss += loss;
|
2561 |
+
}
|
2562 |
+
totalLoss /= currentTask.data.length;
|
2563 |
+
|
2564 |
+
// Calculate numerical gradients
|
2565 |
+
if (network.weights[0] && network.weights[0][0]) {
|
2566 |
+
// Gradient in x direction
|
2567 |
+
network.weights[0][0][0] = originalWeights[0][0][0] + w1 + epsilon;
|
2568 |
+
let lossPlus = 0;
|
2569 |
+
for (const sample of currentTask.data) {
|
2570 |
+
const output = network.forward(sample.input);
|
2571 |
+
const loss = sample.target.reduce((sum, t, idx) =>
|
2572 |
+
sum + Math.pow(t - output[idx], 2), 0) / sample.target.length;
|
2573 |
+
lossPlus += loss;
|
2574 |
+
}
|
2575 |
+
lossPlus /= currentTask.data.length;
|
2576 |
+
gradX = (lossPlus - totalLoss) / epsilon;
|
2577 |
+
|
2578 |
+
// Reset and calculate gradient in y direction
|
2579 |
+
network.weights[0][0][0] = originalWeights[0][0][0] + w1;
|
2580 |
+
if (network.weights[0][0][1] !== undefined) {
|
2581 |
+
network.weights[0][0][1] = originalWeights[0][0][1] + w2 + epsilon;
|
2582 |
+
let lossPlusY = 0;
|
2583 |
+
for (const sample of currentTask.data) {
|
2584 |
+
const output = network.forward(sample.input);
|
2585 |
+
const loss = sample.target.reduce((sum, t, idx) =>
|
2586 |
+
sum + Math.pow(t - output[idx], 2), 0) / sample.target.length;
|
2587 |
+
lossPlusY += loss;
|
2588 |
+
}
|
2589 |
+
lossPlusY /= currentTask.data.length;
|
2590 |
+
gradY = (lossPlusY - totalLoss) / epsilon;
|
2591 |
+
}
|
2592 |
+
}
|
2593 |
+
|
2594 |
+
lossLandscape[i][j] = totalLoss;
|
2595 |
+
gradientField[i][j] = { x: gradX, y: gradY };
|
2596 |
+
}
|
2597 |
+
}
|
2598 |
+
|
2599 |
+
// Restore original weights
|
2600 |
+
network.weights = originalWeights;
|
2601 |
+
}
|
2602 |
+
|
2603 |
+
function drawLossLandscape() {
|
2604 |
+
const canvas = lossLandscapeCanvas;
|
2605 |
+
const ctx = canvas.getContext('2d');
|
2606 |
+
const width = canvas.width;
|
2607 |
+
const height = canvas.height;
|
2608 |
+
|
2609 |
+
ctx.clearRect(0, 0, width, height);
|
2610 |
+
|
2611 |
+
if (lossLandscape.length === 0) {
|
2612 |
+
ctx.fillStyle = '#9ca3af';
|
2613 |
+
ctx.font = '16px system-ui';
|
2614 |
+
ctx.textAlign = 'center';
|
2615 |
+
ctx.fillText('Training to generate loss landscape...', width/2, height/2);
|
2616 |
+
return;
|
2617 |
+
}
|
2618 |
+
|
2619 |
+
// Transform and projection parameters
|
2620 |
+
const centerX = width / 2;
|
2621 |
+
const centerY = height / 2;
|
2622 |
+
const scale = 200 * zoomLevel;
|
2623 |
+
|
2624 |
+
const radX = (rotationX * Math.PI) / 180;
|
2625 |
+
const radZ = (rotationZ * Math.PI) / 180;
|
2626 |
+
|
2627 |
+
// Find min/max loss for normalization
|
2628 |
+
let minLoss = Infinity, maxLoss = -Infinity;
|
2629 |
+
for (let i = 0; i < landscapeResolution; i++) {
|
2630 |
+
for (let j = 0; j < landscapeResolution; j++) {
|
2631 |
+
minLoss = Math.min(minLoss, lossLandscape[i][j]);
|
2632 |
+
maxLoss = Math.max(maxLoss, lossLandscape[i][j]);
|
2633 |
+
}
|
2634 |
+
}
|
2635 |
+
|
2636 |
+
const lossRange = maxLoss - minLoss || 1;
|
2637 |
+
|
2638 |
+
// Create projected points
|
2639 |
+
const projectedPoints = [];
|
2640 |
+
for (let i = 0; i < landscapeResolution; i++) {
|
2641 |
+
projectedPoints[i] = [];
|
2642 |
+
for (let j = 0; j < landscapeResolution; j++) {
|
2643 |
+
const x = (i / (landscapeResolution - 1) - 0.5) * 2;
|
2644 |
+
const y = (j / (landscapeResolution - 1) - 0.5) * 2;
|
2645 |
+
const z = ((lossLandscape[i][j] - minLoss) / lossRange - 0.5) * 1.5;
|
2646 |
+
|
2647 |
+
// 3D rotation
|
2648 |
+
const y1 = y * Math.cos(radX) - z * Math.sin(radX);
|
2649 |
+
const z1 = y * Math.sin(radX) + z * Math.cos(radX);
|
2650 |
+
const x2 = x * Math.cos(radZ) - y1 * Math.sin(radZ);
|
2651 |
+
const y2 = x * Math.sin(radZ) + y1 * Math.cos(radZ);
|
2652 |
+
|
2653 |
+
projectedPoints[i][j] = {
|
2654 |
+
x: centerX + x2 * scale,
|
2655 |
+
y: centerY + y2 * scale,
|
2656 |
+
z: z1,
|
2657 |
+
loss: lossLandscape[i][j]
|
2658 |
+
};
|
2659 |
+
}
|
2660 |
+
}
|
2661 |
+
|
2662 |
+
// Draw contour lines if enabled
|
2663 |
+
if (showContours) {
|
2664 |
+
const contourLevels = 8;
|
2665 |
+
for (let level = 0; level < contourLevels; level++) {
|
2666 |
+
const targetLoss = minLoss + (level / contourLevels) * lossRange;
|
2667 |
+
ctx.strokeStyle = `hsla(${240 - level * 30}, 70%, 60%, 0.3)`;
|
2668 |
+
ctx.lineWidth = 1;
|
2669 |
+
|
2670 |
+
for (let i = 0; i < landscapeResolution - 1; i++) {
|
2671 |
+
for (let j = 0; j < landscapeResolution - 1; j++) {
|
2672 |
+
const corners = [
|
2673 |
+
projectedPoints[i][j],
|
2674 |
+
projectedPoints[i+1][j],
|
2675 |
+
projectedPoints[i+1][j+1],
|
2676 |
+
projectedPoints[i][j+1]
|
2677 |
+
];
|
2678 |
+
|
2679 |
+
// Simple contour drawing
|
2680 |
+
for (let k = 0; k < 4; k++) {
|
2681 |
+
const p1 = corners[k];
|
2682 |
+
const p2 = corners[(k + 1) % 4];
|
2683 |
+
|
2684 |
+
if ((p1.loss <= targetLoss && p2.loss >= targetLoss) ||
|
2685 |
+
(p1.loss >= targetLoss && p2.loss <= targetLoss)) {
|
2686 |
+
const t = (targetLoss - p1.loss) / (p2.loss - p1.loss);
|
2687 |
+
const x = p1.x + t * (p2.x - p1.x);
|
2688 |
+
const y = p1.y + t * (p2.y - p1.y);
|
2689 |
+
|
2690 |
+
ctx.beginPath();
|
2691 |
+
ctx.arc(x, y, 1, 0, 2 * Math.PI);
|
2692 |
+
ctx.stroke();
|
2693 |
+
}
|
2694 |
+
}
|
2695 |
+
}
|
2696 |
+
}
|
2697 |
+
}
|
2698 |
+
}
|
2699 |
+
|
2700 |
+
// Draw surface mesh
|
2701 |
+
for (let i = 0; i < landscapeResolution - 1; i++) {
|
2702 |
+
for (let j = 0; j < landscapeResolution - 1; j++) {
|
2703 |
+
const corners = [
|
2704 |
+
projectedPoints[i][j],
|
2705 |
+
projectedPoints[i+1][j],
|
2706 |
+
projectedPoints[i+1][j+1],
|
2707 |
+
projectedPoints[i][j+1]
|
2708 |
+
];
|
2709 |
+
|
2710 |
+
// Color based on loss value
|
2711 |
+
const avgLoss = corners.reduce((sum, p) => sum + p.loss, 0) / 4;
|
2712 |
+
const normalizedLoss = (avgLoss - minLoss) / lossRange;
|
2713 |
+
const hue = 240 - normalizedLoss * 120; // Blue to red
|
2714 |
+
const alpha = 0.6;
|
2715 |
+
|
2716 |
+
ctx.fillStyle = `hsla(${hue}, 70%, 50%, ${alpha})`;
|
2717 |
+
ctx.strokeStyle = `hsla(${hue}, 70%, 30%, 0.8)`;
|
2718 |
+
ctx.lineWidth = 0.5;
|
2719 |
+
|
2720 |
+
ctx.beginPath();
|
2721 |
+
ctx.moveTo(corners[0].x, corners[0].y);
|
2722 |
+
for (let k = 1; k < corners.length; k++) {
|
2723 |
+
ctx.lineTo(corners[k].x, corners[k].y);
|
2724 |
+
}
|
2725 |
+
ctx.closePath();
|
2726 |
+
ctx.fill();
|
2727 |
+
ctx.stroke();
|
2728 |
+
}
|
2729 |
+
}
|
2730 |
+
|
2731 |
+
// Draw gradient arrows if enabled
|
2732 |
+
if (showGradients) {
|
2733 |
+
const step = Math.max(1, Math.floor(landscapeResolution / 15));
|
2734 |
+
for (let i = 0; i < landscapeResolution; i += step) {
|
2735 |
+
for (let j = 0; j < landscapeResolution; j += step) {
|
2736 |
+
const point = projectedPoints[i][j];
|
2737 |
+
const grad = gradientField[i][j];
|
2738 |
+
|
2739 |
+
if (grad && (Math.abs(grad.x) > 0.01 || Math.abs(grad.y) > 0.01)) {
|
2740 |
+
const arrowLength = 30;
|
2741 |
+
const gradMagnitude = Math.sqrt(grad.x * grad.x + grad.y * grad.y);
|
2742 |
+
const normalizedGradX = (grad.x / gradMagnitude) * arrowLength;
|
2743 |
+
const normalizedGradY = (grad.y / gradMagnitude) * arrowLength;
|
2744 |
+
|
2745 |
+
// Transform gradient direction
|
2746 |
+
const gx1 = normalizedGradX * Math.cos(radZ) - normalizedGradY * Math.sin(radZ);
|
2747 |
+
const gy1 = normalizedGradX * Math.sin(radZ) + normalizedGradY * Math.cos(radZ);
|
2748 |
+
|
2749 |
+
ctx.strokeStyle = '#f59e0b';
|
2750 |
+
ctx.lineWidth = 2;
|
2751 |
+
ctx.beginPath();
|
2752 |
+
ctx.moveTo(point.x, point.y);
|
2753 |
+
ctx.lineTo(point.x - gx1, point.y - gy1);
|
2754 |
+
ctx.stroke();
|
2755 |
+
|
2756 |
+
// Arrow head
|
2757 |
+
const headLength = 6;
|
2758 |
+
const angle = Math.atan2(-gy1, -gx1);
|
2759 |
+
ctx.beginPath();
|
2760 |
+
ctx.moveTo(point.x - gx1, point.y - gy1);
|
2761 |
+
ctx.lineTo(
|
2762 |
+
point.x - gx1 + headLength * Math.cos(angle - Math.PI/6),
|
2763 |
+
point.y - gy1 + headLength * Math.sin(angle - Math.PI/6)
|
2764 |
+
);
|
2765 |
+
ctx.moveTo(point.x - gx1, point.y - gy1);
|
2766 |
+
ctx.lineTo(
|
2767 |
+
point.x - gx1 + headLength * Math.cos(angle + Math.PI/6),
|
2768 |
+
point.y - gy1 + headLength * Math.sin(angle + Math.PI/6)
|
2769 |
+
);
|
2770 |
+
ctx.stroke();
|
2771 |
+
}
|
2772 |
+
}
|
2773 |
+
}
|
2774 |
+
}
|
2775 |
+
|
2776 |
+
// Draw training path if enabled
|
2777 |
+
if (showPath && trainingPath.length > 1) {
|
2778 |
+
ctx.strokeStyle = '#06b6d4';
|
2779 |
+
ctx.lineWidth = 3;
|
2780 |
+
ctx.beginPath();
|
2781 |
+
|
2782 |
+
for (let i = 0; i < trainingPath.length; i++) {
|
2783 |
+
const pathPoint = trainingPath[i];
|
2784 |
+
// Project path point to screen coordinates
|
2785 |
+
const x = (pathPoint.w1 / 2 + 0.5) * (landscapeResolution - 1);
|
2786 |
+
const y = (pathPoint.w2 / 2 + 0.5) * (landscapeResolution - 1);
|
2787 |
+
|
2788 |
+
if (x >= 0 && x < landscapeResolution && y >= 0 && y < landscapeResolution) {
|
2789 |
+
const xi = Math.floor(x);
|
2790 |
+
const yi = Math.floor(y);
|
2791 |
+
|
2792 |
+
if (xi < landscapeResolution && yi < landscapeResolution && projectedPoints[xi] && projectedPoints[xi][yi]) {
|
2793 |
+
const point = projectedPoints[xi][yi];
|
2794 |
+
if (i === 0) {
|
2795 |
+
ctx.moveTo(point.x, point.y);
|
2796 |
+
} else {
|
2797 |
+
ctx.lineTo(point.x, point.y);
|
2798 |
+
}
|
2799 |
+
}
|
2800 |
+
}
|
2801 |
+
}
|
2802 |
+
ctx.stroke();
|
2803 |
+
|
2804 |
+
// Mark current position
|
2805 |
+
if (trainingPath.length > 0) {
|
2806 |
+
const current = trainingPath[trainingPath.length - 1];
|
2807 |
+
const x = (current.w1 / 2 + 0.5) * (landscapeResolution - 1);
|
2808 |
+
const y = (current.w2 / 2 + 0.5) * (landscapeResolution - 1);
|
2809 |
+
|
2810 |
+
if (x >= 0 && x < landscapeResolution && y >= 0 && y < landscapeResolution) {
|
2811 |
+
const xi = Math.floor(x);
|
2812 |
+
const yi = Math.floor(y);
|
2813 |
+
|
2814 |
+
if (xi < landscapeResolution && yi < landscapeResolution && projectedPoints[xi] && projectedPoints[xi][yi]) {
|
2815 |
+
const point = projectedPoints[xi][yi];
|
2816 |
+
ctx.fillStyle = '#06b6d4';
|
2817 |
+
ctx.beginPath();
|
2818 |
+
ctx.arc(point.x, point.y, 5, 0, 2 * Math.PI);
|
2819 |
+
ctx.fill();
|
2820 |
+
}
|
2821 |
+
}
|
2822 |
+
}
|
2823 |
+
}
|
2824 |
+
}
|
2825 |
+
|
2826 |
// Training step
|
2827 |
function trainStep() {
|
2828 |
const result = network.trainBatch(currentTask.data);
|
|
|
2837 |
lossHistory.push(result.loss);
|
2838 |
if (lossHistory.length > 100) lossHistory.shift();
|
2839 |
|
2840 |
+
// Track training path for loss landscape
|
2841 |
+
if (network.weights[0] && network.weights[0][0] && trainingPath.length < 1000) {
|
2842 |
+
trainingPath.push({
|
2843 |
+
w1: network.weights[0][0][0] || 0,
|
2844 |
+
w2: network.weights[0][0][1] || 0,
|
2845 |
+
loss: result.loss,
|
2846 |
+
epoch: epoch
|
2847 |
+
});
|
2848 |
+
}
|
2849 |
+
|
2850 |
+
// Recalculate loss landscape periodically
|
2851 |
+
if (epoch % 10 === 0) {
|
2852 |
+
calculateLossLandscape();
|
2853 |
+
}
|
2854 |
+
|
2855 |
const newPredictions = currentTask.data.map(data => {
|
2856 |
const output = network.forward(data.input);
|
2857 |
const rawOutput = output[0];
|
|
|
2915 |
|
2916 |
updateLossChart();
|
2917 |
drawNetwork();
|
2918 |
+
drawLossLandscape();
|
2919 |
if (currentTask.hasVisualization) {
|
2920 |
drawDataVisualization();
|
2921 |
}
|
|
|
2952 |
if (currentTask.isBabyMode) {
|
2953 |
updateBabyVisualization();
|
2954 |
}
|
2955 |
+
drawLossLandscape();
|
2956 |
}
|
2957 |
animationId = requestAnimationFrame(animate);
|
2958 |
}
|
|
|
2982 |
accuracy = 0;
|
2983 |
animationTime = 0;
|
2984 |
|
2985 |
+
// Reset loss landscape
|
2986 |
+
lossLandscape = [];
|
2987 |
+
gradientField = [];
|
2988 |
+
trainingPath = [];
|
2989 |
+
calculateLossLandscape();
|
2990 |
+
|
2991 |
trainBtn.innerHTML = `
|
2992 |
<svg class="icon" fill="currentColor" viewBox="0 0 24 24">
|
2993 |
<path d="M8 5v14l11-7z"/>
|
|
|
3012 |
`;
|
3013 |
trainBtn.className = 'btn btn-pause';
|
3014 |
|
3015 |
+
const trainingSpeed = 100;
|
|
|
3016 |
trainInterval = setInterval(trainStep, trainingSpeed);
|
3017 |
} else {
|
3018 |
trainBtn.innerHTML = `
|
|
|
3033 |
// Developer mode event listeners
|
3034 |
devArchitecture.addEventListener('input', updateParameterCount);
|
3035 |
|
3036 |
+
// Loss landscape event listeners
|
3037 |
+
rotationXSlider.addEventListener('input', (e) => {
|
3038 |
+
rotationX = parseFloat(e.target.value);
|
3039 |
+
drawLossLandscape();
|
3040 |
+
});
|
3041 |
+
|
3042 |
+
rotationZSlider.addEventListener('input', (e) => {
|
3043 |
+
rotationZ = parseFloat(e.target.value);
|
3044 |
+
drawLossLandscape();
|
3045 |
+
});
|
3046 |
+
|
3047 |
+
zoomSlider.addEventListener('input', (e) => {
|
3048 |
+
zoomLevel = parseFloat(e.target.value);
|
3049 |
+
drawLossLandscape();
|
3050 |
+
});
|
3051 |
+
|
3052 |
+
gradientsCheckbox.addEventListener('change', (e) => {
|
3053 |
+
showGradients = e.target.checked;
|
3054 |
+
drawLossLandscape();
|
3055 |
+
});
|
3056 |
+
|
3057 |
+
contoursCheckbox.addEventListener('change', (e) => {
|
3058 |
+
showContours = e.target.checked;
|
3059 |
+
drawLossLandscape();
|
3060 |
+
});
|
3061 |
+
|
3062 |
+
pathCheckbox.addEventListener('change', (e) => {
|
3063 |
+
showPath = e.target.checked;
|
3064 |
+
drawLossLandscape();
|
3065 |
+
});
|
3066 |
+
|
3067 |
+
// Mouse interaction for loss landscape
|
3068 |
+
lossLandscapeCanvas.addEventListener('mousedown', (e) => {
|
3069 |
+
mouseDown = true;
|
3070 |
+
lastMouseX = e.clientX;
|
3071 |
+
lastMouseY = e.clientY;
|
3072 |
+
});
|
3073 |
+
|
3074 |
+
lossLandscapeCanvas.addEventListener('mousemove', (e) => {
|
3075 |
+
if (mouseDown) {
|
3076 |
+
const deltaX = e.clientX - lastMouseX;
|
3077 |
+
const deltaY = e.clientY - lastMouseY;
|
3078 |
+
|
3079 |
+
rotationZ = (rotationZ + deltaX * 0.5) % 360;
|
3080 |
+
rotationX = Math.max(-90, Math.min(90, rotationX - deltaY * 0.5));
|
3081 |
+
|
3082 |
+
rotationXSlider.value = rotationX;
|
3083 |
+
rotationZSlider.value = rotationZ;
|
3084 |
+
|
3085 |
+
drawLossLandscape();
|
3086 |
+
|
3087 |
+
lastMouseX = e.clientX;
|
3088 |
+
lastMouseY = e.clientY;
|
3089 |
+
}
|
3090 |
+
});
|
3091 |
+
|
3092 |
+
lossLandscapeCanvas.addEventListener('mouseup', () => {
|
3093 |
+
mouseDown = false;
|
3094 |
+
});
|
3095 |
+
|
3096 |
+
lossLandscapeCanvas.addEventListener('mouseleave', () => {
|
3097 |
+
mouseDown = false;
|
3098 |
+
});
|
3099 |
+
|
3100 |
+
// Wheel zoom for loss landscape
|
3101 |
+
lossLandscapeCanvas.addEventListener('wheel', (e) => {
|
3102 |
+
e.preventDefault();
|
3103 |
+
const delta = e.deltaY > 0 ? 0.9 : 1.1;
|
3104 |
+
zoomLevel = Math.max(0.5, Math.min(3, zoomLevel * delta));
|
3105 |
+
zoomSlider.value = zoomLevel;
|
3106 |
+
drawLossLandscape();
|
3107 |
+
});
|
3108 |
+
|
3109 |
// Walkthrough Mode functionality
|
3110 |
let walkthroughActive = false;
|
3111 |
let walkthroughStep = 0;
|
|
|
3127 |
content: 'This is the input layer (left side). It receives the raw data - like numbers, images, or text. Each circle represents one input neuron that holds a piece of information.',
|
3128 |
element: '#networkCanvas',
|
3129 |
position: 'right',
|
3130 |
+
highlight: {x: 0, y: 0, width: 130, height: 300}
|
3131 |
},
|
3132 |
{
|
3133 |
title: 'Hidden Layers',
|
3134 |
content: 'These middle layers are where the "magic" happens! They transform the input data through mathematical operations, finding patterns and relationships.',
|
3135 |
element: '#networkCanvas',
|
3136 |
position: 'top',
|
3137 |
+
highlight: {x: 130, y: 0, width: 140, height: 300}
|
3138 |
},
|
3139 |
{
|
3140 |
title: 'Output Layer',
|
3141 |
content: 'The final layer gives us the result - a prediction, classification, or decision based on what the network learned from the input.',
|
3142 |
element: '#networkCanvas',
|
3143 |
position: 'left',
|
3144 |
+
highlight: {x: 270, y: 0, width: 130, height: 300}
|
3145 |
},
|
3146 |
{
|
3147 |
title: 'Connections (Weights)',
|
|
|
3262 |
position: 'right'
|
3263 |
}
|
3264 |
]
|
3265 |
+
},
|
3266 |
+
landscape: {
|
3267 |
+
title: 'Loss Landscape & Gradients',
|
3268 |
+
steps: [
|
3269 |
+
{
|
3270 |
+
title: 'Welcome to Loss Landscapes!',
|
3271 |
+
content: 'The loss landscape shows how the error changes as we adjust the network\'s weights. Think of it like a 3D mountain range where we want to find the lowest valley.',
|
3272 |
+
element: null,
|
3273 |
+
position: 'center'
|
3274 |
+
},
|
3275 |
+
{
|
3276 |
+
title: 'The 3D Loss Surface',
|
3277 |
+
content: 'This 3D visualization shows the loss landscape. Blue areas are low loss (good), red areas are high loss (bad). The network tries to \'roll downhill\' to find the minimum.',
|
3278 |
+
element: '#lossLandscapeCanvas',
|
3279 |
+
position: 'right'
|
3280 |
+
},
|
3281 |
+
{
|
3282 |
+
title: 'Gradient Arrows',
|
3283 |
+
content: 'The yellow arrows show gradients - they point in the direction of steepest increase in loss. The network moves OPPOSITE to these arrows to reduce loss.',
|
3284 |
+
element: '.landscape-toggle',
|
3285 |
+
position: 'top'
|
3286 |
+
},
|
3287 |
+
{
|
3288 |
+
title: 'Training Path',
|
3289 |
+
content: 'The blue line shows the path the network takes during training. It starts somewhere random and gradually finds its way to low-loss regions.',
|
3290 |
+
element: '#lossLandscapeCanvas',
|
3291 |
+
position: 'bottom'
|
3292 |
+
},
|
3293 |
+
{
|
3294 |
+
title: 'Interactive Controls',
|
3295 |
+
content: 'Use these controls to rotate, zoom, and toggle different visualizations. Try dragging on the landscape to rotate it around!',
|
3296 |
+
element: '.landscape-controls',
|
3297 |
+
position: 'bottom'
|
3298 |
+
},
|
3299 |
+
{
|
3300 |
+
title: 'Start Training to See It Live',
|
3301 |
+
content: 'Now start training a task and watch how the loss landscape guides the learning process. The path will update in real-time!',
|
3302 |
+
element: '#trainBtn',
|
3303 |
+
position: 'bottom'
|
3304 |
+
}
|
3305 |
+
]
|
3306 |
+
},
|
3307 |
+
advanced: {
|
3308 |
+
title: 'Advanced Visualizations',
|
3309 |
+
steps: [
|
3310 |
+
{
|
3311 |
+
title: 'Advanced Neural Network Insights',
|
3312 |
+
content: 'Now let\'s explore the advanced visualizations that show exactly what happens inside the neural network during training.',
|
3313 |
+
element: null,
|
3314 |
+
position: 'center'
|
3315 |
+
},
|
3316 |
+
{
|
3317 |
+
title: 'Gradient Flow in Connections',
|
3318 |
+
content: 'Watch the animated particles flowing along connections. More particles = stronger gradients. Green flows are positive weights, red are negative.',
|
3319 |
+
element: '#networkCanvas',
|
3320 |
+
position: 'right'
|
3321 |
+
},
|
3322 |
+
{
|
3323 |
+
title: 'Neuron Gradient Rings',
|
3324 |
+
content: 'Yellow rings around neurons show gradient magnitude. Bright, pulsing rings mean the neuron is changing rapidly during training.',
|
3325 |
+
element: '#networkCanvas',
|
3326 |
+
position: 'left'
|
3327 |
+
},
|
3328 |
+
{
|
3329 |
+
title: 'Weight Change Indicators',
|
3330 |
+
content: 'The thickness and brightness of connections show how much each weight is changing. Thicker, brighter lines = more learning happening.',
|
3331 |
+
element: '#networkCanvas',
|
3332 |
+
position: 'top'
|
3333 |
+
},
|
3334 |
+
{
|
3335 |
+
title: 'Loss Landscape Contours',
|
3336 |
+
content: 'Contour lines on the loss landscape are like elevation lines on a topographic map - they show areas of equal loss value.',
|
3337 |
+
element: '#lossLandscapeCanvas',
|
3338 |
+
position: 'right'
|
3339 |
+
},
|
3340 |
+
{
|
3341 |
+
title: 'Real-time Training Visualization',
|
3342 |
+
content: 'All these visualizations update in real-time as the network trains. You can see the exact moment when the network \'gets it\'!',
|
3343 |
+
element: '.control-panel',
|
3344 |
+
position: 'bottom'
|
3345 |
+
},
|
3346 |
+
{
|
3347 |
+
title: 'Understanding Convergence',
|
3348 |
+
content: 'Watch how the training path in the loss landscape eventually stops moving - this means the network has converged to a solution.',
|
3349 |
+
element: '#lossLandscapeCanvas',
|
3350 |
+
position: 'bottom'
|
3351 |
+
}
|
3352 |
+
]
|
3353 |
}
|
3354 |
};
|
3355 |
|
|
|
3367 |
taskSelection.style.display = 'block';
|
3368 |
currentCategory = 'fundamentals';
|
3369 |
showCategory('fundamentals');
|
3370 |
+
} else if (tutorialId === 'landscape' || tutorialId === 'advanced') {
|
3371 |
+
document.getElementById('walkthroughMode').style.display = 'none';
|
3372 |
+
taskSelection.style.display = 'block';
|
3373 |
+
currentCategory = 'fundamentals';
|
3374 |
+
showCategory('fundamentals');
|
3375 |
+
// Auto-select XOR for better landscape visualization
|
3376 |
+
setTimeout(() => selectTask('xor'), 500);
|
3377 |
+
} else if (tutorialId === 'basics' || tutorialId === 'training' || tutorialId === 'visualization') {
|
3378 |
+
// For basic tutorials, start with a simple task for better understanding
|
3379 |
+
document.getElementById('walkthroughMode').style.display = 'none';
|
3380 |
+
taskSelection.style.display = 'block';
|
3381 |
+
currentCategory = 'fundamentals';
|
3382 |
+
showCategory('fundamentals');
|
3383 |
+
setTimeout(() => selectTask('and'), 500);
|
3384 |
}
|
3385 |
|
3386 |
showWalkthroughStep();
|
|
|
3397 |
document.getElementById('walkthroughStep').textContent = walkthroughStep + 1;
|
3398 |
document.getElementById('walkthroughTotal').textContent = walkthroughTutorial.steps.length;
|
3399 |
|
3400 |
+
// Show overlay with reduced opacity for better visibility
|
3401 |
overlay.style.display = 'block';
|
3402 |
progress.style.display = 'block';
|
3403 |
+
|
3404 |
+
// Ensure all content is visible by adjusting z-index
|
3405 |
+
document.getElementById('trainingInterface').style.position = 'relative';
|
3406 |
+
document.getElementById('trainingInterface').style.zIndex = '1';
|
3407 |
|
3408 |
// Update popup content
|
3409 |
document.getElementById('walkthroughTitle').textContent = step.title;
|
|
|
3415 |
if (element) {
|
3416 |
const rect = element.getBoundingClientRect();
|
3417 |
|
3418 |
+
// Highlight element with better visibility
|
3419 |
if (step.highlight) {
|
3420 |
const canvasRect = element.getBoundingClientRect();
|
3421 |
highlight.style.left = (canvasRect.left + step.highlight.x) + 'px';
|
|
|
3423 |
highlight.style.width = step.highlight.width + 'px';
|
3424 |
highlight.style.height = step.highlight.height + 'px';
|
3425 |
} else {
|
3426 |
+
highlight.style.left = rect.left - 8 + 'px';
|
3427 |
+
highlight.style.top = rect.top - 8 + 'px';
|
3428 |
+
highlight.style.width = rect.width + 16 + 'px';
|
3429 |
+
highlight.style.height = rect.height + 16 + 'px';
|
3430 |
}
|
3431 |
highlight.style.display = 'block';
|
3432 |
+
|
3433 |
+
// Bring highlighted element to front
|
3434 |
+
if (element) {
|
3435 |
+
element.style.position = 'relative';
|
3436 |
+
element.style.zIndex = '10002';
|
3437 |
+
}
|
3438 |
|
3439 |
// Position popup
|
3440 |
positionPopup(popup, rect, step.position);
|
|
|
3523 |
document.getElementById('walkthroughPopup').style.display = 'none';
|
3524 |
document.getElementById('walkthroughProgress').style.display = 'none';
|
3525 |
document.getElementById('walkthroughIndicator').style.display = 'none';
|
3526 |
+
|
3527 |
+
// Reset z-index values
|
3528 |
+
const trainingInterface = document.getElementById('trainingInterface');
|
3529 |
+
if (trainingInterface) {
|
3530 |
+
trainingInterface.style.zIndex = '';
|
3531 |
+
trainingInterface.style.position = '';
|
3532 |
+
}
|
3533 |
+
|
3534 |
+
// Reset any highlighted elements
|
3535 |
+
const networkCanvas = document.getElementById('networkCanvas');
|
3536 |
+
if (networkCanvas) {
|
3537 |
+
networkCanvas.style.zIndex = '';
|
3538 |
+
networkCanvas.style.position = '';
|
3539 |
+
}
|
3540 |
}
|
3541 |
|
3542 |
// Walkthrough event listeners
|
|
|
3546 |
|
3547 |
// Initialize
|
3548 |
startAnimation();
|
3549 |
+
|
3550 |
+
// Initialize loss landscape on page load
|
3551 |
+
setTimeout(() => {
|
3552 |
+
if (lossLandscapeCanvas) {
|
3553 |
+
const ctx = lossLandscapeCanvas.getContext('2d');
|
3554 |
+
ctx.fillStyle = '#9ca3af';
|
3555 |
+
ctx.font = '16px system-ui';
|
3556 |
+
ctx.textAlign = 'center';
|
3557 |
+
ctx.fillText('Select a task to view loss landscape', lossLandscapeCanvas.width/2, lossLandscapeCanvas.height/2);
|
3558 |
+
}
|
3559 |
+
}, 100);
|
3560 |
</script>
|
|
|
3561 |
</body>
|
3562 |
</html>
|