File size: 12,065 Bytes
5b8c940
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
<!DOCTYPE html>
<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>