IotaCluster commited on
Commit
0ea77fd
·
verified ·
1 Parent(s): 39822ed

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +814 -18
index.html CHANGED
@@ -1,19 +1,815 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Neural Network Visual Architect</title>
7
+ <!-- Third-party libraries for machine learning and charting -->
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/tensorflow/4.10.0/tf.min.js"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
10
+ <style>
11
+ /* General Styling and Resets */
12
+ :root {
13
+ --primary-color: #6a82fb;
14
+ --secondary-color: #fc5c7d;
15
+ --bg-color: #f4f7f6;
16
+ --panel-bg: rgba(255, 255, 255, 0.9);
17
+ --text-color: #333;
18
+ --shadow-light: rgba(0, 0, 0, 0.05);
19
+ --shadow-dark: rgba(0, 0, 0, 0.1);
20
+ }
21
+
22
+ * {
23
+ margin: 0;
24
+ padding: 0;
25
+ box-sizing: border-box;
26
+ }
27
+
28
+ body {
29
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
30
+ background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
31
+ min-height: 100vh;
32
+ color: var(--text-color);
33
+ overflow-x: hidden;
34
+ }
35
+
36
+ /* Main Layout */
37
+ .container {
38
+ max-width: 1800px;
39
+ margin: 0 auto;
40
+ padding: 20px;
41
+ }
42
+
43
+ .header {
44
+ text-align: center;
45
+ margin-bottom: 30px;
46
+ color: white;
47
+ }
48
+
49
+ .header h1 {
50
+ font-size: 2.8rem;
51
+ font-weight: 700;
52
+ margin-bottom: 10px;
53
+ text-shadow: 0 4px 15px var(--shadow-dark);
54
+ }
55
+
56
+ .header p {
57
+ font-size: 1.2rem;
58
+ opacity: 0.9;
59
+ }
60
+
61
+ .main-layout {
62
+ display: grid;
63
+ grid-template-columns: 320px 1fr 420px;
64
+ gap: 20px;
65
+ height: calc(100vh - 150px);
66
+ }
67
+
68
+ .panel {
69
+ background: var(--panel-bg);
70
+ backdrop-filter: blur(15px);
71
+ border-radius: 20px;
72
+ padding: 25px;
73
+ box-shadow: 0 15px 30px var(--shadow-dark);
74
+ border: 1px solid rgba(255, 255, 255, 0.2);
75
+ overflow-y: auto;
76
+ display: flex;
77
+ flex-direction: column;
78
+ }
79
+
80
+ .panel h2 {
81
+ font-size: 1.4rem;
82
+ margin-bottom: 20px;
83
+ color: #4a5568;
84
+ display: flex;
85
+ align-items: center;
86
+ gap: 10px;
87
+ }
88
+
89
+ /* Layer Palette (Left Panel) */
90
+ .layer-palette .layer-template {
91
+ padding: 15px;
92
+ border-radius: 12px;
93
+ cursor: grab;
94
+ transition: all 0.3s ease;
95
+ text-align: center;
96
+ user-select: none;
97
+ margin-bottom: 15px;
98
+ }
99
+
100
+ .layer-template:hover {
101
+ transform: translateY(-3px);
102
+ box-shadow: 0 8px 25px var(--shadow-dark);
103
+ }
104
+
105
+ .layer-template:active {
106
+ cursor: grabbing;
107
+ transform: scale(0.95);
108
+ }
109
+
110
+ .input-layer-bg { background: linear-gradient(145deg, #e0f7fa, #b2ebf2); border: 2px solid #4dd0e1; }
111
+ .dense-layer-bg { background: linear-gradient(145deg, #ffcdd2, #ef9a9a); border: 2px solid #e57373; }
112
+ .output-layer-bg { background: linear-gradient(145deg, #c8e6c9, #a5d6a7); border: 2px solid #81c784; }
113
+
114
+ /* Layer Configuration */
115
+ .layer-config {
116
+ margin-top: 20px;
117
+ padding-top: 20px;
118
+ border-top: 1px solid #e0e0e0;
119
+ }
120
+ .config-group { margin-bottom: 15px; }
121
+ .config-group label { display: block; font-size: 0.9rem; margin-bottom: 8px; color: #4a5568; font-weight: 500; }
122
+ .config-group input, .config-group select, .config-group textarea {
123
+ width: 100%;
124
+ padding: 10px;
125
+ border: 1px solid #ddd;
126
+ border-radius: 8px;
127
+ font-size: 0.9rem;
128
+ font-family: inherit;
129
+ }
130
+
131
+ /* Architecture Canvas (Center Panel) */
132
+ .architecture-canvas {
133
+ position: relative;
134
+ background: rgba(0, 0, 0, 0.1) url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20"><circle cx="1" cy="1" r="1" fill="rgba(255,255,255,0.1)"/></svg>');
135
+ border: 2px dashed rgba(255, 255, 255, 0.4);
136
+ border-radius: 15px;
137
+ overflow: hidden;
138
+ height: 100%;
139
+ }
140
+
141
+ .drop-zone-text {
142
+ position: absolute;
143
+ top: 50%;
144
+ left: 50%;
145
+ transform: translate(-50%, -50%);
146
+ text-align: center;
147
+ color: rgba(255, 255, 255, 0.8);
148
+ font-size: 1.3rem;
149
+ pointer-events: none;
150
+ }
151
+
152
+ /* Individual Layer Instances on Canvas */
153
+ .layer-instance {
154
+ position: absolute;
155
+ padding: 10px;
156
+ border-radius: 12px;
157
+ cursor: move;
158
+ min-width: 80px;
159
+ text-align: center;
160
+ user-select: none;
161
+ transition: box-shadow 0.2s ease, transform 0.2s ease;
162
+ backdrop-filter: blur(10px);
163
+ display: flex;
164
+ flex-direction: column;
165
+ align-items: center;
166
+ gap: 5px;
167
+ }
168
+ .layer-instance.selected {
169
+ box-shadow: 0 0 0 3px var(--primary-color);
170
+ }
171
+
172
+ .layer-header { font-weight: bold; font-size: 0.9rem; }
173
+ .layer-details { font-size: 0.75rem; opacity: 0.8; }
174
+
175
+ .neuron-column {
176
+ display: flex;
177
+ flex-direction: column;
178
+ align-items: center;
179
+ gap: 4px; /* Space between neurons */
180
+ margin-top: 5px;
181
+ }
182
+
183
+ .neuron {
184
+ width: 12px;
185
+ height: 12px;
186
+ border-radius: 50%;
187
+ background-color: rgba(255, 255, 255, 0.7);
188
+ border: 1px solid rgba(0, 0, 0, 0.2);
189
+ }
190
+
191
+ .delete-btn {
192
+ position: absolute;
193
+ top: -10px; right: -10px;
194
+ width: 24px; height: 24px;
195
+ background: #e53e3e; color: white;
196
+ border: none; border-radius: 50%;
197
+ cursor: pointer; font-size: 14px;
198
+ display: flex; align-items: center; justify-content: center;
199
+ opacity: 0; transition: opacity 0.2s;
200
+ z-index: 10;
201
+ }
202
+ .layer-instance:hover .delete-btn { opacity: 1; }
203
+
204
+ /* Connections */
205
+ #connection-svg {
206
+ position: absolute;
207
+ top: 0; left: 0;
208
+ width: 100%; height: 100%;
209
+ pointer-events: none;
210
+ z-index: -1;
211
+ }
212
+ .connection-line {
213
+ stroke: rgba(255, 255, 255, 0.5);
214
+ stroke-width: 1.5;
215
+ }
216
+
217
+ /* Training Panel (Right Panel) */
218
+ .training-panel { display: flex; flex-direction: column; }
219
+ .training-panel h3 {
220
+ font-size: 1.1rem;
221
+ margin-top: 15px;
222
+ margin-bottom: 10px;
223
+ padding-bottom: 5px;
224
+ border-bottom: 1px solid #eee;
225
+ }
226
+
227
+ .train-btn, .validate-btn, .clear-btn, .load-data-btn {
228
+ border: none;
229
+ padding: 12px 20px;
230
+ border-radius: 10px;
231
+ cursor: pointer;
232
+ font-size: 1rem;
233
+ font-weight: 600;
234
+ transition: all 0.3s ease;
235
+ margin-top: 10px;
236
+ color: white;
237
+ }
238
+
239
+ .train-btn { background: linear-gradient(45deg, #4CAF50, #81C784); }
240
+ .train-btn:hover { transform: translateY(-2px); box-shadow: 0 4px 15px rgba(76, 175, 80, 0.4); }
241
+ .train-btn:disabled { background: #ccc; cursor: not-allowed; transform: none; box-shadow: none; }
242
+
243
+ .validate-btn { background: linear-gradient(45deg, #2196F3, #64B5F6); }
244
+ .validate-btn:hover { transform: translateY(-2px); box-shadow: 0 4px 15px rgba(33, 150, 243, 0.4); }
245
+ .validate-btn:disabled { background: #ccc; cursor: not-allowed; transform: none; box-shadow: none; }
246
+
247
+ .clear-btn { background: linear-gradient(45deg, #f44336, #e57373); padding: 10px 18px; }
248
+ .load-data-btn { background: linear-gradient(45deg, var(--primary-color), #899cfb); padding: 10px 18px; font-size: 0.9rem; }
249
+
250
+ .chart-container {
251
+ margin-top: 15px;
252
+ padding-top: 15px;
253
+ border-top: 1px solid #eee;
254
+ height: 220px;
255
+ min-height: 220px;
256
+ }
257
+
258
+ /* Data Input Methods */
259
+ .input-method-selector { display: flex; gap: 5px; margin-bottom: 15px; }
260
+ .method-btn {
261
+ flex: 1; padding: 8px 12px; border: 1px solid #e2e8f0;
262
+ background: white; border-radius: 6px; cursor: pointer;
263
+ font-size: 0.85rem; transition: all 0.2s ease;
264
+ }
265
+ .method-btn.active { background: var(--primary-color); color: white; border-color: var(--primary-color); }
266
+
267
+ /* Metrics Display */
268
+ .metrics { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 15px; }
269
+ .metric { text-align: center; padding: 10px; background: rgba(0,0,0,0.05); border-radius: 8px; }
270
+ .metric-value { font-size: 1.2rem; font-weight: 700; color: var(--primary-color); }
271
+ .metric-label { font-size: 0.8rem; color: #718096; }
272
+
273
+ /* Status Messages */
274
+ .status {
275
+ margin-top: 10px; padding: 12px;
276
+ border-radius: 8px; font-size: 0.9rem;
277
+ text-align: center; display: none;
278
+ }
279
+ .status.success { background: rgba(76, 175, 80, 0.15); color: #388E3C; }
280
+ .status.error { background: rgba(244, 67, 54, 0.15); color: #D32F2F; }
281
+
282
+ /* Progress Bar */
283
+ .progress-bar {
284
+ width: 100%; height: 8px; background: #e0e0e0;
285
+ border-radius: 4px; overflow: hidden; margin: 10px 0 5px 0;
286
+ }
287
+ .progress-fill {
288
+ height: 100%; background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
289
+ width: 0%; transition: width 0.3s ease;
290
+ }
291
+
292
+ /* Responsive Design */
293
+ @media (max-width: 1200px) {
294
+ .main-layout {
295
+ grid-template-columns: 1fr;
296
+ grid-template-rows: auto 500px auto;
297
+ height: auto;
298
+ }
299
+ }
300
+ </style>
301
+ </head>
302
+ <body>
303
+ <div class="container">
304
+ <header class="header">
305
+ <h1>🧠 Neural Network Visual Architect</h1>
306
+ <p>Build, train, and visualize neural networks interactively.</p>
307
+ </header>
308
+
309
+ <div class="main-layout">
310
+ <!-- Left Panel: Layer Palette & Configuration -->
311
+ <div class="panel">
312
+ <h2><span class="icon">🧩</span>Layer Palette</h2>
313
+ <div class="layer-palette">
314
+ <div class="layer-template input-layer-bg" draggable="true" data-type="input"><h4>Input Layer</h4><p>Starting point</p></div>
315
+ <div class="layer-template dense-layer-bg" draggable="true" data-type="dense"><h4>Dense Layer</h4><p>Hidden layer</p></div>
316
+ <div class="layer-template output-layer-bg" draggable="true" data-type="output"><h4>Output Layer</h4><p>Prediction layer</p></div>
317
+ </div>
318
+ <div class="layer-config" id="layerConfig" style="display: none;">
319
+ <h3>Selected Layer Settings</h3>
320
+ <div class="config-group"><label for="layerUnits">Neurons:</label><input type="number" id="layerUnits" value="8" min="1" max="16"></div>
321
+ <div class="config-group"><label for="layerActivation">Activation Function:</label><select id="layerActivation"><option value="relu">ReLU</option><option value="sigmoid">Sigmoid</option><option value="tanh">Tanh</option><option value="linear">Linear</option></select></div>
322
+ </div>
323
+ <button class="clear-btn" onclick="clearArchitecture()" style="margin-top: auto;">Clear Architecture</button>
324
+ </div>
325
+
326
+ <!-- Center Panel: Architecture Canvas -->
327
+ <div class="architecture-canvas" id="architectureCanvas">
328
+ <svg id="connection-svg"></svg>
329
+ <div class="drop-zone-text"><p>🎯 Drag layers here to build</p></div>
330
+ </div>
331
+
332
+ <!-- Right Panel: Data, Training & Results -->
333
+ <div class="panel training-panel">
334
+ <h2><span class="icon">📊</span>Data & Training</h2>
335
+
336
+ <!-- Training Data Section -->
337
+ <div id="data-controls">
338
+ <h3>Training Dataset</h3>
339
+ <div class="input-method-selector">
340
+ <button class="method-btn active" id="functionBtn" onclick="switchInputMethod('function', 'training')">Generate</button>
341
+ <button class="method-btn" id="manualBtn" onclick="switchInputMethod('manual', 'training')">Manual</button>
342
+ </div>
343
+ <div id="functionInput">
344
+ <div class="config-group"><label>Function:</label><select id="functionType" onchange="generateFunctionData()"><option value="linear">Linear</option><option value="quadratic" selected>Quadratic</option><option value="sine">Sine Wave</option><option value="exponential">Exponential</option></select></div>
345
+ <div class="config-group"><label>Samples:</label><input type="number" id="numSamples" value="100" min="10" max="500" step="10" onchange="generateFunctionData()"></div>
346
+ </div>
347
+ <div id="manualInput" style="display: none;">
348
+ <div class="config-group"><label>X Values (comma-separated):</label><textarea id="xValues" rows="2" placeholder="e.g., 1, 2, 3, 4"></textarea></div>
349
+ <div class="config-group"><label>Y Values (comma-separated):</label><textarea id="yValues" rows="2" placeholder="e.g., 2, 4, 6, 8"></textarea></div>
350
+ <button class="load-data-btn" onclick="processManualData()">Load Data</button>
351
+ </div>
352
+ </div>
353
+
354
+ <h3>Training Settings</h3>
355
+ <div class="training-controls">
356
+ <div class="config-group"><label>Learning Rate:</label><input type="number" id="learningRate" value="0.01" step="0.001"></div>
357
+ <div class="config-group"><label>Epochs:</label><input type="number" id="epochs" value="100" step="10"></div>
358
+ <div class="config-group"><label>Optimizer:</label><select id="optimizer"><option value="adam">Adam</option><option value="sgd">SGD</option><option value="rmsprop">RMSprop</option></select></div>
359
+ <button class="train-btn" id="trainBtn" onclick="trainModel()" disabled>Train Network</button>
360
+ <div id="trainingProgress" style="display: none;">
361
+ <div class="progress-bar"><div class="progress-fill" id="progressFill"></div></div>
362
+ <div id="progressText" style="font-size: 0.8rem; text-align: center;"></div>
363
+ </div>
364
+ </div>
365
+ <div class="metrics" id="metricsContainer" style="display: none;">
366
+ <div class="metric"><div class="metric-value" id="lossValue">-</div><div class="metric-label">Training Loss</div></div>
367
+ <div class="metric"><div class="metric-value" id="r2Value">-</div><div class="metric-label">Training R²</div></div>
368
+ </div>
369
+ <div id="dataStatus" class="status"></div>
370
+ <div class="chart-container">
371
+ <canvas id="chart"></canvas>
372
+ </div>
373
+
374
+ <!-- Validation Data Section -->
375
+ <div id="validation-data-controls" style="margin-top: 20px; padding-top: 20px; border-top: 2px solid #ddd;">
376
+ <h3>Validation Dataset</h3>
377
+ <div class="input-method-selector">
378
+ <button class="method-btn active" id="valFunctionBtn" onclick="switchInputMethod('function', 'validation')">Generate</button>
379
+ <button class="method-btn" id="valManualBtn" onclick="switchInputMethod('manual', 'validation')">Manual</button>
380
+ </div>
381
+ <div id="valFunctionInput">
382
+ <div class="config-group"><label>Function:</label><select id="valFunctionType" onchange="generateValidationData()"><option value="linear">Linear</option><option value="quadratic">Quadratic</option><option value="sine" selected>Sine Wave</option><option value="exponential">Exponential</option></select></div>
383
+ <div class="config-group"><label>Samples:</label><input type="number" id="valNumSamples" value="50" min="10" max="500" step="10" onchange="generateValidationData()"></div>
384
+ </div>
385
+ <div id="valManualInput" style="display: none;">
386
+ <div class="config-group"><label>X Values (comma-separated):</label><textarea id="valXValues" rows="2" placeholder="e.g., 1.5, 2.5, 3.5"></textarea></div>
387
+ <div class="config-group"><label>Y Values (comma-separated):</label><textarea id="valYValues" rows="2" placeholder="e.g., 3, 5, 7"></textarea></div>
388
+ <button class="load-data-btn" onclick="processManualValidationData()">Load Data</button>
389
+ </div>
390
+ <button class="validate-btn" id="validateBtn" onclick="validateModel()" disabled>Validate Model</button>
391
+ </div>
392
+ <div class="metrics" id="validationMetricsContainer" style="display: none;">
393
+ <div class="metric"><div class="metric-value" id="validationLossValue">-</div><div class="metric-label">Validation Loss</div></div>
394
+ <div class="metric"><div class="metric-value" id="validationR2Value">-</div><div class="metric-label">Validation R²</div></div>
395
+ </div>
396
+ <div id="validationStatus" class="status"></div>
397
+ <div class="chart-container">
398
+ <canvas id="validationChart"></canvas>
399
+ </div>
400
+
401
+ </div>
402
+ </div>
403
+ </div>
404
+
405
+ <script>
406
+ // Global state variables
407
+ let dataset = null, validationDataset = null, model = null, chart = null, validationChart = null, isTraining = false;
408
+ let layers = [], selectedLayerId = null, layerCounter = 0;
409
+
410
+ // --- CORE LOGIC: NEURAL NETWORK ARCHITECTURE ---
411
+ const canvas = document.getElementById('architectureCanvas');
412
+ const connectionSvg = document.getElementById('connection-svg');
413
+
414
+ function createLayer(type, x, y) {
415
+ if ((type === 'input' && layers.some(l => l.type === 'input')) || (type === 'output' && layers.some(l => l.type === 'output'))) {
416
+ showStatus(`Only one ${type} layer is allowed.`, 'error', 'data');
417
+ return;
418
+ }
419
+ const layerId = `layer_${layerCounter++}`;
420
+ const layer = { id: layerId, type, x, y, units: type === 'input' || type === 'output' ? 1 : 8, activation: type === 'output' ? 'linear' : 'relu' };
421
+ if (type === 'dense') layer.units = Math.min(layer.units, 16);
422
+ layers.push(layer);
423
+ renderLayer(layer);
424
+ updateConnections();
425
+ checkTrainingReady();
426
+ document.querySelector('.drop-zone-text').style.display = 'none';
427
+ }
428
+
429
+ function renderLayer(layer) {
430
+ let layerEl = document.getElementById(layer.id);
431
+ if (!layerEl) {
432
+ layerEl = document.createElement('div');
433
+ layerEl.id = layer.id;
434
+ canvas.appendChild(layerEl);
435
+ layerEl.addEventListener('mousedown', (e) => startDrag(e, layer));
436
+ layerEl.addEventListener('click', (e) => { e.stopPropagation(); selectLayer(layer); });
437
+ }
438
+ layerEl.className = `layer-instance ${layer.type}-layer-bg`;
439
+ layerEl.style.left = `${layer.x}px`;
440
+ layerEl.style.top = `${layer.y}px`;
441
+ if (layer.id === selectedLayerId) layerEl.classList.add('selected');
442
+ const activationText = layer.type !== 'input' ? `(${layer.activation})` : '';
443
+ let neuronsHTML = Array.from({ length: Math.min(layer.units, 16) }, () => '<div class="neuron"></div>').join('');
444
+ layerEl.innerHTML = `<div class="layer-header">${layer.type.charAt(0).toUpperCase() + layer.type.slice(1)}</div><div class="layer-details">${layer.units} Neurons ${activationText}</div><div class="neuron-column">${neuronsHTML}</div><button class="delete-btn" onclick="deleteLayer(event, '${layer.id}')">&times;</button>`;
445
+ }
446
+
447
+ function deleteLayer(e, layerId) {
448
+ e.stopPropagation();
449
+ layers = layers.filter(l => l.id !== layerId);
450
+ document.getElementById(layerId).remove();
451
+ if (selectedLayerId === layerId) {
452
+ selectedLayerId = null;
453
+ document.getElementById('layerConfig').style.display = 'none';
454
+ }
455
+ updateConnections();
456
+ checkTrainingReady();
457
+ if (layers.length === 0) document.querySelector('.drop-zone-text').style.display = 'block';
458
+ }
459
+
460
+ function clearArchitecture() {
461
+ layers = []; selectedLayerId = null; model = null;
462
+ canvas.querySelectorAll('.layer-instance').forEach(el => el.remove());
463
+ document.getElementById('layerConfig').style.display = 'none';
464
+ document.getElementById('validateBtn').disabled = true;
465
+ document.getElementById('metricsContainer').style.display = 'none';
466
+ document.getElementById('validationMetricsContainer').style.display = 'none';
467
+ updateConnections();
468
+ checkTrainingReady();
469
+ document.querySelector('.drop-zone-text').style.display = 'block';
470
+ }
471
+
472
+ function selectLayer(layer) {
473
+ selectedLayerId = layer.id;
474
+ document.querySelectorAll('.layer-instance').forEach(el => el.classList.remove('selected'));
475
+ document.getElementById(layer.id).classList.add('selected');
476
+ const configPanel = document.getElementById('layerConfig');
477
+ const unitsInput = document.getElementById('layerUnits');
478
+ const activationSelect = document.getElementById('layerActivation');
479
+ unitsInput.value = layer.units;
480
+ activationSelect.value = layer.activation;
481
+ unitsInput.disabled = (layer.type === 'input' || layer.type === 'output');
482
+ activationSelect.disabled = (layer.type === 'input');
483
+ configPanel.style.display = 'block';
484
+ }
485
+
486
+ function updateConnections() {
487
+ connectionSvg.innerHTML = '';
488
+ const sortedLayers = [...layers].sort((a, b) => a.x - b.x);
489
+ for (let i = 0; i < sortedLayers.length - 1; i++) {
490
+ const fromEl = document.getElementById(sortedLayers[i].id);
491
+ const toEl = document.getElementById(sortedLayers[i + 1].id);
492
+ const fromNeurons = fromEl.querySelectorAll('.neuron');
493
+ const toNeurons = toEl.querySelectorAll('.neuron');
494
+ fromNeurons.forEach(fromNode => {
495
+ toNeurons.forEach(toNode => {
496
+ const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
497
+ const fromRect = fromNode.getBoundingClientRect();
498
+ const toRect = toNode.getBoundingClientRect();
499
+ const canvasRect = canvas.getBoundingClientRect();
500
+ line.setAttribute('x1', fromRect.left - canvasRect.left + fromRect.width / 2);
501
+ line.setAttribute('y1', fromRect.top - canvasRect.top + fromRect.height / 2);
502
+ line.setAttribute('x2', toRect.left - canvasRect.left + toRect.width / 2);
503
+ line.setAttribute('y2', toRect.top - canvasRect.top + toRect.height / 2);
504
+ line.setAttribute('class', 'connection-line');
505
+ connectionSvg.appendChild(line);
506
+ });
507
+ });
508
+ }
509
+ }
510
+
511
+ // --- DRAG AND DROP FUNCTIONALITY ---
512
+ canvas.addEventListener('dragover', (e) => e.preventDefault());
513
+ canvas.addEventListener('drop', (e) => {
514
+ e.preventDefault();
515
+ const type = e.dataTransfer.getData('text/plain');
516
+ const rect = canvas.getBoundingClientRect();
517
+ createLayer(type, e.clientX - rect.left - 40, e.clientY - rect.top - 50);
518
+ });
519
+ document.querySelectorAll('.layer-template').forEach(template => {
520
+ template.addEventListener('dragstart', (e) => e.dataTransfer.setData('text/plain', template.dataset.type));
521
+ });
522
+ function startDrag(e, layer) {
523
+ const layerEl = e.currentTarget;
524
+ const offsetX = e.clientX - layer.x, offsetY = e.clientY - layer.y;
525
+ function onMouseMove(e) {
526
+ const rect = canvas.getBoundingClientRect();
527
+ layer.x = Math.max(0, Math.min(e.clientX - offsetX, rect.width - layerEl.offsetWidth));
528
+ layer.y = Math.max(0, Math.min(e.clientY - offsetY, rect.height - layerEl.offsetHeight));
529
+ layerEl.style.left = `${layer.x}px`;
530
+ layerEl.style.top = `${layer.y}px`;
531
+ updateConnections();
532
+ }
533
+ function onMouseUp() {
534
+ document.removeEventListener('mousemove', onMouseMove);
535
+ document.removeEventListener('mouseup', onMouseUp);
536
+ }
537
+ document.addEventListener('mousemove', onMouseMove);
538
+ document.addEventListener('mouseup', onMouseUp);
539
+ }
540
+
541
+ // --- MODEL TRAINING & DATA HANDLING ---
542
+ async function trainModel() {
543
+ if (!dataset || isTraining || layers.length < 2) return;
544
+
545
+ isTraining = true;
546
+ const trainBtn = document.getElementById('trainBtn');
547
+ trainBtn.disabled = true;
548
+ document.getElementById('validateBtn').disabled = true;
549
+ trainBtn.textContent = 'Training...';
550
+ document.getElementById('trainingProgress').style.display = 'block';
551
+ document.getElementById('metricsContainer').style.display = 'none';
552
+ let finalLoss = 0;
553
+
554
+ let inputTensor, outputTensor, predTensor;
555
+
556
+ try {
557
+ const xs = dataset.map(d => d.x);
558
+ const ys = dataset.map(d => d.y);
559
+ inputTensor = tf.tensor2d(xs, [xs.length, 1]);
560
+ outputTensor = tf.tensor2d(ys, [ys.length, 1]);
561
+
562
+ model = tf.sequential();
563
+ const sortedLayers = [...layers].sort((a, b) => a.x - b.x);
564
+ sortedLayers.forEach((layer, i) => {
565
+ if (layer.type === 'input') return;
566
+ let config = { units: layer.units, activation: layer.activation };
567
+ if (i === 1 || (i === 0 && sortedLayers[0].type !== 'input')) config.inputShape = [1];
568
+ model.add(tf.layers.dense(config));
569
+ });
570
+
571
+ const learningRate = parseFloat(document.getElementById('learningRate').value);
572
+ const optimizerType = document.getElementById('optimizer').value;
573
+ let optimizer = optimizerType === 'sgd' ? tf.train.sgd(learningRate) : optimizerType === 'rmsprop' ? tf.train.rmsprop(learningRate) : tf.train.adam(learningRate);
574
+ model.compile({ optimizer, loss: 'meanSquaredError' });
575
+
576
+ const epochs = parseInt(document.getElementById('epochs').value);
577
+ await model.fit(inputTensor, outputTensor, {
578
+ epochs: epochs,
579
+ callbacks: {
580
+ onEpochEnd: (epoch, logs) => {
581
+ finalLoss = logs.loss;
582
+ const progress = ((epoch + 1) / epochs) * 100;
583
+ document.getElementById('progressFill').style.width = `${progress}%`;
584
+ document.getElementById('progressText').textContent = `Epoch ${epoch + 1}/${epochs} - Loss: ${finalLoss.toFixed(5)}`;
585
+ }
586
+ }
587
+ });
588
+
589
+ predTensor = model.predict(inputTensor);
590
+ const predData = await predTensor.data();
591
+ plotPredictions(Array.from(predData), finalLoss, 'training');
592
+ showStatus('✓ Model trained successfully!', 'success', 'data');
593
+ } catch (error) {
594
+ showStatus(`Training Error: ${error.message}`, 'error', 'data');
595
+ console.error(error);
596
+ } finally {
597
+ if (inputTensor) inputTensor.dispose();
598
+ if (outputTensor) outputTensor.dispose();
599
+ if (predTensor) predTensor.dispose();
600
+
601
+ isTraining = false;
602
+ trainBtn.disabled = false;
603
+ trainBtn.textContent = 'Train Network';
604
+ if (model) document.getElementById('validateBtn').disabled = false;
605
+ }
606
+ }
607
+
608
+ async function validateModel() {
609
+ if (!model || !validationDataset) {
610
+ showStatus('Train a model and load validation data first.', 'error', 'validation');
611
+ return;
612
+ }
613
+ let valInputTensor, valOutputTensor, valPredTensor;
614
+ try {
615
+ const xs = validationDataset.map(d => d.x);
616
+ const ys = validationDataset.map(d => d.y);
617
+ valInputTensor = tf.tensor2d(xs, [xs.length, 1]);
618
+ valOutputTensor = tf.tensor2d(ys, [ys.length, 1]);
619
+
620
+ valPredTensor = model.predict(valInputTensor);
621
+ const lossTensor = tf.losses.meanSquaredError(valOutputTensor, valPredTensor);
622
+ const loss = await lossTensor.data();
623
+ lossTensor.dispose();
624
+
625
+ const predData = await valPredTensor.data();
626
+ plotPredictions(Array.from(predData), loss[0], 'validation');
627
+ showStatus('✓ Validation complete!', 'success', 'validation');
628
+ } catch (error) {
629
+ showStatus(`Validation Error: ${error.message}`, 'error', 'validation');
630
+ console.error(error);
631
+ } finally {
632
+ if (valInputTensor) valInputTensor.dispose();
633
+ if (valOutputTensor) valOutputTensor.dispose();
634
+ if (valPredTensor) valPredTensor.dispose();
635
+ }
636
+ }
637
+
638
+ function processManualData() {
639
+ const xText = document.getElementById('xValues').value.trim();
640
+ const yText = document.getElementById('yValues').value.trim();
641
+ if (!xText || !yText) return showStatus('Please enter both X and Y values.', 'error', 'data');
642
+ try {
643
+ const xValues = xText.split(',').map(v => parseFloat(v.trim()));
644
+ const yValues = yText.split(',').map(v => parseFloat(v.trim()));
645
+ if (xValues.length !== yValues.length) return showStatus('X and Y must have the same number of values.', 'error', 'data');
646
+ if (xValues.some(isNaN) || yValues.some(isNaN)) return showStatus('All values must be valid numbers.', 'error', 'data');
647
+ dataset = xValues.map((x, i) => ({ x, y: yValues[i] }));
648
+ updateChart('training');
649
+ checkTrainingReady();
650
+ showStatus(`✓ Loaded ${dataset.length} training data points`, 'success', 'data');
651
+ } catch (error) {
652
+ showStatus(`Error processing data: ${error.message}`, 'error', 'data');
653
+ }
654
+ }
655
+
656
+ function processManualValidationData() {
657
+ const xText = document.getElementById('valXValues').value.trim();
658
+ const yText = document.getElementById('valYValues').value.trim();
659
+ if (!xText || !yText) return showStatus('Please enter both X and Y values.', 'error', 'validation');
660
+ try {
661
+ const xValues = xText.split(',').map(v => parseFloat(v.trim()));
662
+ const yValues = yText.split(',').map(v => parseFloat(v.trim()));
663
+ if (xValues.length !== yValues.length) return showStatus('X and Y must have the same number of values.', 'error', 'validation');
664
+ if (xValues.some(isNaN) || yValues.some(isNaN)) return showStatus('All values must be valid numbers.', 'error', 'validation');
665
+ validationDataset = xValues.map((x, i) => ({ x, y: yValues[i] }));
666
+ updateChart('validation');
667
+ showStatus(`✓ Loaded ${validationDataset.length} validation data points`, 'success', 'validation');
668
+ } catch (error) {
669
+ showStatus(`Error processing validation data: ${error.message}`, 'error', 'validation');
670
+ }
671
+ }
672
+
673
+ function generateFunctionData() {
674
+ const type = document.getElementById('functionType').value;
675
+ const numSamples = parseInt(document.getElementById('numSamples').value);
676
+ const data = Array.from({ length: numSamples }, (_, i) => {
677
+ const x = -5 + (i * 10 / (numSamples -1)); // Scale x from -5 to 5
678
+ let y;
679
+ switch (type) {
680
+ case 'quadratic': y = 0.5 * x**2 - x - 2; break;
681
+ case 'sine': y = 3 * Math.sin(x); break;
682
+ case 'exponential': y = Math.exp(0.5 * x); break;
683
+ default: y = 2 * x + 1;
684
+ }
685
+ return { x, y: y + (Math.random() - 0.5) * 2.5 };
686
+ });
687
+ dataset = data;
688
+ updateChart('training');
689
+ checkTrainingReady();
690
+ showStatus(`✓ Generated ${type} training dataset`, 'success', 'data');
691
+ }
692
+
693
+ function generateValidationData() {
694
+ const type = document.getElementById('valFunctionType').value;
695
+ const numSamples = parseInt(document.getElementById('valNumSamples').value);
696
+ const data = Array.from({ length: numSamples }, (_, i) => {
697
+ // Generate data from a different range (e.g., 5 to 15) to test extrapolation
698
+ const x = 5 + (i * 10 / (numSamples-1));
699
+ let y;
700
+ switch (type) {
701
+ case 'quadratic': y = 0.5 * x**2 - x - 2; break;
702
+ case 'sine': y = 3 * Math.sin(x); break;
703
+ case 'exponential': y = Math.exp(0.5 * x); break;
704
+ default: y = 2 * x + 1;
705
+ }
706
+ return { x, y: y + (Math.random() - 0.5) * 2.5 }; // Add some noise
707
+ });
708
+ validationDataset = data;
709
+ updateChart('validation');
710
+ showStatus(`✓ Generated new ${type} validation dataset`, 'success', 'validation');
711
+ }
712
+
713
+ // --- UI & UTILITY FUNCTIONS ---
714
+ function updateChart(mode) {
715
+ const targetChart = mode === 'training' ? chart : validationChart;
716
+ const targetDataset = mode === 'training' ? dataset : validationDataset;
717
+ if (!targetChart || !targetDataset) return;
718
+ targetChart.data.datasets[0].data = targetDataset;
719
+ targetChart.data.datasets[1].data = [];
720
+ targetChart.update();
721
+ }
722
+
723
+ function plotPredictions(predictions, loss, mode) {
724
+ const targetChart = mode === 'training' ? chart : validationChart;
725
+ const targetDataset = mode === 'training' ? dataset : validationDataset;
726
+
727
+ const sortedData = [...targetDataset].sort((a, b) => a.x - b.x);
728
+ const predPoints = sortedData.map((point) => ({
729
+ x: point.x,
730
+ y: predictions[targetDataset.findIndex(d => d.x === point.x)]
731
+ }));
732
+ targetChart.data.datasets[1].data = predPoints;
733
+ targetChart.update();
734
+
735
+ const actuals = targetDataset.map(d => d.y);
736
+ const r2 = calculateR2(actuals, predictions);
737
+
738
+ if (mode === 'training') {
739
+ document.getElementById('lossValue').textContent = loss.toFixed(5);
740
+ document.getElementById('r2Value').textContent = r2.toFixed(4);
741
+ document.getElementById('metricsContainer').style.display = 'grid';
742
+ } else {
743
+ document.getElementById('validationLossValue').textContent = loss.toFixed(5);
744
+ document.getElementById('validationR2Value').textContent = r2.toFixed(4);
745
+ document.getElementById('validationMetricsContainer').style.display = 'grid';
746
+ }
747
+ }
748
+
749
+ function calculateR2(actual, predicted) {
750
+ const actualMean = actual.reduce((a, b) => a + b, 0) / actual.length;
751
+ const totalSumSquares = actual.reduce((sum, val) => sum + (val - actualMean) ** 2, 0);
752
+ const residualSumSquares = actual.reduce((sum, val, i) => sum + (val - predicted[i]) ** 2, 0);
753
+ return 1 - (residualSumSquares / totalSumSquares);
754
+ }
755
+
756
+ function showStatus(message, type, context) {
757
+ const statusEl = context === 'validation' ? document.getElementById('validationStatus') : document.getElementById('dataStatus');
758
+ statusEl.textContent = message;
759
+ statusEl.className = `status ${type}`;
760
+ statusEl.style.display = 'block';
761
+ if (type !== 'error') setTimeout(() => statusEl.style.display = 'none', 3000);
762
+ }
763
+
764
+ function checkTrainingReady() {
765
+ document.getElementById('trainBtn').disabled = !(layers.some(l => l.type === 'input') && layers.some(l => l.type === 'output') && dataset && layers.length >= 2);
766
+ }
767
+
768
+ function switchInputMethod(method, context) {
769
+ if (context === 'training') {
770
+ document.getElementById('manualInput').style.display = method === 'manual' ? 'block' : 'none';
771
+ document.getElementById('functionInput').style.display = method === 'function' ? 'block' : 'none';
772
+ document.getElementById('manualBtn').classList.toggle('active', method === 'manual');
773
+ document.getElementById('functionBtn').classList.toggle('active', method === 'function');
774
+ } else {
775
+ document.getElementById('valManualInput').style.display = method === 'manual' ? 'block' : 'none';
776
+ document.getElementById('valFunctionInput').style.display = method === 'function' ? 'block' : 'none';
777
+ document.getElementById('valManualBtn').classList.toggle('active', method === 'manual');
778
+ document.getElementById('valFunctionBtn').classList.toggle('active', method === 'function');
779
+ }
780
+ }
781
+
782
+ // Event Listeners for Layer Configuration
783
+ document.getElementById('layerUnits').addEventListener('input', (e) => {
784
+ if (!selectedLayerId) return;
785
+ const layer = layers.find(l => l.id === selectedLayerId);
786
+ if (layer) { layer.units = parseInt(e.target.value); renderLayer(layer); updateConnections(); }
787
+ });
788
+ document.getElementById('layerActivation').addEventListener('change', (e) => {
789
+ if (!selectedLayerId) return;
790
+ const layer = layers.find(l => l.id === selectedLayerId);
791
+ if (layer) { layer.activation = e.target.value; renderLayer(layer); }
792
+ });
793
+
794
+ // --- INITIALIZATION ---
795
+ document.addEventListener('DOMContentLoaded', () => {
796
+ const ctx = document.getElementById('chart').getContext('2d');
797
+ chart = new Chart(ctx, {
798
+ type: 'scatter',
799
+ data: { datasets: [{ label: 'Training Data', data: [], backgroundColor: 'rgba(106, 130, 251, 0.7)' }, { label: 'Model Prediction', data: [], borderColor: 'rgba(252, 92, 125, 1)', backgroundColor: 'transparent', type: 'line', fill: false, tension: 0.4, borderWidth: 2 }] },
800
+ options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'Training Results' } } }
801
+ });
802
+
803
+ const valCtx = document.getElementById('validationChart').getContext('2d');
804
+ validationChart = new Chart(valCtx, {
805
+ type: 'scatter',
806
+ data: { datasets: [{ label: 'Validation Data', data: [], backgroundColor: 'rgba(33, 150, 243, 0.7)' }, { label: 'Model Prediction', data: [], borderColor: 'rgba(255, 152, 0, 1)', backgroundColor: 'transparent', type: 'line', fill: false, tension: 0.4, borderWidth: 2 }] },
807
+ options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'Validation Results' } } }
808
+ });
809
+
810
+ generateFunctionData();
811
+ generateValidationData();
812
+ });
813
+ </script>
814
+ </body>
815
  </html>