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>
|