Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Softmax Function Visualizer</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .slider-container { | |
| transition: all 0.3s ease; | |
| } | |
| .slider-container:hover { | |
| transform: translateY(-3px); | |
| } | |
| .bar-container { | |
| transition: all 0.5s ease; | |
| } | |
| .glow { | |
| box-shadow: 0 0 15px rgba(59, 130, 246, 0.5); | |
| } | |
| .input-value { | |
| min-width: 60px; | |
| text-align: center; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <div class="max-w-4xl mx-auto"> | |
| <!-- Header --> | |
| <div class="text-center mb-8"> | |
| <h1 class="text-4xl font-bold text-blue-600 mb-2">Softmax Function Visualizer</h1> | |
| <p class="text-gray-600 text-lg"> | |
| Interactive visualization of the softmax activation function | |
| </p> | |
| </div> | |
| <!-- Explanation Card --> | |
| <div class="bg-white rounded-xl shadow-md p-6 mb-8"> | |
| <div class="flex items-start"> | |
| <div class="bg-blue-100 p-3 rounded-full mr-4"> | |
| <i class="fas fa-info-circle text-blue-600 text-xl"></i> | |
| </div> | |
| <div> | |
| <h2 class="text-xl font-semibold text-gray-800 mb-2">About Softmax</h2> | |
| <p class="text-gray-600 mb-2"> | |
| The softmax function converts a vector of real numbers into a probability distribution. | |
| It's commonly used in machine learning for multi-class classification. | |
| </p> | |
| <p class="text-gray-600"> | |
| Formula: <code class="bg-gray-100 px-2 py-1 rounded">σ(z)_i = e^{z_i} / Σ_j e^{z_j}</code> | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Controls --> | |
| <div class="bg-white rounded-xl shadow-md p-6 mb-8"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h2 class="text-xl font-semibold text-gray-800">Input Values</h2> | |
| <div class="flex space-x-2"> | |
| <button id="add-input" class="bg-green-500 hover:bg-green-600 text-white px-3 py-1 rounded-lg transition"> | |
| <i class="fas fa-plus mr-1"></i> Add | |
| </button> | |
| <button id="reset" class="bg-gray-500 hover:bg-gray-600 text-white px-3 py-1 rounded-lg transition"> | |
| <i class="fas fa-sync-alt mr-1"></i> Reset | |
| </button> | |
| </div> | |
| </div> | |
| <div id="sliders-container" class="space-y-4"> | |
| <!-- Sliders will be added here dynamically --> | |
| </div> | |
| </div> | |
| <!-- Results --> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8"> | |
| <!-- Input Values --> | |
| <div class="bg-white rounded-xl shadow-md p-6"> | |
| <h2 class="text-xl font-semibold text-gray-800 mb-4">Input Values</h2> | |
| <div id="input-values" class="space-y-2"> | |
| <!-- Input values will be displayed here --> | |
| </div> | |
| </div> | |
| <!-- Softmax Output --> | |
| <div class="bg-white rounded-xl shadow-md p-6"> | |
| <h2 class="text-xl font-semibold text-gray-800 mb-4">Softmax Output</h2> | |
| <div id="output-values" class="space-y-2"> | |
| <!-- Output values will be displayed here --> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Summary --> | |
| <div class="bg-blue-50 rounded-xl shadow-md p-6"> | |
| <div class="flex items-start"> | |
| <div class="bg-blue-100 p-3 rounded-full mr-4"> | |
| <i class="fas fa-lightbulb text-blue-600 text-xl"></i> | |
| </div> | |
| <div> | |
| <h2 class="text-xl font-semibold text-gray-800 mb-2">Key Insights</h2> | |
| <ul class="list-disc pl-5 text-gray-600 space-y-1"> | |
| <li>Softmax converts input values to probabilities that sum to 1</li> | |
| <li>Higher input values get exponentially larger probabilities</li> | |
| <li>The function is sensitive to differences between values</li> | |
| <li>Adding a constant to all inputs doesn't change the output</li> | |
| </ul> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Initialize with 3 inputs | |
| let inputValues = [1, 2, 3]; | |
| // DOM elements | |
| const slidersContainer = document.getElementById('sliders-container'); | |
| const inputValuesContainer = document.getElementById('input-values'); | |
| const outputValuesContainer = document.getElementById('output-values'); | |
| const addInputBtn = document.getElementById('add-input'); | |
| const resetBtn = document.getElementById('reset'); | |
| // Initialize the app | |
| function init() { | |
| renderSliders(); | |
| updateVisualization(); | |
| } | |
| // Render sliders based on current input values | |
| function renderSliders() { | |
| slidersContainer.innerHTML = ''; | |
| inputValuesContainer.innerHTML = ''; | |
| inputValues.forEach((value, index) => { | |
| // Create slider container | |
| const sliderContainer = document.createElement('div'); | |
| sliderContainer.className = 'slider-container bg-gray-50 p-4 rounded-lg'; | |
| // Create label | |
| const label = document.createElement('div'); | |
| label.className = 'flex justify-between items-center mb-2'; | |
| label.innerHTML = ` | |
| <span class="font-medium text-gray-700">Input ${index + 1}</span> | |
| <div class="flex items-center"> | |
| <span class="input-value bg-blue-100 text-blue-800 font-mono px-2 py-1 rounded mr-2">${value.toFixed(2)}</span> | |
| <button class="delete-btn text-red-500 hover:text-red-700 transition" data-index="${index}"> | |
| <i class="fas fa-trash"></i> | |
| </button> | |
| </div> | |
| `; | |
| // Create slider | |
| const slider = document.createElement('input'); | |
| slider.type = 'range'; | |
| slider.min = '-5'; | |
| slider.max = '5'; | |
| slider.step = '0.1'; | |
| slider.value = value; | |
| slider.className = 'w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer'; | |
| slider.dataset.index = index; | |
| // Add event listener | |
| slider.addEventListener('input', function() { | |
| const index = parseInt(this.dataset.index); | |
| const value = parseFloat(this.value); | |
| inputValues[index] = value; | |
| // Update the displayed value | |
| this.parentNode.querySelector('.input-value').textContent = value.toFixed(2); | |
| updateVisualization(); | |
| }); | |
| // Add delete button event listener | |
| const deleteBtn = label.querySelector('.delete-btn'); | |
| deleteBtn.addEventListener('click', function() { | |
| if (inputValues.length > 1) { | |
| const index = parseInt(this.dataset.index); | |
| inputValues.splice(index, 1); | |
| renderSliders(); | |
| updateVisualization(); | |
| } else { | |
| alert("You need at least one input!"); | |
| } | |
| }); | |
| // Assemble slider container | |
| sliderContainer.appendChild(label); | |
| sliderContainer.appendChild(slider); | |
| slidersContainer.appendChild(sliderContainer); | |
| }); | |
| } | |
| // Calculate softmax function | |
| function softmax(values) { | |
| const max = Math.max(...values); | |
| const exps = values.map(x => Math.exp(x - max)); // Numerical stability | |
| const sumExps = exps.reduce((a, b) => a + b, 0); | |
| return exps.map(exp => exp / sumExps); | |
| } | |
| // Update all visualizations | |
| function updateVisualization() { | |
| const softmaxOutput = softmax(inputValues); | |
| // Update input values display | |
| inputValuesContainer.innerHTML = inputValues.map((value, index) => ` | |
| <div class="flex justify-between items-center py-2 px-3 rounded ${index % 2 === 0 ? 'bg-gray-50' : ''}"> | |
| <span class="font-medium">Input ${index + 1}</span> | |
| <span class="font-mono text-blue-600">${value.toFixed(4)}</span> | |
| </div> | |
| `).join(''); | |
| // Update output values display | |
| outputValuesContainer.innerHTML = softmaxOutput.map((value, index) => { | |
| const percent = (value * 100).toFixed(2); | |
| return ` | |
| <div class="bar-container"> | |
| <div class="flex justify-between items-center mb-1"> | |
| <span class="font-medium">Output ${index + 1}</span> | |
| <span class="font-mono text-green-600">${value.toFixed(4)} <span class="text-gray-500">(${percent}%)</span></span> | |
| </div> | |
| <div class="w-full bg-gray-200 rounded-full h-2.5"> | |
| <div class="bg-gradient-to-r from-blue-400 to-blue-600 h-2.5 rounded-full" | |
| style="width: ${percent}%"></div> | |
| </div> | |
| </div> | |
| `; | |
| }).join(''); | |
| } | |
| // Event listeners | |
| addInputBtn.addEventListener('click', function() { | |
| if (inputValues.length < 8) { | |
| // Add a new input with value similar to the last one | |
| const newValue = inputValues.length > 0 ? inputValues[inputValues.length - 1] : 0; | |
| inputValues.push(newValue); | |
| renderSliders(); | |
| updateVisualization(); | |
| } else { | |
| alert("Maximum of 8 inputs reached!"); | |
| } | |
| }); | |
| resetBtn.addEventListener('click', function() { | |
| inputValues = [1, 2, 3]; | |
| renderSliders(); | |
| updateVisualization(); | |
| }); | |
| // Initialize the app | |
| init(); | |
| }); | |
| </script> | |
| </body> | |
| </html> |