bhuvi06 commited on
Commit
9981057
·
verified ·
1 Parent(s): 1a3683c

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +399 -0
app.py ADDED
@@ -0,0 +1,399 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ st.set_page_config(layout="wide")
3
+
4
+ # Debug: Check for any unexpected Streamlit commands or state before this point
5
+ st.write("Starting app with page config set as first command.")
6
+
7
+ # Imports (after set_page_config)
8
+ import networkx as nx
9
+ import pandas as pd
10
+ import numpy as np
11
+ import matplotlib.pyplot as plt
12
+ import seaborn as sns
13
+ from sklearn.datasets import make_blobs, make_circles, make_moons
14
+ from sklearn.preprocessing import StandardScaler
15
+ from mlxtend.plotting import plot_decision_regions
16
+ import tensorflow as tf
17
+ from keras.models import Sequential
18
+ from keras.layers import Input, Dense
19
+ from keras.optimizers import SGD
20
+ from keras.losses import MeanSquaredError, BinaryCrossentropy
21
+ from keras.regularizers import l2, l1
22
+ from keras.callbacks import Callback
23
+
24
+ # Check TensorFlow and Keras versions with fallback
25
+ try:
26
+ tf_version = tf.__version__
27
+ # Try multiple ways to get Keras version, accounting for TensorFlow integration
28
+ keras_version = None
29
+ if hasattr(tf.keras, '__version__'):
30
+ keras_version = tf.keras.__version__
31
+ elif hasattr(tf, 'keras') and hasattr(tf.keras, 'version'):
32
+ keras_version = tf.keras.version.__version__
33
+ else:
34
+ keras_version = "Keras version not available (bundled with TensorFlow)"
35
+ st.write(f"TensorFlow version: {tf_version}")
36
+ st.write(f"Keras version: {keras_version}")
37
+ except AttributeError as e:
38
+ st.error(f"Error checking versions: {e}")
39
+ st.write("Falling back to default versions: TensorFlow ~2.15, Keras ~2.15")
40
+
41
+ # Set White Theme CSS
42
+ st.markdown("""
43
+ <style>
44
+ .stApp {
45
+ background-color: white;
46
+ color: #333;
47
+ font-family: Arial, sans-serif;
48
+ }
49
+ h1, h2, h3 {
50
+ color: #333;
51
+ font-weight: bold;
52
+ margin: 0;
53
+ padding: 5px 0;
54
+ }
55
+ .stButton>button {
56
+ background-color: #f0f0f0;
57
+ color: #333;
58
+ border: 2px solid #ddd;
59
+ border-radius: 5px;
60
+ padding: 5px 10px;
61
+ font-size: 14px;
62
+ font-weight: bold;
63
+ }
64
+ .stButton>button:hover {
65
+ background-color: #e0e0e0;
66
+ border-color: #ccc;
67
+ }
68
+ .stSelectbox, .stSlider {
69
+ background-color: white;
70
+ color: #333;
71
+ border: 2px solid #ddd;
72
+ border-radius: 5px;
73
+ padding: 5px;
74
+ }
75
+ .stCheckbox label {
76
+ color: #333;
77
+ font-size: 14px;
78
+ font-weight: bold;
79
+ }
80
+ .control-bar {
81
+ background-color: #f8f8f8;
82
+ padding: 10px;
83
+ border: 2px solid #ddd;
84
+ border-radius: 5px;
85
+ margin-bottom: 10px;
86
+ }
87
+ .panel {
88
+ background-color: white;
89
+ padding: 10px;
90
+ border: 2px solid #ddd;
91
+ border-radius: 5px;
92
+ margin: 10px 0;
93
+ }
94
+ .stSelectbox label, .stSlider label {
95
+ color: #333;
96
+ font-size: 12px;
97
+ font-weight: bold;
98
+ }
99
+ </style>
100
+ """, unsafe_allow_html=True)
101
+
102
+ # Session state initialization
103
+ if "training" not in st.session_state:
104
+ st.session_state.training = False
105
+ if "num_hidden_layers" not in st.session_state:
106
+ st.session_state.num_hidden_layers = 2
107
+ if "hidden_layer_neurons" not in st.session_state:
108
+ st.session_state.hidden_layer_neurons = [4, 2]
109
+ if "prev_params" not in st.session_state:
110
+ st.session_state.prev_params = {}
111
+
112
+ def reset_session():
113
+ st.session_state.clear()
114
+ st.session_state.num_hidden_layers = 2
115
+ st.session_state.hidden_layer_neurons = [4, 2]
116
+
117
+ # Two-row top control bar
118
+ with st.container():
119
+ st.markdown('<div class="control-bar">', unsafe_allow_html=True)
120
+ # Row 1
121
+ col1, col2, col3, col4, col5 = st.columns(5)
122
+ with col1:
123
+ problem_type = st.selectbox("Problem Type", ["Classification", "Regression"])
124
+ with col2:
125
+ dataset_options = {"Classification": ["Blobs", "Circles", "Spirals", "XOR"], "Regression": ["Sine Wave"]}
126
+ dataset_type = st.selectbox("Dataset", dataset_options[problem_type])
127
+ with col3:
128
+ learning_rate = st.selectbox("Learning Rate", [0.0001, 0.001, 0.03, 0.1, 0.3, 1], index=2)
129
+ with col4:
130
+ activation = st.selectbox("Activation", ["ReLU", "Sigmoid", "Tanh"], index=2)
131
+ with col5:
132
+ batch_size = st.slider("Batch Size", 1, 10, 5) # Reduced max batch size for Spaces
133
+
134
+ # Row 2
135
+ col6, col7, col8, col9, col10 = st.columns(5)
136
+ with col6:
137
+ noise_level = st.slider("Noise", 0, 50, 0, step=5)
138
+ with col7:
139
+ reg_type = st.selectbox("Regularization", ["None", "L1", "L2"], index=0)
140
+ with col8:
141
+ reg_rate = st.selectbox("Reg Rate", [0.0, 0.001, 0.01, 0.1, 1], index=0)
142
+ with col9:
143
+ train_ratio = st.slider("Train %", 10, 90, 50, 10) / 100
144
+ with col10:
145
+ st.button("Reset", key="reset_global", on_click=reset_session)
146
+ st.markdown('</div>', unsafe_allow_html=True)
147
+
148
+ # Dataset generation (reduced sample size for performance)
149
+ def generate_xor(n_samples=400): # Reduced from 800 for performance
150
+ X = np.random.rand(n_samples, 2) * 2 - 1
151
+ y = np.logical_xor(X[:, 0] > 0, X[:, 1] > 0).astype(int)
152
+ return X, y
153
+
154
+ def generate_sine_wave(noise, n_samples=400): # Reordered: non-default before default
155
+ X = np.linspace(-3, 3, n_samples).reshape(-1, 1)
156
+ y = np.sin(X) + np.random.normal(0, noise / 100, X.shape)
157
+ return np.hstack([X, X**2]), y.ravel()
158
+
159
+ if problem_type == "Classification":
160
+ if dataset_type == "Blobs":
161
+ fv, cv = make_blobs(n_samples=400, centers=2, n_features=2, cluster_std=1.5 + noise_level / 50, random_state=42)
162
+ elif dataset_type == "Circles":
163
+ fv, cv = make_circles(n_samples=400, noise=noise_level / 250, factor=0.2)
164
+ elif dataset_type == "Spirals":
165
+ fv, cv = make_moons(n_samples=400, noise=noise_level / 250)
166
+ elif dataset_type == "XOR":
167
+ fv, cv = generate_xor(400)
168
+ else:
169
+ fv, cv = generate_sine_wave(noise_level, 400)
170
+
171
+ # Feature preprocessing
172
+ std = StandardScaler()
173
+ X = std.fit_transform(fv)
174
+ x1, x2 = X[:, 0], X[:, 1]
175
+ features = {
176
+ "X1": x1, "X2": x2, "X1*X2": x1 * x2, "X1^2": x1**2, "X2^2": x2**2,
177
+ "cos(X1)": np.cos(x1), "sin(X1)": np.sin(x1), "cos(X2)": np.cos(x2), "sin(X2)": np.sin(x2)
178
+ }
179
+ selected_features = [f for f in features.keys() if st.session_state.get(f, f in ["X1", "X2"])]
180
+ selected_data = np.column_stack([features[f] for f in selected_features])
181
+
182
+ if problem_type == "Classification":
183
+ cv = cv.astype(int)
184
+
185
+ # Main layout
186
+ col_left, col_center, col_right = st.columns([1, 2, 1])
187
+
188
+ # Left panel: Dataset with Seaborn (3x3 size)
189
+ with col_left:
190
+ st.markdown('<div class="panel">', unsafe_allow_html=True)
191
+ st.subheader("Data")
192
+ fig, ax = plt.subplots(figsize=(3, 3)) # Fixed size for consistency
193
+ if problem_type == "Classification":
194
+ sns.scatterplot(x=fv[:, 0], y=fv[:, 1], hue=cv, palette="coolwarm", edgecolor="k", alpha=0.7, ax=ax, legend=False)
195
+ else:
196
+ sns.scatterplot(x=fv[:, 0], y=cv, color="blue", edgecolor="k", alpha=0.7, ax=ax)
197
+ ax.set_xticks([])
198
+ ax.set_yticks([])
199
+ ax.set_facecolor("white")
200
+ st.pyplot(fig)
201
+ st.subheader("Features")
202
+ for feature in features.keys():
203
+ st.checkbox(feature, value=feature in ["X1", "X2"], key=feature)
204
+ st.markdown('</div>', unsafe_allow_html=True)
205
+
206
+ # Center panel: Horizontal Network Visualization
207
+ with col_center:
208
+ st.markdown('<div class="panel">', unsafe_allow_html=True)
209
+ st.subheader("Network")
210
+
211
+ def draw_nn(features, hidden_neurons):
212
+ G = nx.DiGraph()
213
+ input_layer = features
214
+ hidden_layers = [[f"H{i+1}_{j+1}" for j in range(n)] for i, n in enumerate(hidden_neurons)]
215
+ output_layer = ["Output"]
216
+ all_layers = [input_layer] + hidden_layers + [output_layer]
217
+
218
+ node_colors = {}
219
+ for layer_idx, layer in enumerate(all_layers):
220
+ for node in layer:
221
+ G.add_node(node, layer=layer_idx)
222
+ if layer_idx == 0:
223
+ node_colors[node] = "#90EE90" # Green for input
224
+ elif layer_idx == len(all_layers) - 1:
225
+ node_colors[node] = "#FFA07A" # Orange for output
226
+ else:
227
+ node_colors[node] = "#87CEFA" # Blue for hidden
228
+
229
+ for i in range(len(all_layers) - 1):
230
+ for node1 in all_layers[i]:
231
+ for node2 in all_layers[i + 1]:
232
+ G.add_edge(node1, node2)
233
+
234
+ pos = nx.multipartite_layout(G, subset_key="layer", align="vertical")
235
+ pos_rotated = {node: (-y, x) for node, (x, y) in pos.items()}
236
+ for node in pos_rotated:
237
+ pos_rotated[node] = (pos_rotated[node][0] * 2, pos_rotated[node][1] * 2)
238
+
239
+ fig, ax = plt.subplots(figsize=(8, 4))
240
+ ax.set_facecolor("white")
241
+ nx.draw(
242
+ G, pos_rotated,
243
+ with_labels=True,
244
+ node_color=[node_colors[node] for node in G.nodes()],
245
+ edge_color="gray",
246
+ node_size=600,
247
+ font_size=8,
248
+ font_color="black",
249
+ font_weight="bold",
250
+ edgecolors="black",
251
+ width=1.0,
252
+ arrows=True,
253
+ ax=ax
254
+ )
255
+ plt.title("Neural Network Structure", color="black", fontsize=12, pad=10)
256
+ return fig
257
+
258
+ st.pyplot(draw_nn(selected_features, st.session_state.hidden_layer_neurons))
259
+
260
+ def add_layer():
261
+ if st.session_state.num_hidden_layers < 6:
262
+ st.session_state.num_hidden_layers += 1
263
+ st.session_state.hidden_layer_neurons.append(1)
264
+
265
+ def remove_layer():
266
+ if st.session_state.num_hidden_layers > 0:
267
+ st.session_state.num_hidden_layers -= 1
268
+ st.session_state.hidden_layer_neurons.pop()
269
+
270
+ def increase_neurons(i):
271
+ if st.session_state.hidden_layer_neurons[i] < 8:
272
+ st.session_state.hidden_layer_neurons[i] += 1
273
+
274
+ def decrease_neurons(i):
275
+ if st.session_state.hidden_layer_neurons[i] > 1:
276
+ st.session_state.hidden_layer_neurons[i] -= 1
277
+
278
+ for i in range(st.session_state.num_hidden_layers):
279
+ col1, col2, col3 = st.columns([1, 2, 1])
280
+ with col1:
281
+ st.button("−", key=f"dec_{i}", on_click=decrease_neurons, args=(i,))
282
+ with col2:
283
+ st.write(f"Layer {i+1}: {st.session_state.hidden_layer_neurons[i]} neurons")
284
+ with col3:
285
+ st.button("+", key=f"inc_{i}", on_click=increase_neurons, args=(i,))
286
+ col_btn1, col_btn2 = st.columns(2)
287
+ with col_btn1:
288
+ st.button("Add Layer", on_click=add_layer)
289
+ with col_btn2:
290
+ st.button("Remove Layer", on_click=remove_layer)
291
+ st.markdown('</div>', unsafe_allow_html=True)
292
+
293
+ # Right panel: Output and Training (decision region and loss plots stacked vertically, same size as dataset scatterplot)
294
+ with col_right:
295
+ st.markdown('<div class="panel">', unsafe_allow_html=True)
296
+ st.subheader("Output")
297
+ col_start, col_stop = st.columns(2)
298
+ with col_start:
299
+ if st.button("▶️ Play"):
300
+ st.session_state.training = True
301
+ with col_stop:
302
+ if st.button("⏹️ Stop"):
303
+ st.session_state.training = False
304
+
305
+ def create_model(input_dim, neurons):
306
+ model = Sequential()
307
+ model.add(Input(shape=(input_dim,)))
308
+ reg = l1(reg_rate) if reg_type == "L1" else l2(reg_rate) if reg_type == "L2" else None
309
+ for n in neurons:
310
+ model.add(Dense(n, activation=activation.lower(), kernel_regularizer=reg))
311
+ output_activation = "sigmoid" if problem_type == "Classification" else "linear"
312
+ loss = BinaryCrossentropy() if problem_type == "Classification" else MeanSquaredError()
313
+ model.add(Dense(1, activation=output_activation))
314
+ model.compile(optimizer=SGD(learning_rate=learning_rate), loss=loss, metrics=["accuracy" if problem_type == "Classification" else "mae"])
315
+ return model
316
+
317
+ class OutputCallback(tf.keras.callbacks.Callback):
318
+ def __init__(self, X, y):
319
+ super().__init__()
320
+ self.X, self.y = X, y
321
+ self.losses = {"Epoch": [], "Train Loss": [], "Val Loss": []}
322
+ self.placeholder = st.empty()
323
+ self.current_epoch = 0 # Track current epoch
324
+
325
+ def on_train_begin(self, logs=None):
326
+ self.model = self.model # Use the model passed implicitly by Keras
327
+ self.current_epoch = 0
328
+
329
+ def on_epoch_end(self, epoch, logs=None):
330
+ try:
331
+ self.current_epoch = epoch + 1 # Update current epoch
332
+ self.losses["Epoch"].append(self.current_epoch)
333
+ self.losses["Train Loss"].append(logs["loss"])
334
+ self.losses["Val Loss"].append(logs.get("val_loss", logs["loss"]))
335
+ with self.placeholder.container():
336
+ # Single column for vertical stacking
337
+ st.subheader("Decision Region & Loss")
338
+ # Display epoch count above decision region
339
+ st.write(f"Epoch: {self.current_epoch}")
340
+ # Decision region plot (3x3 size, improved accuracy)
341
+ fig1, ax1 = plt.subplots(figsize=(3, 3)) # Match dataset scatterplot size
342
+ if problem_type == "Classification":
343
+ X_2d = self.X[:, :2] # Use only first two features for 2D
344
+ # Ensure model prediction for decision boundary
345
+ y_pred_proba = self.model.predict(X_2d, verbose=0)
346
+ y_pred = (y_pred_proba > 0.5).astype(int).ravel()
347
+ try:
348
+ # Use mlxtend for decision regions
349
+ plot_decision_regions(X_2d, self.y, clf=self.model, legend=2, colors='blue,red')
350
+ plt.scatter(X_2d[:, 0], X_2d[:, 1], c=self.y, cmap='coolwarm', edgecolors='k', alpha=0.7)
351
+ # Add precise decision boundary using contour
352
+ xx, yy = np.meshgrid(np.linspace(X_2d[:, 0].min(), X_2d[:, 0].max(), 100),
353
+ np.linspace(X_2d[:, 1].min(), X_2d[:, 1].max(), 100))
354
+ grid = np.c_[xx.ravel(), yy.ravel()]
355
+ Z = self.model.predict(grid, verbose=0)
356
+ Z = (Z > 0.5).astype(int).reshape(xx.shape)
357
+ plt.contour(xx, yy, Z, levels=[0.5], colors='black', linewidths=2)
358
+ except Exception as e:
359
+ st.warning(f"Decision region plot failed: {e}")
360
+ # Fallback: Use contourf for decision regions
361
+ xx, yy = np.meshgrid(np.linspace(X_2d[:, 0].min(), X_2d[:, 0].max(), 100),
362
+ np.linspace(X_2d[:, 1].min(), X_2d[:, 1].max(), 100))
363
+ grid = np.c_[xx.ravel(), yy.ravel()]
364
+ Z = self.model.predict(grid, verbose=0) if self.model else np.zeros((len(grid), 1))
365
+ Z = (Z > 0.5).astype(int).reshape(xx.shape)
366
+ plt.contour(xx, yy, Z, levels=[0.5], colors='black', linewidths=2)
367
+ plt.contourf(xx, yy, Z, alpha=0.3, cmap="coolwarm")
368
+ plt.scatter(X_2d[:, 0], X_2d[:, 1], c=self.y, cmap="coolwarm", edgecolors="k", alpha=0.7)
369
+ else:
370
+ y_pred = self.model.predict(self.X, verbose=0) if self.model else np.zeros_like(self.X[:, 0])
371
+ plt.scatter(self.X[:, 0], self.y, c="blue", alpha=0.5)
372
+ plt.plot(self.X[:, 0], y_pred, "r-", linewidths=2)
373
+ ax1.set_facecolor("white")
374
+ ax1.set_xticks([])
375
+ ax1.set_yticks([])
376
+ st.pyplot(fig1)
377
+
378
+ # Train-Val-Loss plot (3x3 size)
379
+ fig2, ax2 = plt.subplots(figsize=(3, 3)) # Match dataset scatterplot size
380
+ ax2.plot(self.losses["Epoch"], self.losses["Train Loss"], "b-", label="Train")
381
+ ax2.plot(self.losses["Epoch"], self.losses["Val Loss"], "r--", label="Val")
382
+ ax2.legend()
383
+ ax2.set_facecolor("white")
384
+ st.pyplot(fig2)
385
+ except Exception as e:
386
+ st.error(f"Error in epoch end: {e}")
387
+
388
+ if st.session_state.training:
389
+ try:
390
+ model = create_model(len(selected_features), st.session_state.hidden_layer_neurons)
391
+ callback = OutputCallback(selected_data, cv)
392
+ callback.model = model # Explicitly set the model for the callback
393
+ model.fit(selected_data, cv, epochs=50, # Further reduced for Spaces
394
+ batch_size=batch_size, validation_split=1-train_ratio,
395
+ callbacks=[callback], verbose=0)
396
+ except Exception as e:
397
+ st.error(f"Training failed: {e}")
398
+
399
+ st.markdown('</div>', unsafe_allow_html=True)