PlaygroundOrganizer / Blog /dropout_neural_network.html
mnoorchenar's picture
Update 2026-01-30 15:32:07
e890e92
Raw
History Blame Contribute Delete
32.5 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dropout Neural Network Visualization</title>
<style>
body {
font-family: 'Arial', sans-serif;
margin: 0;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
color: white;
}
.container {
max-width: 1400px;
margin: 0 auto;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 30px;
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37);
}
h1 {
text-align: center;
margin-bottom: 30px;
font-size: 2.5em;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
}
.controls {
display: flex;
justify-content: center;
gap: 20px;
margin-bottom: 30px;
flex-wrap: wrap;
}
.control-group {
background: rgba(255, 255, 255, 0.2);
padding: 15px;
border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
}
label {
font-weight: bold;
font-size: 14px;
}
input[type="range"] {
width: 120px;
}
button {
background: linear-gradient(45deg, #ff6b6b, #ee5a24);
color: white;
border: none;
padding: 12px 24px;
border-radius: 25px;
cursor: pointer;
font-weight: bold;
font-size: 16px;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(238, 90, 36, 0.3);
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(238, 90, 36, 0.4);
}
button:disabled {
background: #666;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
.visualization {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 30px;
}
.network-container {
flex: 2;
background: rgba(0, 0, 0, 0.3);
border-radius: 15px;
padding: 20px;
position: relative;
}
.loss-chart {
margin-top: 20px;
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
padding: 15px;
height: 250px;
}
.chart-title {
text-align: center;
margin-bottom: 15px;
font-weight: bold;
color: white;
}
.info-panel {
flex: 1;
background: rgba(255, 255, 255, 0.2);
border-radius: 15px;
padding: 20px;
height: fit-content;
}
.epoch-info {
text-align: center;
margin-bottom: 20px;
font-size: 18px;
font-weight: bold;
}
.stats {
margin-bottom: 20px;
}
.stat-item {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
padding: 8px;
background: rgba(255, 255, 255, 0.1);
border-radius: 5px;
}
.legend {
margin-top: 20px;
}
.legend-item {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.legend-color {
width: 20px;
height: 20px;
border-radius: 50%;
margin-right: 10px;
}
.progress-bar {
width: 100%;
height: 20px;
background: rgba(255, 255, 255, 0.2);
border-radius: 10px;
overflow: hidden;
margin-top: 10px;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #00d2ff, #3a7bd5);
width: 0%;
transition: width 0.3s ease;
}
#networkSvg {
width: 100%;
height: 500px;
background: rgba(255, 255, 255, 0.05);
border-radius: 10px;
}
.mode-toggle {
text-align: center;
margin-bottom: 20px;
}
.mode-toggle button {
margin: 0 10px;
padding: 8px 16px;
font-size: 14px;
}
.mode-toggle button.active {
background: linear-gradient(45deg, #3a7bd5, #00d2ff);
}
</style>
</head>
<body>
<div class="container">
<h1>🧠 Dropout Neural Network Visualization</h1>
<div class="controls">
<div class="control-group">
<label for="dropoutRate">Dropout Rate</label>
<input type="range" id="dropoutRate" min="0" max="0.8" step="0.1" value="0.5">
<span id="dropoutValue">0.5</span>
</div>
<div class="control-group">
<label for="learningRate">Learning Rate</label>
<input type="range" id="learningRate" min="0.01" max="0.5" step="0.01" value="0.1">
<span id="learningValue">0.1</span>
</div>
<div class="control-group">
<label for="epochs">Epochs</label>
<input type="range" id="epochs" min="10" max="200" step="10" value="50">
<span id="epochsValue">50</span>
</div>
</div>
<div class="mode-toggle">
<button id="trainMode" class="active">Training Mode</button>
<button id="testMode">Testing Mode</button>
</div>
<div class="controls">
<button id="startBtn">🚀 Start Training</button>
<button id="stopBtn" disabled>⏹️ Stop</button>
<button id="resetBtn">🔄 Reset</button>
</div>
<div class="visualization">
<div class="network-container">
<svg id="networkSvg"></svg>
<div class="loss-chart">
<div class="chart-title">📊 Training & Validation Loss</div>
<canvas id="lossChart" width="550" height="180"></canvas>
</div>
</div>
<div class="info-panel">
<div class="epoch-info">
<div>Epoch: <span id="currentEpoch">0</span> / <span id="totalEpochs">50</span></div>
<div class="progress-bar">
<div class="progress-fill" id="progressFill"></div>
</div>
</div>
<div class="stats">
<div class="stat-item">
<span>Mode:</span>
<span id="currentMode">Training</span>
</div>
<div class="stat-item">
<span>Active Neurons:</span>
<span id="activeNeurons">12/12</span>
</div>
<div class="stat-item">
<span>Dropped Neurons:</span>
<span id="droppedNeurons">0/12</span>
</div>
<div class="stat-item">
<span>Train Loss:</span>
<span id="trainLoss">1.000</span>
</div>
<div class="stat-item">
<span>Val Loss:</span>
<span id="valLoss">1.000</span>
</div>
<div class="stat-item">
<span>Loss:</span>
<span id="currentLoss">1.000</span>
</div>
<div class="stat-item">
<span>Accuracy:</span>
<span id="currentAccuracy">50%</span>
</div>
</div>
<div class="legend">
<h3>Legend:</h3>
<div class="legend-item">
<div class="legend-color" style="background: #4CAF50;"></div>
<span>Active Neuron</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #f44336; opacity: 0.3;"></div>
<span>Dropped Neuron</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #2196F3;"></div>
<span>Input Layer</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #FF9800;"></div>
<span>Output Layer</span>
</div>
</div>
<div style="margin-top: 20px; font-size: 14px; line-height: 1.4;">
<strong>How Dropout Works:</strong><br>
• During training, neurons are randomly "turned off"<br>
• Prevents over-reliance on specific neurons<br>
• Reduces overfitting and improves generalization<br>
• During testing, all neurons are active
</div>
</div>
</div>
</div>
<script>
class DropoutNeuralNetwork {
constructor() {
this.svg = document.getElementById('networkSvg');
this.lossCanvas = document.getElementById('lossChart');
this.lossCtx = this.lossCanvas.getContext('2d');
this.layers = [3, 4, 4, 2]; // Input, Hidden1, Hidden2, Output
this.neurons = [];
this.connections = [];
this.isTraining = false;
this.currentEpoch = 0;
this.totalEpochs = 50;
this.mode = 'training'; // 'training' or 'testing'
this.trainLoss = 1.0;
this.valLoss = 1.0;
this.accuracy = 0.5;
// Loss history for plotting
this.trainLossHistory = [];
this.valLossHistory = [];
this.initializeNetwork();
this.setupEventListeners();
this.updateDisplay();
this.initializeLossChart();
}
initializeNetwork() {
const svgRect = this.svg.getBoundingClientRect();
const width = 600;
const height = 500;
this.svg.setAttribute('viewBox', `0 0 ${width} ${height}`);
// Clear existing content
this.svg.innerHTML = '';
this.neurons = [];
this.connections = [];
// Create neurons
var layerSpacing = width / (this.layers.length + 1);
for (var layerIdx = 0; layerIdx < this.layers.length; layerIdx++) {
var layerSize = this.layers[layerIdx];
var neuronSpacing = height / (layerSize + 1);
for (var neuronIdx = 0; neuronIdx < layerSize; neuronIdx++) {
var x = layerSpacing * (layerIdx + 1);
var y = neuronSpacing * (neuronIdx + 1);
var neuron = {
id: 'neuron_' + layerIdx + '_' + neuronIdx,
layer: layerIdx,
index: neuronIdx,
x: x,
y: y,
active: true,
element: null
};
this.neurons.push(neuron);
}
}
// Create connections
for (var layerIdx2 = 0; layerIdx2 < this.layers.length - 1; layerIdx2++) {
var currentLayerNeurons = this.neurons.filter(function(n) { return n.layer === layerIdx2; });
var nextLayerNeurons = this.neurons.filter(function(n) { return n.layer === layerIdx2 + 1; });
for (var i = 0; i < currentLayerNeurons.length; i++) {
var currentNeuron = currentLayerNeurons[i];
for (var j = 0; j < nextLayerNeurons.length; j++) {
var nextNeuron = nextLayerNeurons[j];
var connection = {
id: 'conn_' + currentNeuron.id + '_' + nextNeuron.id,
from: currentNeuron,
to: nextNeuron,
weight: Math.random() * 2 - 1,
element: null
};
this.connections.push(connection);
}
}
}
this.renderNetwork();
}
renderNetwork() {
// Render connections first (so they appear behind neurons)
for (let conn of this.connections) {
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
line.setAttribute('x1', conn.from.x);
line.setAttribute('y1', conn.from.y);
line.setAttribute('x2', conn.to.x);
line.setAttribute('y2', conn.to.y);
line.setAttribute('stroke', this.getConnectionColor(conn));
line.setAttribute('stroke-width', Math.abs(conn.weight) * 2 + 0.5);
line.setAttribute('opacity', this.getConnectionOpacity(conn));
line.setAttribute('id', conn.id);
this.svg.appendChild(line);
conn.element = line;
}
// Render neurons
for (let neuron of this.neurons) {
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
circle.setAttribute('cx', neuron.x);
circle.setAttribute('cy', neuron.y);
circle.setAttribute('r', 15);
circle.setAttribute('fill', this.getNeuronColor(neuron));
circle.setAttribute('stroke', '#fff');
circle.setAttribute('stroke-width', 2);
circle.setAttribute('opacity', neuron.active ? 1 : 0.3);
circle.setAttribute('id', neuron.id);
// Add pulsing animation for active neurons
if (neuron.active && this.isTraining) {
const animate = document.createElementNS('http://www.w3.org/2000/svg', 'animate');
animate.setAttribute('attributeName', 'r');
animate.setAttribute('values', '15;18;15');
animate.setAttribute('dur', '2s');
animate.setAttribute('repeatCount', 'indefinite');
circle.appendChild(animate);
}
this.svg.appendChild(circle);
neuron.element = circle;
// Add layer labels
if (neuron.index === 0) {
const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
label.setAttribute('x', neuron.x);
label.setAttribute('y', neuron.y - 40);
label.setAttribute('text-anchor', 'middle');
label.setAttribute('fill', 'white');
label.setAttribute('font-size', '14');
label.setAttribute('font-weight', 'bold');
const layerNames = ['Input', 'Hidden 1', 'Hidden 2', 'Output'];
label.textContent = layerNames[neuron.layer];
this.svg.appendChild(label);
}
}
}
initializeLossChart() {
this.lossCtx.fillStyle = '#1a1a2e';
this.lossCtx.fillRect(0, 0, this.lossCanvas.width, this.lossCanvas.height);
// Draw axes
this.lossCtx.strokeStyle = '#ffffff';
this.lossCtx.lineWidth = 1;
this.lossCtx.beginPath();
// Y-axis
this.lossCtx.moveTo(50, 20);
this.lossCtx.lineTo(50, 160);
// X-axis
this.lossCtx.moveTo(50, 160);
this.lossCtx.lineTo(530, 160);
this.lossCtx.stroke();
// Labels
this.lossCtx.fillStyle = '#ffffff';
this.lossCtx.font = '12px Arial';
this.lossCtx.fillText('Loss', 10, 15);
this.lossCtx.fillText('Epochs', 490, 175);
// Y-axis labels
for (var i = 0; i <= 10; i++) {
var y = 160 - (i * 14);
var value = (i / 10).toFixed(1);
this.lossCtx.fillText(value, 15, y + 3);
// Grid lines
this.lossCtx.strokeStyle = '#333';
this.lossCtx.lineWidth = 0.5;
this.lossCtx.beginPath();
this.lossCtx.moveTo(50, y);
this.lossCtx.lineTo(530, y);
this.lossCtx.stroke();
}
}
updateLossChart() {
// Clear and redraw background
this.initializeLossChart();
if (this.trainLossHistory.length < 2) return;
var maxEpochs = this.totalEpochs;
var chartWidth = 480; // 530 - 50
var chartHeight = 140; // 160 - 20
// Draw training loss
this.lossCtx.strokeStyle = '#ff6b6b';
this.lossCtx.lineWidth = 2;
this.lossCtx.beginPath();
for (var i = 0; i < this.trainLossHistory.length; i++) {
var x = 50 + (i / maxEpochs) * chartWidth;
var y = 160 - (this.trainLossHistory[i] * chartHeight);
if (i === 0) {
this.lossCtx.moveTo(x, y);
} else {
this.lossCtx.lineTo(x, y);
}
}
this.lossCtx.stroke();
// Draw validation loss
this.lossCtx.strokeStyle = '#4ecdc4';
this.lossCtx.lineWidth = 2;
this.lossCtx.beginPath();
for (var j = 0; j < this.valLossHistory.length; j++) {
var x2 = 50 + (j / maxEpochs) * chartWidth;
var y2 = 160 - (this.valLossHistory[j] * chartHeight);
if (j === 0) {
this.lossCtx.moveTo(x2, y2);
} else {
this.lossCtx.lineTo(x2, y2);
}
}
this.lossCtx.stroke();
// Draw legend
this.lossCtx.fillStyle = '#ff6b6b';
this.lossCtx.fillRect(350, 25, 15, 3);
this.lossCtx.fillStyle = '#ffffff';
this.lossCtx.font = '12px Arial';
this.lossCtx.fillText('Train Loss', 370, 30);
this.lossCtx.fillStyle = '#4ecdc4';
this.lossCtx.fillRect(450, 25, 15, 3);
this.lossCtx.fillStyle = '#ffffff';
this.lossCtx.fillText('Val Loss', 470, 30);
}
getNeuronColor(neuron) {
if (neuron.layer === 0) return '#2196F3'; // Input - Blue
if (neuron.layer === this.layers.length - 1) return '#FF9800'; // Output - Orange
return neuron.active ? '#4CAF50' : '#f44336'; // Hidden - Green/Red
}
getConnectionColor(conn) {
const opacity = this.getConnectionOpacity(conn);
if (opacity < 0.5) return '#666';
return conn.weight > 0 ? '#4CAF50' : '#f44336';
}
getConnectionOpacity(conn) {
if (this.mode === 'testing') return 1;
// Connection is active if both neurons are active
const bothActive = conn.from.active && conn.to.active;
return bothActive ? 1 : 0.2;
}
applyDropout() {
if (this.mode === 'testing') {
// In testing mode, all neurons are active
for (var i = 0; i < this.neurons.length; i++) {
this.neurons[i].active = true;
}
return;
}
var dropoutRate = parseFloat(document.getElementById('dropoutRate').value);
// Apply dropout independently per hidden layer
for (var layerIdx = 1; layerIdx < this.layers.length - 1; layerIdx++) {
// Get all neurons in this hidden layer
var layerNeurons = this.neurons.filter(function(n) {
return n.layer === layerIdx;
});
// Apply dropout to each neuron independently
var activeCount = 0;
for (var i = 0; i < layerNeurons.length; i++) {
layerNeurons[i].active = Math.random() > dropoutRate;
if (layerNeurons[i].active) activeCount++;
}
// Safety check: ensure at least one neuron remains active per layer
// This prevents complete layer dropout which would break information flow
if (activeCount === 0 && layerNeurons.length > 0) {
// Randomly activate one neuron to maintain connectivity
var randomIdx = Math.floor(Math.random() * layerNeurons.length);
layerNeurons[randomIdx].active = true;
}
}
// Input and output layers are always active
for (var j = 0; j < this.neurons.length; j++) {
var neuron = this.neurons[j];
if (neuron.layer === 0 || neuron.layer === this.layers.length - 1) {
neuron.active = true;
}
}
}
updateNetwork() {
for (var i = 0; i < this.neurons.length; i++) {
var neuron = this.neurons[i];
if (neuron.element) {
neuron.element.setAttribute('fill', this.getNeuronColor(neuron));
neuron.element.setAttribute('opacity', neuron.active ? 1 : 0.3);
}
}
for (var j = 0; j < this.connections.length; j++) {
var conn = this.connections[j];
if (conn.element) {
conn.element.setAttribute('stroke', this.getConnectionColor(conn));
conn.element.setAttribute('opacity', this.getConnectionOpacity(conn));
}
}
}
simulateTraining() {
if (!this.isTraining) return;
// Apply dropout
this.applyDropout();
// Simulate loss decrease and accuracy increase with realistic patterns
var progress = this.currentEpoch / this.totalEpochs;
// Training loss decreases faster initially, then plateaus
var baseLoss = Math.max(0.05, 1.0 - progress * 0.85);
this.trainLoss = baseLoss + Math.random() * 0.08 - 0.04;
// Validation loss decreases slower and may fluctuate more
var valBaseLoss = Math.max(0.08, 1.0 - progress * 0.75);
this.valLoss = valBaseLoss + Math.random() * 0.12 - 0.06;
// Sometimes validation loss increases slightly (overfitting simulation)
if (progress > 0.6 && Math.random() < 0.3) {
this.valLoss += 0.05;
}
// Accuracy increases
this.accuracy = Math.min(0.95, 0.5 + progress * 0.4 + Math.random() * 0.05);
// Store loss history
this.trainLossHistory.push(Math.max(0, Math.min(1, this.trainLoss)));
this.valLossHistory.push(Math.max(0, Math.min(1, this.valLoss)));
// Update network visualization
this.updateNetwork();
this.updateLossChart();
this.updateDisplay();
this.currentEpoch++;
if (this.currentEpoch <= this.totalEpochs) {
setTimeout(() => this.simulateTraining(), 500);
} else {
this.stopTraining();
}
}
startTraining() {
this.isTraining = true;
this.currentEpoch = 0;
this.totalEpochs = parseInt(document.getElementById('epochs').value);
this.trainLossHistory = [];
this.valLossHistory = [];
document.getElementById('startBtn').disabled = true;
document.getElementById('stopBtn').disabled = false;
this.simulateTraining();
}
stopTraining() {
this.isTraining = false;
document.getElementById('startBtn').disabled = false;
document.getElementById('stopBtn').disabled = true;
// Reset all neurons to active state after training
for (var i = 0; i < this.neurons.length; i++) {
this.neurons[i].active = true;
}
this.updateNetwork();
this.updateDisplay();
}
reset() {
this.stopTraining();
this.currentEpoch = 0;
this.trainLoss = 1.0;
this.valLoss = 1.0;
this.accuracy = 0.5;
this.trainLossHistory = [];
this.valLossHistory = [];
// Reset all neurons to active state
for (var i = 0; i < this.neurons.length; i++) {
this.neurons[i].active = true;
}
this.updateNetwork();
this.initializeLossChart();
this.updateDisplay();
}
setMode(mode) {
this.mode = mode;
this.applyDropout();
this.updateNetwork();
this.updateDisplay();
}
updateDisplay() {
document.getElementById('currentEpoch').textContent = this.currentEpoch;
document.getElementById('totalEpochs').textContent = this.totalEpochs;
document.getElementById('currentMode').textContent = this.mode === 'training' ? 'Training' : 'Testing';
document.getElementById('trainLoss').textContent = this.trainLoss.toFixed(3);
document.getElementById('valLoss').textContent = this.valLoss.toFixed(3);
document.getElementById('currentLoss').textContent = ((this.trainLoss + this.valLoss) / 2).toFixed(3);
document.getElementById('currentAccuracy').textContent = (this.accuracy * 100).toFixed(1) + '%';
// Count active and dropped neurons (excluding input/output layers)
var hiddenNeurons = this.neurons.filter(function(n) {
return n.layer > 0 && n.layer < this.layers.length - 1;
}.bind(this));
var activeHidden = hiddenNeurons.filter(function(n) { return n.active; }).length;
var totalHidden = hiddenNeurons.length;
document.getElementById('activeNeurons').textContent = `${activeHidden}/${totalHidden}`;
document.getElementById('droppedNeurons').textContent = `${totalHidden - activeHidden}/${totalHidden}`;
// Update progress bar
var progress = this.totalEpochs > 0 ? (this.currentEpoch / this.totalEpochs) * 100 : 0;
document.getElementById('progressFill').style.width = progress + '%';
}
setupEventListeners() {
// Control sliders
document.getElementById('dropoutRate').addEventListener('input', (e) => {
document.getElementById('dropoutValue').textContent = e.target.value;
});
document.getElementById('learningRate').addEventListener('input', (e) => {
document.getElementById('learningValue').textContent = e.target.value;
});
document.getElementById('epochs').addEventListener('input', (e) => {
document.getElementById('epochsValue').textContent = e.target.value;
});
// Buttons
document.getElementById('startBtn').addEventListener('click', () => {
this.startTraining();
});
document.getElementById('stopBtn').addEventListener('click', () => {
this.stopTraining();
});
document.getElementById('resetBtn').addEventListener('click', () => {
this.reset();
});
// Mode toggle
document.getElementById('trainMode').addEventListener('click', () => {
document.getElementById('trainMode').classList.add('active');
document.getElementById('testMode').classList.remove('active');
this.setMode('training');
});
document.getElementById('testMode').addEventListener('click', () => {
document.getElementById('testMode').classList.add('active');
document.getElementById('trainMode').classList.remove('active');
this.setMode('testing');
});
}
}
// Initialize the application
const network = new DropoutNeuralNetwork();
</script>
</body>
</html>