CosmickVisions commited on
Commit
b0c63cf
·
verified ·
1 Parent(s): 44ad33b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +439 -214
app.py CHANGED
@@ -12,7 +12,7 @@ from sklearn.manifold import TSNE
12
  import umap.umap_ as umap
13
  import shap
14
  import joblib
15
- from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_curve, auc, mean_squared_error, mean_absolute_error, r2_score, classification_report
16
  from sklearn.pipeline import Pipeline
17
  from sklearn.compose import ColumnTransformer
18
  from sklearn.impute import SimpleImputer
@@ -20,15 +20,66 @@ from sklearn.cluster import KMeans, DBSCAN
20
  from sklearn.mixture import GaussianMixture
21
  from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor
22
  from sklearn.linear_model import LogisticRegression, LinearRegression
 
23
  from xgboost import XGBClassifier, XGBRegressor
24
  import matplotlib.pyplot as plt
25
  from io import BytesIO
26
  import time
 
 
 
27
 
28
  # Set page config
29
  st.set_page_config(page_title="Neural-Vision Enhanced", layout="wide")
30
 
31
- # Helper Functions
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  def get_model_config(model_type, problem_type):
33
  configs = {
34
  "Random Forest": {
@@ -54,6 +105,11 @@ def get_model_config(model_type, problem_type):
54
  "Linear Regression": {
55
  "Regression": {"model_class": LinearRegression, "params": {}, "grid_params": {}}
56
  },
 
 
 
 
 
57
  "K-Means": {
58
  "Clustering": {"model_class": KMeans, "params": {"n_clusters": 3, "random_state": 42},
59
  "grid_params": {"n_clusters": [2, 3, 4, 5]}}
@@ -68,85 +124,130 @@ def get_model_config(model_type, problem_type):
68
  }
69
  }
70
  return configs.get(model_type, {}).get(problem_type, {"model_class": None, "params": {}, "grid_params": {}})
71
-
72
  def preprocess_data(X_train, X_test, numerical_features, categorical_features):
73
- # Define the numeric and categorical transformers
74
  numeric_transformer = Pipeline(steps=[
75
  ('imputer', SimpleImputer(strategy='mean')),
76
  ('scaler', StandardScaler())])
77
-
78
  categorical_transformer = Pipeline(steps=[
79
  ('imputer', SimpleImputer(strategy='most_frequent')),
80
  ('onehot', OneHotEncoder(handle_unknown='ignore', sparse_output=False))])
81
-
82
- # Combine transformers into a ColumnTransformer
83
  preprocessor = ColumnTransformer(
84
  transformers=[
85
  ('num', numeric_transformer, numerical_features),
86
  ('cat', categorical_transformer, categorical_features)],
87
- remainder='passthrough') # Handle unseen columns
88
-
89
- # Fit and transform the training data
90
  X_train_processed = preprocessor.fit_transform(X_train)
91
-
92
- # Transform the test data
93
  X_test_processed = preprocessor.transform(X_test)
94
-
95
- # Get feature names after one-hot encoding
96
  if categorical_features:
97
- # Access the fitted OneHotEncoder
98
  onehot_encoder = preprocessor.named_transformers_['cat'].named_steps['onehot']
99
  categorical_feature_names = onehot_encoder.get_feature_names_out(categorical_features)
100
  feature_names = numerical_features + list(categorical_feature_names)
101
  else:
102
  feature_names = numerical_features
103
-
104
  return X_train_processed, X_test_processed, feature_names, preprocessor
105
- def build_neural_network(input_shape, output_units, problem_type, layers_config, optimizer_name="Adam", loss_function="mse", metrics=["accuracy"]):
106
- try:
107
- model = keras.Sequential()
108
- model.add(keras.layers.InputLayer(input_shape=input_shape))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  for layer in layers_config:
110
  if layer['type'] == 'dense':
111
- model.add(keras.layers.Dense(layer['units'], activation=layer['activation']))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  elif layer['type'] == 'dropout':
113
- model.add(keras.layers.Dropout(layer['rate']))
114
- elif layer['type'] == 'conv2d':
115
- model.add(keras.layers.Conv2D(layer['filters'], tuple(layer['kernel_size']), activation=layer['activation']))
116
- elif layer['type'] == 'lstm':
117
- model.add(keras.layers.LSTM(layer['units'], activation=layer['activation']))
118
- elif layer['type'] == 'maxpooling2d':
119
- model.add(keras.layers.MaxPooling2D(pool_size=tuple(layer['pool_size'])))
120
- elif layer['type'] == 'flatten':
121
- model.add(keras.layers.Flatten())
122
-
123
- if problem_type == "Regression":
124
- model.add(keras.layers.Dense(1))
125
- elif problem_type == "Binary Classification":
126
- model.add(keras.layers.Dense(1, activation='sigmoid'))
127
- elif problem_type == "Multi-Class":
128
- model.add(keras.layers.Dense(output_units, activation='softmax'))
129
-
130
- if optimizer_name == "Adam":
131
- optimizer = keras.optimizers.Adam()
132
- elif optimizer_name == "SGD":
133
- optimizer = keras.optimizers.SGD()
134
- elif optimizer_name == "RMSprop":
135
- optimizer = keras.optimizers.RMSprop()
136
-
137
- model.compile(optimizer=optimizer, loss=loss_function, metrics=metrics)
138
- return model
139
- except Exception as e:
140
- st.error("Failed to build neural network. Check Debug Log for details.")
141
- #log_error("Error in build_neural_network", e) # ADDED
142
- raise
143
-
144
 
145
- def train_model(model, X_train, y_train, X_test, y_test, epochs, batch_size, problem_type, do_grid_search=False, params=None, grid_params=None, training_placeholder=None):
146
  start_time = time.time()
147
  history = None
 
 
 
 
148
  if isinstance(model, keras.Model):
149
- # Define a callback to update Streamlit during training
150
  class StreamlitCallback(keras.callbacks.Callback):
151
  def __init__(self, placeholder):
152
  super().__init__()
@@ -157,31 +258,28 @@ def train_model(model, X_train, y_train, X_test, y_test, epochs, batch_size, pro
157
  self.epoch_data.append(logs)
158
  df = pd.DataFrame(self.epoch_data)
159
  fig = px.line(df, x=df.index, y=['loss', 'val_loss'], labels={'index': 'Epoch', 'value': 'Loss'})
160
- fig.add_trace(go.Scatter(x=df.index, y=df['accuracy'], mode='lines', name='accuracy'))
161
- fig.add_trace(go.Scatter(x=df.index, y=df['val_accuracy'], mode='lines', name='val_accuracy'))
162
-
163
  self.placeholder.plotly_chart(fig)
164
 
165
  streamlit_callback = StreamlitCallback(training_placeholder)
166
- history = model.fit(X_train, y_train, epochs=epochs, batch_size=batch_size,
167
- validation_data=(X_test, y_test), verbose=0,
168
- callbacks=[streamlit_callback])
169
-
170
  else:
171
  if do_grid_search and grid_params:
172
- grid_search = GridSearchCV(model, grid_params, cv=3, n_jobs=-1, scoring='accuracy' if problem_type in ["Binary Classification", "Multi-Class"] else 'neg_mean_squared_error') # Added scoring
173
  grid_search.fit(X_train, y_train)
174
  model = grid_search.best_estimator_
175
- st.write("Best parameters found by Grid Search:", grid_search.best_params_) # Print best params
176
  else:
177
  model.set_params(**params)
178
  model.fit(X_train, y_train)
179
- history = None # Reset to none here
180
-
181
  training_time = time.time() - start_time
182
  return history, model, training_time
183
 
184
- def evaluate_model(model, X_test, y_test, problem_type, le=None):
185
  y_pred = model.predict(X_test)
186
  metrics = {}
187
  if problem_type == "Regression":
@@ -190,9 +288,13 @@ def evaluate_model(model, X_test, y_test, problem_type, le=None):
190
  metrics['rmse'] = np.sqrt(metrics['mse'])
191
  metrics['r2'] = r2_score(y_test, y_pred)
192
  return metrics, y_pred.flatten()
193
- elif problem_type in ["Binary Classification", "Multi-Class"]:
194
- y_pred_classes = (y_pred > 0.5).astype(int).flatten() if problem_type == "Binary Classification" else np.argmax(y_pred, axis=1)
195
- y_test_classes = y_test if problem_type == "Binary Classification" else np.argmax(y_test, axis=1)
 
 
 
 
196
  metrics['accuracy'] = accuracy_score(y_test_classes, y_pred_classes)
197
  metrics['precision'] = precision_score(y_test_classes, y_pred_classes, average='weighted', zero_division=0)
198
  metrics['recall'] = recall_score(y_test_classes, y_pred_classes, average='weighted', zero_division=0)
@@ -200,7 +302,16 @@ def evaluate_model(model, X_test, y_test, problem_type, le=None):
200
  return metrics, y_pred_classes
201
  elif problem_type == "Clustering":
202
  labels = model.labels_ if hasattr(model, 'labels_') else model.predict(X_test)
203
- return {"n_clusters": len(np.unique(labels))}, labels
 
 
 
 
 
 
 
 
 
204
 
205
  def save_model(model, preprocessor, features, target, problem_type, filename="model.pkl"):
206
  model_data = {
@@ -229,42 +340,63 @@ with st.sidebar:
229
  st.markdown("Your AI-powered model toolbox.")
230
  st.markdown("---")
231
  app_mode = st.selectbox("Navigation", ["Data Upload", "Model Training", "Validation & Exploration"])
 
232
  st.markdown("---")
233
- st.markdown("**Dependencies**: `tensorflow`, `shap`, `umap-learn`, `joblib`, `scikit-learn`, `plotly`, `xgboost`")
234
- st.markdown("Created by Calvin Allen-Crawford | v1.2 | © 2025")
235
 
236
  # Main App Sections
237
  if app_mode == "Data Upload":
238
  st.title("📤 Data Upload")
239
  col1, col2, col3 = st.columns([1, 2, 1])
240
  with col2:
241
- uploaded_file = st.file_uploader("Upload CSV Dataset", type=["csv"])
242
-
243
- if uploaded_file:
244
- df = pd.read_csv(uploaded_file)
245
- st.session_state.df = df
246
- st.write("---")
247
- st.subheader("Dataset Preview")
248
- st.dataframe(df.head(10))
249
- st.write("---")
250
- st.subheader("Statistics")
251
- col1, col2, col3 = st.columns(3)
252
- with col1: st.metric("Rows", df.shape[0])
253
- with col2: st.metric("Columns", df.shape[1])
254
- with col3: st.metric("Missing Values", df.isna().sum().sum())
 
 
 
 
 
 
 
 
 
 
 
 
255
 
256
  elif app_mode == "Model Training":
257
  st.title("🧠 Model Training")
258
- if 'df' not in st.session_state:
259
- st.warning("Please upload a dataset first.")
 
 
 
260
  st.stop()
261
 
262
- df = st.session_state.df
263
- problem_type = st.selectbox("Problem Type", ["Regression", "Binary Classification", "Multi-Class", "Clustering"])
264
- features = st.multiselect("Select Features", df.columns)
265
- target = st.selectbox("Select Target", df.columns) if problem_type != "Clustering" else None
 
 
 
 
 
266
 
267
- if problem_type != "Clustering" and target:
268
  unique_target_values = df[target].nunique()
269
  if problem_type == "Binary Classification" and unique_target_values != 2:
270
  st.error("Binary Classification requires exactly 2 unique target values.")
@@ -277,17 +409,20 @@ elif app_mode == "Model Training":
277
  st.stop()
278
 
279
  model_types = {
280
- "Regression": ["Neural Network", "Random Forest", "XGBoost", "Linear Regression"],
281
- "Binary Classification": ["Neural Network", "Random Forest", "XGBoost", "Logistic Regression"],
282
- "Multi-Class": ["Neural Network", "Random Forest", "XGBoost"],
283
- "Clustering": ["K-Means", "DBSCAN", "Gaussian Mixture"]
 
 
284
  }[problem_type]
285
  model_type = st.selectbox("Model Type", model_types)
286
 
287
  if model_type == "Neural Network":
288
  st.subheader("Neural Network Configuration")
 
289
  layers_config = st.session_state.get('layers_config', [])
290
- layer_type = st.selectbox("Layer Type", ["Dense", "Dropout"])
291
  if layer_type == "Dense":
292
  units = st.number_input("Units", min_value=1, value=64)
293
  activation = st.selectbox("Activation", ["relu", "sigmoid", "tanh"])
@@ -297,14 +432,50 @@ elif app_mode == "Model Training":
297
  rate = st.number_input("Dropout Rate", 0.0, 1.0, 0.2)
298
  if st.button("Add Layer"):
299
  layers_config.append({"type": "dropout", "rate": rate})
300
-
 
 
 
 
 
 
 
 
 
 
 
 
301
  if layers_config:
302
  st.write("Current Layers:", layers_config)
303
  if st.button("Clear Layers"):
304
  layers_config.clear()
305
  st.session_state.layers_config = layers_config
306
  st.rerun()
307
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
  st.session_state.layers_config = layers_config
309
  else:
310
  st.subheader("Model Hyperparameters")
@@ -312,15 +483,8 @@ elif app_mode == "Model Training":
312
  params = {}
313
  for param_name, param_values in config["grid_params"].items():
314
  if isinstance(param_values[0], (int, float)) and len(param_values) > 2:
315
- slider_value = st.slider(
316
- param_name,
317
- min_value=float(min(param_values)),
318
- max_value=float(max(param_values)),
319
- value=float(param_values[1])
320
- )
321
- # CAST TO INT FOR INTEGER PARAMETERS
322
- if param_name in {'n_estimators', 'n_clusters', 'min_samples',
323
- 'n_components', 'max_depth'}:
324
  params[param_name] = int(slider_value)
325
  else:
326
  params[param_name] = slider_value
@@ -329,52 +493,75 @@ elif app_mode == "Model Training":
329
  do_grid_search = st.checkbox("Use Grid Search for Tuning", value=False)
330
 
331
  col1, col2, col3 = st.columns(3)
332
- with col1: epochs = st.number_input("Epochs", min_value=1, value=10) if model_type == "Neural Network" else 10
333
- with col2: batch_size = st.number_input("Batch Size", min_value=1, value=32) if model_type == "Neural Network" else 32
334
- with col3: learning_rate = st.number_input("Learning Rate", min_value=0.0, value=0.001, step=0.0001) if model_type == "Neural Network" else 0.001
335
 
336
- uploaded_model = st.file_uploader("Upload Pre-trained Model (.h5)", type=["h5"]) if model_type == "Neural Network" else None
337
  base_model = keras.models.load_model(uploaded_model) if uploaded_model else None
338
 
339
  if st.button("Train Model"):
340
  with st.spinner("Preparing data..."):
341
- X = df[features]
342
- y = df[target] if problem_type != "Clustering" else None
343
- X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) if problem_type != "Clustering" else (X, X.copy(), None, None)
344
- numerical_features = X.select_dtypes(include=np.number).columns.tolist()
345
- categorical_features = X.select_dtypes(exclude=np.number).columns.tolist()
346
- X_train_processed, X_test_processed, feature_names, preprocessor = preprocess_data(X_train, X_test, numerical_features, categorical_features)
347
-
348
- le = None
349
- if problem_type in ["Binary Classification", "Multi-Class"] and y is not None:
350
- le = LabelEncoder()
351
- y_train = le.fit_transform(y_train)
352
- y_test = le.transform(y_test)
353
- if problem_type == "Multi-Class":
354
- y_train = tf.keras.utils.to_categorical(y_train)
355
- y_test = tf.keras.utils.to_categorical(y_test)
 
 
 
 
 
356
 
357
  with st.spinner("Training model..."):
358
- training_placeholder = st.empty() # Placeholder for real-time training updates
359
  if model_type == "Neural Network":
360
  if not layers_config and not base_model:
361
  st.error("Please add layers or upload a pre-trained model.")
362
  st.stop()
363
- model = base_model if base_model else build_neural_network(X_train_processed.shape[1:], y_train.shape[1] if problem_type == "Multi-Class" else 1,
364
- problem_type, layers_config, "Adam", learning_rate)
365
- history, model, training_time = train_model(model, X_train_processed, y_train, X_test_processed, y_test, epochs, batch_size, problem_type,
366
- training_placeholder=training_placeholder)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
367
  else:
368
  config = get_model_config(model_type, problem_type)
369
  model = config['model_class'](**config['params'])
370
- history, model, training_time = train_model(model, X_train_processed, y_train, X_test_processed, y_test, epochs, batch_size, problem_type,
371
- do_grid_search, params, config['grid_params'])
372
- st.subheader("Training Metrics")
373
- if history:
374
- fig = px.line(x=range(len(history.history['loss'])), y=history.history['loss'], labels={'x':'Epoch', 'y':'Loss'})
375
- st.plotly_chart(fig)
376
- else:
377
- st.write("No history available for this model type.")
378
 
379
  st.session_state.model = model
380
  st.session_state.preprocessor = preprocessor
@@ -390,115 +577,153 @@ elif app_mode == "Model Training":
390
 
391
  elif app_mode == "Validation & Exploration":
392
  st.title("🔍 Validation & Exploration")
393
- if 'model' not in st.session_state or 'df' not in st.session_state:
394
- st.warning("Please upload a dataset and train a model first.")
 
 
 
395
  st.stop()
396
 
397
- df = st.session_state.df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
398
  model = st.session_state.model
399
- preprocessor = st.session_state.preprocessor
400
- features = st.session_state.features
401
- target = st.session_state.target
402
  problem_type = st.session_state.problem_type
403
- le = st.session_state.le
404
-
405
- X = df[features]
406
- y = df[target] if problem_type != "Clustering" else None
407
- X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) if problem_type != "Clustering" else (X, X.copy(), None, None)
408
-
409
- # FIXED FEATURE SELECTION
410
- numerical_features = X.select_dtypes(include=np.number).columns.tolist()
411
- categorical_features = X.select_dtypes(exclude=np.number).columns.tolist()
412
- X_train_processed, X_test_processed, feature_names, _ = preprocess_data(X_train, X_test, numerical_features, categorical_features)
413
-
414
- if problem_type in ["Binary Classification", "Multi-Class"] and y is not None:
415
- y_train = le.transform(y_train) if le else y_train
416
- y_test = le.transform(y_test) if le else y_test
417
- if problem_type == "Multi-Class":
418
- y_train = tf.keras.utils.to_categorical(y_train)
419
- y_test = tf.keras.utils.to_categorical(y_test)
420
 
421
  # Validation
422
  st.subheader("Model Validation")
423
- metrics, y_pred = evaluate_model(model, X_test_processed, y_test, problem_type, le)
 
 
 
 
424
  col1, col2 = st.columns(2)
425
  with col1:
426
  for metric, value in metrics.items():
427
  st.metric(metric, f"{value:.4f}" if isinstance(value, float) else value)
428
-
429
  with col2:
430
  if problem_type == "Regression":
431
  fig = px.scatter(x=y_test, y=y_pred, labels={"x": "Actual", "y": "Predicted"}, title="Predicted vs Actual")
432
  st.plotly_chart(fig)
433
- elif problem_type == "Binary Classification":
434
- y_pred_proba = model.predict_proba(X_test_processed)[:, 1] if hasattr(model, 'predict_proba') else y_pred # Handle cases where predict_proba isn't available
435
- fpr, tpr, _ = roc_curve(y_test, y_pred_proba)
436
- roc_auc = auc(fpr, tpr)
437
- fig = px.area(x=fpr, y=tpr, title=f"ROC Curve (AUC = {roc_auc:.2f})",
438
- labels={"x": "False Positive Rate", "y": "True Positive Rate"})
439
- st.plotly_chart(fig)
 
 
 
 
 
 
 
 
440
  elif problem_type == "Multi-Class":
441
- y_pred_classes = np.argmax(model.predict(X_test_processed), axis=1) if isinstance(model, keras.Model) else model.predict(X_test_processed)
442
  y_test_classes = np.argmax(y_test, axis=1)
443
  cm = np.zeros((y_train.shape[1], y_train.shape[1]))
444
  for i, j in zip(y_test_classes, y_pred_classes):
445
  cm[i, j] += 1
446
  fig = px.imshow(cm, title="Confusion Matrix", labels={"x": "Predicted", "y": "Actual"})
447
  st.plotly_chart(fig)
448
-
449
- # Display Classification Report
450
- report = classification_report(y_test_classes, y_pred_classes, target_names=le.classes_ if le else [str(i) for i in range(y_train.shape[1])], zero_division=0)
451
  st.text("Classification Report:\n" + report)
452
  elif problem_type == "Clustering":
453
  labels = y_pred
454
- fig = px.scatter(x=X_test_processed[:, 0], y=X_test_processed[:, 1], color=labels, title="Cluster Visualization")
 
 
 
 
 
 
455
  st.plotly_chart(fig)
456
 
457
  # Dimensionality Reduction
458
  st.subheader("Dimensionality Reduction")
459
- method = st.selectbox("Method", ["PCA", "SVD", "t-SNE", "UMAP"])
460
- n_components = st.slider("Components", 2, min(X_train_processed.shape[1], 10), 2)
461
-
462
- if method == "PCA":
463
- reducer = PCA(n_components=n_components)
464
- X_reduced = reducer.fit_transform(X_train_processed)
465
- fig = px.bar(x=range(n_components), y=reducer.explained_variance_ratio_, title="Explained Variance Ratio")
466
- st.plotly_chart(fig)
467
- elif method == "SVD":
468
- reducer = TruncatedSVD(n_components=n_components)
469
- X_reduced = reducer.fit_transform(X_train_processed)
470
- fig = px.bar(x=range(n_components), y=reducer.explained_variance_ratio_, title="Explained Variance Ratio")
471
- st.plotly_chart(fig)
472
- elif method == "t-SNE":
473
- X_reduced = TSNE(n_components=n_components, random_state=42).fit_transform(X_train_processed)
474
- elif method == "UMAP":
475
- X_reduced = umap.UMAP(n_components=n_components, random_state=42).fit_transform(X_train_processed)
 
 
 
 
 
 
 
 
 
 
 
 
476
 
477
  if n_components >= 2:
478
- fig = px.scatter(x=X_reduced[:, 0], y=X_reduced[:, 1], color=y_train if problem_type != "Clustering" else y_pred,
479
- title=f"{method} Visualization")
 
 
 
 
480
  st.plotly_chart(fig)
481
 
482
  # Interpretability
483
- st.subheader("Interpretability")
484
- try:
485
- explainer = shap.KernelExplainer(model.predict, X_test_processed[:50]) if isinstance(model, keras.Model) else shap.Explainer(model, X_test_processed)
486
- shap_values = explainer.shap_values(X_test_processed[:50])
487
-
488
- if problem_type == "Regression":
489
- shap_fig, ax = plt.subplots()
490
- shap.summary_plot(shap_values, X_test_processed[:50], feature_names=feature_names, show=False)
491
- st.pyplot(shap_fig)
492
- elif problem_type in ["Binary Classification", "Multi-Class"]:
493
- class_names = le.classes_ if le else [str(i) for i in range(y_train.shape[1])]
494
- for i in range(len(class_names)):
495
  shap_fig, ax = plt.subplots()
496
- shap.summary_plot(shap_values[i], X_test_processed[:50], feature_names=feature_names, class_names=class_names, show=False)
497
  st.pyplot(shap_fig)
498
- else:
499
- st.write("SHAP plots are not directly applicable to Clustering problems.")
500
- except Exception as e:
501
- st.error(f"Error generating SHAP plot: {e}")
 
 
 
 
502
 
503
  # Custom CSS
504
  st.markdown("""
 
12
  import umap.umap_ as umap
13
  import shap
14
  import joblib
15
+ from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_curve, auc, mean_squared_error, mean_absolute_error, r2_score, classification_report, silhouette_score
16
  from sklearn.pipeline import Pipeline
17
  from sklearn.compose import ColumnTransformer
18
  from sklearn.impute import SimpleImputer
 
20
  from sklearn.mixture import GaussianMixture
21
  from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor
22
  from sklearn.linear_model import LogisticRegression, LinearRegression
23
+ from sklearn.svm import SVC, SVR
24
  from xgboost import XGBClassifier, XGBRegressor
25
  import matplotlib.pyplot as plt
26
  from io import BytesIO
27
  import time
28
+ from PIL import Image
29
+ import zipfile
30
+ import os
31
 
32
  # Set page config
33
  st.set_page_config(page_title="Neural-Vision Enhanced", layout="wide")
34
 
35
+ # Helper Functions for Image Processing
36
+ def preprocess_image(image_path, target_size=(224, 224)):
37
+ """Preprocess an image by resizing and normalizing it."""
38
+ img = Image.open(image_path).convert("RGB")
39
+ img = img.resize(target_size)
40
+ img_array = np.array(img) / 255.0 # Normalize pixel values to [0, 1]
41
+ return img_array
42
+
43
+ def load_image_dataset(zip_path, target_size=(224, 224), problem_type="Classification"):
44
+ """Load and preprocess an image dataset from a zip file."""
45
+ # Extract zip file to a temporary directory
46
+ with zipfile.ZipFile(zip_path, 'r') as zip_ref:
47
+ zip_ref.extractall('temp_images')
48
+
49
+ if problem_type == "Classification":
50
+ image_paths = []
51
+ labels = []
52
+ class_names = sorted(os.listdir('temp_images'))
53
+ for label, class_name in enumerate(class_names):
54
+ class_dir = os.path.join('temp_images', class_name)
55
+ if os.path.isdir(class_dir):
56
+ for img_name in os.listdir(class_dir):
57
+ image_path = os.path.join(class_dir, img_name)
58
+ if os.path.isfile(image_path):
59
+ image_paths.append(image_path)
60
+ labels.append(label)
61
+ images = [preprocess_image(path, target_size) for path in image_paths]
62
+ images = np.array(images)
63
+ labels = np.array(labels)
64
+ data = (images, labels, class_names)
65
+ else: # Compression or Clustering
66
+ image_dir = 'temp_images'
67
+ image_paths = [os.path.join(image_dir, img_name) for img_name in os.listdir(image_dir) if os.path.isfile(os.path.join(image_dir, img_name))]
68
+ images = [preprocess_image(path, target_size) for path in image_paths]
69
+ images = np.array(images)
70
+ data = (images, None, None)
71
+
72
+ # Clean up temporary directory
73
+ for root, dirs, files in os.walk('temp_images', topdown=False):
74
+ for name in files:
75
+ os.remove(os.path.join(root, name))
76
+ for name in dirs:
77
+ os.rmdir(os.path.join(root, name))
78
+ os.rmdir('temp_images')
79
+
80
+ return data
81
+
82
+ # Model Building Functions
83
  def get_model_config(model_type, problem_type):
84
  configs = {
85
  "Random Forest": {
 
105
  "Linear Regression": {
106
  "Regression": {"model_class": LinearRegression, "params": {}, "grid_params": {}}
107
  },
108
+ "SVM": {
109
+ "Regression": {"model_class": SVR, "params": {"kernel": "rbf"}, "grid_params": {"C": [0.1, 1, 10], "gamma": ["scale", "auto"]}},
110
+ "Binary Classification": {"model_class": SVC, "params": {"kernel": "rbf", "random_state": 42}, "grid_params": {"C": [0.1, 1, 10], "gamma": ["scale", "auto"]}},
111
+ "Multi-Class": {"model_class": SVC, "params": {"kernel": "rbf", "random_state": 42}, "grid_params": {"C": [0.1, 1, 10], "gamma": ["scale", "auto"]}}
112
+ },
113
  "K-Means": {
114
  "Clustering": {"model_class": KMeans, "params": {"n_clusters": 3, "random_state": 42},
115
  "grid_params": {"n_clusters": [2, 3, 4, 5]}}
 
124
  }
125
  }
126
  return configs.get(model_type, {}).get(problem_type, {"model_class": None, "params": {}, "grid_params": {}})
127
+
128
  def preprocess_data(X_train, X_test, numerical_features, categorical_features):
 
129
  numeric_transformer = Pipeline(steps=[
130
  ('imputer', SimpleImputer(strategy='mean')),
131
  ('scaler', StandardScaler())])
 
132
  categorical_transformer = Pipeline(steps=[
133
  ('imputer', SimpleImputer(strategy='most_frequent')),
134
  ('onehot', OneHotEncoder(handle_unknown='ignore', sparse_output=False))])
 
 
135
  preprocessor = ColumnTransformer(
136
  transformers=[
137
  ('num', numeric_transformer, numerical_features),
138
  ('cat', categorical_transformer, categorical_features)],
139
+ remainder='drop')
 
 
140
  X_train_processed = preprocessor.fit_transform(X_train)
 
 
141
  X_test_processed = preprocessor.transform(X_test)
 
 
142
  if categorical_features:
 
143
  onehot_encoder = preprocessor.named_transformers_['cat'].named_steps['onehot']
144
  categorical_feature_names = onehot_encoder.get_feature_names_out(categorical_features)
145
  feature_names = numerical_features + list(categorical_feature_names)
146
  else:
147
  feature_names = numerical_features
 
148
  return X_train_processed, X_test_processed, feature_names, preprocessor
149
+
150
+ def build_neural_network(input_shape, output_units, problem_type, layers_config, optimizer_name="Adam", learning_rate=0.001):
151
+ model = keras.Sequential()
152
+ model.add(keras.layers.InputLayer(input_shape=input_shape))
153
+ for layer in layers_config:
154
+ if layer['type'] == 'dense':
155
+ model.add(keras.layers.Dense(layer['units'], activation=layer['activation']))
156
+ elif layer['type'] == 'dropout':
157
+ model.add(keras.layers.Dropout(layer['rate']))
158
+ elif layer['type'] == 'conv2d':
159
+ model.add(keras.layers.Conv2D(layer['filters'], tuple(layer['kernel_size']), activation=layer['activation'], padding='same'))
160
+ elif layer['type'] == 'maxpooling2d':
161
+ model.add(keras.layers.MaxPooling2D(pool_size=tuple(layer['pool_size'])))
162
+ elif layer['type'] == 'flatten':
163
+ model.add(keras.layers.Flatten())
164
+ if problem_type == "Regression":
165
+ model.add(keras.layers.Dense(1))
166
+ loss_function = "mse"
167
+ metrics = ["mse"]
168
+ elif problem_type == "Binary Classification":
169
+ model.add(keras.layers.Dense(1, activation='sigmoid'))
170
+ loss_function = "binary_crossentropy"
171
+ metrics = ["accuracy"]
172
+ elif problem_type == "Multi-Class" or problem_type == "Image Classification":
173
+ model.add(keras.layers.Dense(output_units, activation='softmax'))
174
+ loss_function = "sparse_categorical_crossentropy" if problem_type == "Image Classification" else "categorical_crossentropy"
175
+ metrics = ["accuracy"]
176
+ else:
177
+ raise ValueError("Unsupported problem type")
178
+ optimizer = {"Adam": keras.optimizers.Adam, "SGD": keras.optimizers.SGD, "RMSprop": keras.optimizers.RMSprop}.get(optimizer_name)(learning_rate=learning_rate)
179
+ model.compile(optimizer=optimizer, loss=loss_function, metrics=metrics)
180
+ return model
181
+
182
+ def build_autoencoder(input_shape, encoding_dim, layers_config, autoencoder_type="Standard", optimizer_name="Adam", learning_rate=0.001):
183
+ if autoencoder_type == "Variational":
184
+ inputs = keras.layers.Input(shape=input_shape)
185
+ x = keras.layers.Flatten()(inputs) if len(input_shape) > 1 else inputs
186
  for layer in layers_config:
187
  if layer['type'] == 'dense':
188
+ x = keras.layers.Dense(layer['units'], activation=layer['activation'])(x)
189
+ z_mean = keras.layers.Dense(encoding_dim, name='z_mean')(x)
190
+ z_log_var = keras.layers.Dense(encoding_dim, name='z_log_var')(x)
191
+
192
+ def sampling(args):
193
+ z_mean, z_log_var = args
194
+ epsilon = keras.backend.random_normal(shape=(keras.backend.shape(z_mean)[0], encoding_dim))
195
+ return z_mean + keras.backend.exp(0.5 * z_log_var) * epsilon
196
+
197
+ z = keras.layers.Lambda(sampling, name='z')([z_mean, z_log_var])
198
+ encoder = keras.Model(inputs, [z_mean, z_log_var, z], name='encoder')
199
+
200
+ decoder_input = keras.layers.Input(shape=(encoding_dim,))
201
+ x = decoder_input
202
+ for layer in reversed(layers_config):
203
+ if layer['type'] == 'dense':
204
+ x = keras.layers.Dense(layer['units'], activation=layer['activation'])(x)
205
+ x = keras.layers.Dense(np.prod(input_shape), activation='sigmoid')(x)
206
+ outputs = keras.layers.Reshape(input_shape)(x) if len(input_shape) > 1 else x
207
+ decoder = keras.Model(decoder_input, outputs, name='decoder')
208
+
209
+ vae_outputs = decoder(encoder(inputs)[2])
210
+ autoencoder = keras.Model(inputs, vae_outputs, name='vae')
211
+
212
+ reconstruction_loss = keras.losses.binary_crossentropy(keras.backend.flatten(inputs), keras.backend.flatten(vae_outputs))
213
+ reconstruction_loss *= np.prod(input_shape)
214
+ kl_loss = 1 + z_log_var - keras.backend.square(z_mean) - keras.backend.exp(z_log_var)
215
+ kl_loss = keras.backend.sum(kl_loss, axis=-1) * -0.5
216
+ vae_loss = keras.backend.mean(reconstruction_loss + kl_loss)
217
+ autoencoder.add_loss(vae_loss)
218
+ else: # Standard or Denoising
219
+ encoder = keras.Sequential([keras.layers.InputLayer(input_shape=input_shape)])
220
+ for layer in layers_config:
221
+ if layer['type'] == 'dense':
222
+ encoder.add(keras.layers.Dense(layer['units'], activation=layer['activation']))
223
  elif layer['type'] == 'dropout':
224
+ encoder.add(keras.layers.Dropout(layer['rate']))
225
+ encoder.add(keras.layers.Dense(encoding_dim, activation='relu', name='encoded'))
226
+
227
+ decoder = keras.Sequential([keras.layers.InputLayer(input_shape=(encoding_dim,))])
228
+ for layer in reversed(layers_config):
229
+ if layer['type'] == 'dense':
230
+ decoder.add(keras.layers.Dense(layer['units'], activation=layer['activation']))
231
+ decoder.add(keras.layers.Dense(np.prod(input_shape), activation='sigmoid'))
232
+ decoder.add(keras.layers.Reshape(input_shape) if len(input_shape) > 1 else keras.layers.Lambda(lambda x: x))
233
+
234
+ autoencoder_input = keras.layers.Input(shape=input_shape)
235
+ encoded = encoder(autoencoder_input)
236
+ decoded = decoder(encoded)
237
+ autoencoder = keras.Model(autoencoder_input, decoded)
238
+
239
+ optimizer = {"Adam": keras.optimizers.Adam, "SGD": keras.optimizers.SGD, "RMSprop": keras.optimizers.RMSprop}.get(optimizer_name)(learning_rate=learning_rate)
240
+ autoencoder.compile(optimizer=optimizer, loss='mse', metrics=['mse'])
241
+ return autoencoder, encoder, decoder
 
 
 
 
 
 
 
 
 
 
 
 
 
242
 
243
+ def train_model(model, X_train, y_train, X_test, y_test, epochs, batch_size, problem_type, input_data=None, target_data=None, do_grid_search=False, params=None, grid_params=None, training_placeholder=None):
244
  start_time = time.time()
245
  history = None
246
+ if input_data is None:
247
+ input_data = X_train
248
+ if target_data is None:
249
+ target_data = y_train if y_train is not None else X_train
250
  if isinstance(model, keras.Model):
 
251
  class StreamlitCallback(keras.callbacks.Callback):
252
  def __init__(self, placeholder):
253
  super().__init__()
 
258
  self.epoch_data.append(logs)
259
  df = pd.DataFrame(self.epoch_data)
260
  fig = px.line(df, x=df.index, y=['loss', 'val_loss'], labels={'index': 'Epoch', 'value': 'Loss'})
261
+ metric_name = 'mse' if 'mse' in logs else 'accuracy'
262
+ fig.add_trace(go.Scatter(x=df.index, y=df[metric_name], mode='lines', name=metric_name))
263
+ fig.add_trace(go.Scatter(x=df.index, y=df[f'val_{metric_name}'], mode='lines', name=f'val_{metric_name}'))
264
  self.placeholder.plotly_chart(fig)
265
 
266
  streamlit_callback = StreamlitCallback(training_placeholder)
267
+ history = model.fit(input_data, target_data, epochs=epochs, batch_size=batch_size,
268
+ validation_data=(X_test, y_test if y_test is not None else X_test), verbose=0,
269
+ callbacks=[streamlit_callback])
 
270
  else:
271
  if do_grid_search and grid_params:
272
+ grid_search = GridSearchCV(model, grid_params, cv=3, n_jobs=-1, scoring='accuracy' if problem_type in ["Binary Classification", "Multi-Class"] else 'neg_mean_squared_error')
273
  grid_search.fit(X_train, y_train)
274
  model = grid_search.best_estimator_
275
+ st.write("Best parameters found by Grid Search:", grid_search.best_params_)
276
  else:
277
  model.set_params(**params)
278
  model.fit(X_train, y_train)
 
 
279
  training_time = time.time() - start_time
280
  return history, model, training_time
281
 
282
+ def evaluate_model(model, X_test, y_test, problem_type, le=None, encoder=None):
283
  y_pred = model.predict(X_test)
284
  metrics = {}
285
  if problem_type == "Regression":
 
288
  metrics['rmse'] = np.sqrt(metrics['mse'])
289
  metrics['r2'] = r2_score(y_test, y_pred)
290
  return metrics, y_pred.flatten()
291
+ elif problem_type in ["Binary Classification", "Multi-Class", "Image Classification"]:
292
+ if problem_type == "Image Classification":
293
+ y_pred_classes = np.argmax(y_pred, axis=1)
294
+ y_test_classes = y_test
295
+ else:
296
+ y_pred_classes = (y_pred > 0.5).astype(int).flatten() if problem_type == "Binary Classification" else np.argmax(y_pred, axis=1)
297
+ y_test_classes = y_test if problem_type == "Binary Classification" else np.argmax(y_test, axis=1)
298
  metrics['accuracy'] = accuracy_score(y_test_classes, y_pred_classes)
299
  metrics['precision'] = precision_score(y_test_classes, y_pred_classes, average='weighted', zero_division=0)
300
  metrics['recall'] = recall_score(y_test_classes, y_pred_classes, average='weighted', zero_division=0)
 
302
  return metrics, y_pred_classes
303
  elif problem_type == "Clustering":
304
  labels = model.labels_ if hasattr(model, 'labels_') else model.predict(X_test)
305
+ metrics["n_clusters"] = len(np.unique(labels))
306
+ if len(np.unique(labels)) > 1:
307
+ metrics["silhouette"] = silhouette_score(X_test, labels)
308
+ return metrics, labels
309
+ elif problem_type == "Compression":
310
+ metrics['mse'] = mean_squared_error(X_test, y_pred)
311
+ metrics['mae'] = mean_absolute_error(X_test, y_pred)
312
+ metrics['rmse'] = np.sqrt(metrics['mse'])
313
+ compressed_data = encoder.predict(X_test) if encoder else None
314
+ return metrics, y_pred, compressed_data
315
 
316
  def save_model(model, preprocessor, features, target, problem_type, filename="model.pkl"):
317
  model_data = {
 
340
  st.markdown("Your AI-powered model toolbox.")
341
  st.markdown("---")
342
  app_mode = st.selectbox("Navigation", ["Data Upload", "Model Training", "Validation & Exploration"])
343
+ data_type = st.selectbox("Data Type", ["Tabular", "Image"])
344
  st.markdown("---")
345
+ st.markdown("**Dependencies**: `tensorflow`, `shap`, `umap-learn`, `joblib`, `scikit-learn`, `plotly`, `xgboost`, `pillow`")
346
+ st.markdown("Created by Calvin Allen-Crawford | v1.3 | © 2025")
347
 
348
  # Main App Sections
349
  if app_mode == "Data Upload":
350
  st.title("📤 Data Upload")
351
  col1, col2, col3 = st.columns([1, 2, 1])
352
  with col2:
353
+ if data_type == "Tabular":
354
+ uploaded_file = st.file_uploader("Upload CSV Dataset", type=["csv"])
355
+ if uploaded_file:
356
+ df = pd.read_csv(uploaded_file)
357
+ st.session_state.df = df
358
+ st.write("---")
359
+ st.subheader("Dataset Preview")
360
+ st.dataframe(df.head(10))
361
+ st.write("---")
362
+ st.subheader("Statistics")
363
+ col1, col2, col3 = st.columns(3)
364
+ with col1: st.metric("Rows", df.shape[0])
365
+ with col2: st.metric("Columns", df.shape[1])
366
+ with col3: st.metric("Missing Values", df.isna().sum().sum())
367
+ else: # Image
368
+ uploaded_file = st.file_uploader("Upload Zip File with Images", type=["zip"])
369
+ if uploaded_file:
370
+ problem_type = st.selectbox("Problem Type for Image Data", ["Image Classification", "Compression", "Clustering"])
371
+ images, labels, class_names = load_image_dataset(uploaded_file, problem_type=problem_type)
372
+ st.session_state.images = images
373
+ st.session_state.labels = labels
374
+ st.session_state.class_names = class_names if problem_type == "Image Classification" else None
375
+ st.write(f"Loaded {len(images)} images.")
376
+ if problem_type == "Image Classification":
377
+ st.write(f"Classes: {class_names}")
378
+ st.image(images[:5], caption=["Sample " + str(i+1) for i in range(min(5, len(images)))], width=100)
379
 
380
  elif app_mode == "Model Training":
381
  st.title("🧠 Model Training")
382
+ if data_type == "Tabular" and 'df' not in st.session_state:
383
+ st.warning("Please upload a tabular dataset first.")
384
+ st.stop()
385
+ elif data_type == "Image" and 'images' not in st.session_state:
386
+ st.warning("Please upload an image dataset first.")
387
  st.stop()
388
 
389
+ if data_type == "Tabular":
390
+ df = st.session_state.df
391
+ problem_type = st.selectbox("Problem Type", ["Regression", "Binary Classification", "Multi-Class", "Clustering", "Compression"])
392
+ features = st.multiselect("Select Features", df.columns)
393
+ target = st.selectbox("Select Target", df.columns) if problem_type not in ["Clustering", "Compression"] else None
394
+ else:
395
+ problem_type = st.selectbox("Problem Type", ["Image Classification", "Compression", "Clustering"])
396
+ features = ["images"]
397
+ target = "labels" if problem_type == "Image Classification" else None
398
 
399
+ if problem_type not in ["Clustering", "Compression"] and data_type == "Tabular" and target:
400
  unique_target_values = df[target].nunique()
401
  if problem_type == "Binary Classification" and unique_target_values != 2:
402
  st.error("Binary Classification requires exactly 2 unique target values.")
 
409
  st.stop()
410
 
411
  model_types = {
412
+ "Regression": ["Neural Network", "Random Forest", "XGBoost", "Linear Regression", "SVM"],
413
+ "Binary Classification": ["Neural Network", "Random Forest", "XGBoost", "Logistic Regression", "SVM"],
414
+ "Multi-Class": ["Neural Network", "Random Forest", "XGBoost", "SVM"],
415
+ "Clustering": ["K-Means", "DBSCAN", "Gaussian Mixture"],
416
+ "Compression": ["Autoencoder"],
417
+ "Image Classification": ["Neural Network", "SVM"]
418
  }[problem_type]
419
  model_type = st.selectbox("Model Type", model_types)
420
 
421
  if model_type == "Neural Network":
422
  st.subheader("Neural Network Configuration")
423
+ optimizer_name = st.selectbox("Optimizer", ["Adam", "SGD", "RMSprop"])
424
  layers_config = st.session_state.get('layers_config', [])
425
+ layer_type = st.selectbox("Layer Type", ["Dense", "Dropout"] if data_type == "Tabular" else ["Conv2D", "MaxPooling2D", "Flatten", "Dense", "Dropout"])
426
  if layer_type == "Dense":
427
  units = st.number_input("Units", min_value=1, value=64)
428
  activation = st.selectbox("Activation", ["relu", "sigmoid", "tanh"])
 
432
  rate = st.number_input("Dropout Rate", 0.0, 1.0, 0.2)
433
  if st.button("Add Layer"):
434
  layers_config.append({"type": "dropout", "rate": rate})
435
+ elif layer_type == "Conv2D":
436
+ filters = st.number_input("Filters", min_value=1, value=32)
437
+ kernel_size = st.multiselect("Kernel Size", options=[1, 3, 5], default=[3])
438
+ activation = st.selectbox("Activation", ["relu", "sigmoid", "tanh"])
439
+ if st.button("Add Layer"):
440
+ layers_config.append({"type": "conv2d", "filters": filters, "kernel_size": kernel_size, "activation": activation})
441
+ elif layer_type == "MaxPooling2D":
442
+ pool_size = st.multiselect("Pool Size", options=[2, 3], default=[2])
443
+ if st.button("Add Layer"):
444
+ layers_config.append({"type": "maxpooling2d", "pool_size": pool_size})
445
+ elif layer_type == "Flatten":
446
+ if st.button("Add Layer"):
447
+ layers_config.append({"type": "flatten"})
448
  if layers_config:
449
  st.write("Current Layers:", layers_config)
450
  if st.button("Clear Layers"):
451
  layers_config.clear()
452
  st.session_state.layers_config = layers_config
453
  st.rerun()
454
+ st.session_state.layers_config = layers_config
455
+ elif model_type == "Autoencoder":
456
+ st.subheader("Autoencoder Configuration")
457
+ encoding_dim = st.number_input("Encoding Dimension", min_value=1, value=32)
458
+ optimizer_name = st.selectbox("Optimizer", ["Adam", "SGD", "RMSprop"])
459
+ autoencoder_type = st.selectbox("Autoencoder Type", ["Standard", "Variational", "Denoising"])
460
+ if autoencoder_type == "Denoising":
461
+ noise_level = st.slider("Noise Level", 0.0, 1.0, 0.1)
462
+ layers_config = st.session_state.get('layers_config', [])
463
+ layer_type = st.selectbox("Layer Type (Encoder)", ["Dense", "Dropout"])
464
+ if layer_type == "Dense":
465
+ units = st.number_input("Units", min_value=1, value=64)
466
+ activation = st.selectbox("Activation", ["relu", "sigmoid", "tanh"])
467
+ if st.button("Add Layer"):
468
+ layers_config.append({"type": "dense", "units": units, "activation": activation})
469
+ elif layer_type == "Dropout":
470
+ rate = st.number_input("Dropout Rate", 0.0, 1.0, 0.2)
471
+ if st.button("Add Layer"):
472
+ layers_config.append({"type": "dropout", "rate": rate})
473
+ if layers_config:
474
+ st.write("Current Encoder Layers:", layers_config)
475
+ if st.button("Clear Layers"):
476
+ layers_config.clear()
477
+ st.session_state.layers_config = layers_config
478
+ st.rerun()
479
  st.session_state.layers_config = layers_config
480
  else:
481
  st.subheader("Model Hyperparameters")
 
483
  params = {}
484
  for param_name, param_values in config["grid_params"].items():
485
  if isinstance(param_values[0], (int, float)) and len(param_values) > 2:
486
+ slider_value = st.slider(param_name, min_value=float(min(param_values)), max_value=float(max(param_values)), value=float(param_values[1]))
487
+ if param_name in {'n_estimators', 'n_clusters', 'min_samples', 'n_components', 'max_depth'}:
 
 
 
 
 
 
 
488
  params[param_name] = int(slider_value)
489
  else:
490
  params[param_name] = slider_value
 
493
  do_grid_search = st.checkbox("Use Grid Search for Tuning", value=False)
494
 
495
  col1, col2, col3 = st.columns(3)
496
+ with col1: epochs = st.number_input("Epochs", min_value=1, value=10) if model_type in ["Neural Network", "Autoencoder"] else 10
497
+ with col2: batch_size = st.number_input("Batch Size", min_value=1, value=32) if model_type in ["Neural Network", "Autoencoder"] else 32
498
+ with col3: learning_rate = st.number_input("Learning Rate", min_value=0.0, value=0.001, step=0.0001) if model_type in ["Neural Network", "Autoencoder"] else 0.001
499
 
500
+ uploaded_model = st.file_uploader("Upload Pre-trained Model (.h5)", type=["h5"]) if model_type in ["Neural Network", "Autoencoder"] else None
501
  base_model = keras.models.load_model(uploaded_model) if uploaded_model else None
502
 
503
  if st.button("Train Model"):
504
  with st.spinner("Preparing data..."):
505
+ if data_type == "Tabular":
506
+ X = df[features]
507
+ y = df[target] if problem_type not in ["Clustering", "Compression"] else None
508
+ X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) if problem_type not in ["Clustering", "Compression"] else (X, X.copy(), None, None)
509
+ numerical_features = X.select_dtypes(include=np.number).columns.tolist()
510
+ categorical_features = X.select_dtypes(exclude=np.number).columns.tolist()
511
+ X_train_processed, X_test_processed, feature_names, preprocessor = preprocess_data(X_train, X_test, numerical_features, categorical_features)
512
+ le = None
513
+ if problem_type in ["Binary Classification", "Multi-Class"] and y is not None:
514
+ le = LabelEncoder()
515
+ y_train = le.fit_transform(y_train)
516
+ y_test = le.transform(y_test)
517
+ if problem_type == "Multi-Class":
518
+ y_train = tf.keras.utils.to_categorical(y_train)
519
+ y_test = tf.keras.utils.to_categorical(y_test)
520
+ else: # Image
521
+ X_train, X_test, y_train, y_test = train_test_split(st.session_state.images, st.session_state.labels, test_size=0.2, random_state=42) if problem_type == "Image Classification" else (st.session_state.images, st.session_state.images.copy(), None, None)
522
+ preprocessor = None
523
+ feature_names = ["image_features"]
524
+ le = None
525
 
526
  with st.spinner("Training model..."):
527
+ training_placeholder = st.empty()
528
  if model_type == "Neural Network":
529
  if not layers_config and not base_model:
530
  st.error("Please add layers or upload a pre-trained model.")
531
  st.stop()
532
+ input_shape = (X_train_processed.shape[1],) if data_type == "Tabular" else (224, 224, 3)
533
+ output_units = len(st.session_state.class_names) if problem_type == "Image Classification" else (y_train.shape[1] if problem_type == "Multi-Class" else 1)
534
+ model = base_model if base_model else build_neural_network(input_shape, output_units, problem_type, layers_config, optimizer_name, learning_rate)
535
+ history, model, training_time = train_model(model, X_train_processed if data_type == "Tabular" else X_train, y_train,
536
+ X_test_processed if data_type == "Tabular" else X_test, y_test,
537
+ epochs, batch_size, problem_type, training_placeholder=training_placeholder)
538
+ elif model_type == "Autoencoder":
539
+ if not layers_config and not base_model:
540
+ st.error("Please add layers to the encoder or upload a pre-trained model.")
541
+ st.stop()
542
+ input_shape = (X_train_processed.shape[1],) if data_type == "Tabular" else (224, 224, 3)
543
+ model, encoder, decoder = (base_model, None, None) if base_model else build_autoencoder(input_shape, encoding_dim, layers_config, autoencoder_type, optimizer_name, learning_rate)
544
+ if autoencoder_type == "Denoising":
545
+ X_train_noisy = X_train_processed + noise_level * np.random.normal(size=X_train_processed.shape) if data_type == "Tabular" else X_train + noise_level * np.random.normal(size=X_train.shape)
546
+ input_data = X_train_noisy
547
+ target_data = X_train_processed if data_type == "Tabular" else X_train
548
+ else:
549
+ input_data = X_train_processed if data_type == "Tabular" else X_train
550
+ target_data = X_train_processed if data_type == "Tabular" else X_train
551
+ history, model, training_time = train_model(model, X_train_processed if data_type == "Tabular" else X_train, None,
552
+ X_test_processed if data_type == "Tabular" else X_test, None,
553
+ epochs, batch_size, problem_type, input_data=input_data, target_data=target_data,
554
+ training_placeholder=training_placeholder)
555
+ st.session_state.encoder = encoder
556
+ st.session_state.decoder = decoder
557
  else:
558
  config = get_model_config(model_type, problem_type)
559
  model = config['model_class'](**config['params'])
560
+ X_train_flat = X_train_processed if data_type == "Tabular" else X_train.reshape(X_train.shape[0], -1)
561
+ X_test_flat = X_test_processed if data_type == "Tabular" else X_test.reshape(X_test.shape[0], -1)
562
+ history, model, training_time = train_model(model, X_train_flat, y_train, X_test_flat, y_test, epochs, batch_size, problem_type,
563
+ do_grid_search=do_grid_search, params=params, grid_params=config['grid_params'],
564
+ training_placeholder=training_placeholder)
 
 
 
565
 
566
  st.session_state.model = model
567
  st.session_state.preprocessor = preprocessor
 
577
 
578
  elif app_mode == "Validation & Exploration":
579
  st.title("🔍 Validation & Exploration")
580
+ if data_type == "Tabular" and ('model' not in st.session_state or 'df' not in st.session_state):
581
+ st.warning("Please upload a tabular dataset and train a model first.")
582
+ st.stop()
583
+ elif data_type == "Image" and ('model' not in st.session_state or 'images' not in st.session_state):
584
+ st.warning("Please upload an image dataset and train a model first.")
585
  st.stop()
586
 
587
+ if data_type == "Tabular":
588
+ df = st.session_state.df
589
+ X = df[st.session_state.features]
590
+ y = df[st.session_state.target] if st.session_state.problem_type not in ["Clustering", "Compression"] else None
591
+ X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) if st.session_state.problem_type not in ["Clustering", "Compression"] else (X, X.copy(), None, None)
592
+ numerical_features = X.select_dtypes(include=np.number).columns.tolist()
593
+ categorical_features = X.select_dtypes(exclude=np.number).columns.tolist()
594
+ X_train_processed, X_test_processed, feature_names, _ = preprocess_data(X_train, X_test, numerical_features, categorical_features)
595
+ if st.session_state.problem_type in ["Binary Classification", "Multi-Class"] and y is not None:
596
+ y_train = st.session_state.le.transform(y_train) if st.session_state.le else y_train
597
+ y_test = st.session_state.le.transform(y_test) if st.session_state.le else y_test
598
+ if st.session_state.problem_type == "Multi-Class":
599
+ y_train = tf.keras.utils.to_categorical(y_train)
600
+ y_test = tf.keras.utils.to_categorical(y_test)
601
+ else:
602
+ X_train, X_test, y_train, y_test = train_test_split(st.session_state.images, st.session_state.labels, test_size=0.2, random_state=42) if st.session_state.problem_type == "Image Classification" else (st.session_state.images, st.session_state.images.copy(), None, None)
603
+ X_train_processed, X_test_processed = X_train, X_test
604
+ feature_names = ["image_features"]
605
+ if st.session_state.problem_type == "Image Classification":
606
+ le = None
607
+
608
  model = st.session_state.model
 
 
 
609
  problem_type = st.session_state.problem_type
610
+ encoder = st.session_state.get('encoder', None)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
611
 
612
  # Validation
613
  st.subheader("Model Validation")
614
+ if problem_type == "Compression":
615
+ metrics, y_pred, compressed_data = evaluate_model(model, X_test_processed, None, problem_type, None, encoder)
616
+ else:
617
+ metrics, y_pred = evaluate_model(model, X_test_processed, y_test, problem_type, st.session_state.le if data_type == "Tabular" else None)
618
+
619
  col1, col2 = st.columns(2)
620
  with col1:
621
  for metric, value in metrics.items():
622
  st.metric(metric, f"{value:.4f}" if isinstance(value, float) else value)
 
623
  with col2:
624
  if problem_type == "Regression":
625
  fig = px.scatter(x=y_test, y=y_pred, labels={"x": "Actual", "y": "Predicted"}, title="Predicted vs Actual")
626
  st.plotly_chart(fig)
627
+ elif problem_type in ["Binary Classification", "Image Classification"]:
628
+ if problem_type == "Binary Classification":
629
+ y_pred_proba = model.predict_proba(X_test_processed)[:, 1] if hasattr(model, 'predict_proba') else y_pred
630
+ fpr, tpr, _ = roc_curve(y_test, y_pred_proba)
631
+ roc_auc = auc(fpr, tpr)
632
+ fig = px.area(x=fpr, y=tpr, title=f"ROC Curve (AUC = {roc_auc:.2f})", labels={"x": "False Positive Rate", "y": "True Positive Rate"})
633
+ st.plotly_chart(fig)
634
+ else:
635
+ cm = np.zeros((len(st.session_state.class_names), len(st.session_state.class_names)))
636
+ for i, j in zip(y_test, y_pred):
637
+ cm[i, j] += 1
638
+ fig = px.imshow(cm, title="Confusion Matrix", labels={"x": "Predicted", "y": "Actual"})
639
+ st.plotly_chart(fig)
640
+ report = classification_report(y_test, y_pred, target_names=st.session_state.class_names, zero_division=0)
641
+ st.text("Classification Report:\n" + report)
642
  elif problem_type == "Multi-Class":
643
+ y_pred_classes = np.argmax(model.predict(X_test_processed), axis=1)
644
  y_test_classes = np.argmax(y_test, axis=1)
645
  cm = np.zeros((y_train.shape[1], y_train.shape[1]))
646
  for i, j in zip(y_test_classes, y_pred_classes):
647
  cm[i, j] += 1
648
  fig = px.imshow(cm, title="Confusion Matrix", labels={"x": "Predicted", "y": "Actual"})
649
  st.plotly_chart(fig)
650
+ report = classification_report(y_test_classes, y_pred_classes, target_names=st.session_state.le.classes_ if st.session_state.le else [str(i) for i in range(y_train.shape[1])], zero_division=0)
 
 
651
  st.text("Classification Report:\n" + report)
652
  elif problem_type == "Clustering":
653
  labels = y_pred
654
+ fig = px.scatter(x=X_test_processed[:, 0] if X_test_processed.shape[-1] == 1 else X_test_processed.reshape(X_test_processed.shape[0], -1)[:, 0],
655
+ y=X_test_processed[:, 1] if X_test_processed.shape[-1] == 1 else X_test_processed.reshape(X_test_processed.shape[0], -1)[:, 1],
656
+ color=labels, title="Cluster Visualization")
657
+ st.plotly_chart(fig)
658
+ elif problem_type == "Compression":
659
+ st.image([X_test_processed[0], y_pred[0]], caption=["Original", "Reconstructed"], width=200) if data_type == "Image" else None
660
+ fig = px.scatter(x=X_test_processed.flatten()[:1000], y=y_pred.flatten()[:1000], labels={"x": "Original", "y": "Reconstructed"}, title="Original vs Reconstructed (First 1000 Values)")
661
  st.plotly_chart(fig)
662
 
663
  # Dimensionality Reduction
664
  st.subheader("Dimensionality Reduction")
665
+ methods = ["PCA", "SVD", "t-SNE", "UMAP"]
666
+ if problem_type == "Compression" and 'encoder' in st.session_state:
667
+ methods.append("Autoencoder")
668
+ method = st.selectbox("Method", methods)
669
+ n_components = st.slider("Components", 2, min(10 if data_type == "Tabular" else X_train_processed.shape[1], 10), 2)
670
+
671
+ if method == "Autoencoder" and 'encoder' in st.session_state:
672
+ X_reduced = st.session_state.encoder.predict(X_train_processed)
673
+ if X_reduced.shape[1] < n_components:
674
+ st.warning(f"Autoencoder encoding dimension is {X_reduced.shape[1]}, using that instead of {n_components}.")
675
+ n_components = X_reduced.shape[1]
676
+ else:
677
+ X_flat = X_train_processed if data_type == "Tabular" else X_train_processed.reshape(X_train_processed.shape[0], -1)
678
+ if method == "PCA":
679
+ reducer = PCA(n_components=n_components)
680
+ X_reduced = reducer.fit_transform(X_flat)
681
+ fig = px.bar(x=range(n_components), y=reducer.explained_variance_ratio_, title="Explained Variance Ratio")
682
+ st.plotly_chart(fig)
683
+ elif method == "SVD":
684
+ reducer = TruncatedSVD(n_components=n_components)
685
+ X_reduced = reducer.fit_transform(X_flat)
686
+ fig = px.bar(x=range(n_components), y=reducer.explained_variance_ratio_, title="Explained Variance Ratio")
687
+ st.plotly_chart(fig)
688
+ elif method == "t-SNE":
689
+ with st.spinner("Running t-SNE..."):
690
+ X_reduced = TSNE(n_components=n_components, random_state=42).fit_transform(X_flat)
691
+ elif method == "UMAP":
692
+ with st.spinner("Running UMAP..."):
693
+ X_reduced = umap.UMAP(n_components=n_components, random_state=42).fit_transform(X_flat)
694
 
695
  if n_components >= 2:
696
+ if n_components == 2:
697
+ fig = px.scatter(x=X_reduced[:, 0], y=X_reduced[:, 1], color=y_train if problem_type not in ["Clustering", "Compression"] else y_pred,
698
+ title=f"{method} Visualization")
699
+ elif n_components == 3:
700
+ fig = px.scatter_3d(x=X_reduced[:, 0], y=X_reduced[:, 1], z=X_reduced[:, 2], color=y_train if problem_type not in ["Clustering", "Compression"] else y_pred,
701
+ title=f"{method} Visualization")
702
  st.plotly_chart(fig)
703
 
704
  # Interpretability
705
+ if problem_type not in ["Compression", "Clustering"]:
706
+ st.subheader("Interpretability")
707
+ try:
708
+ X_flat = X_test_processed if data_type == "Tabular" else X_test_processed.reshape(X_test_processed.shape[0], -1)
709
+ if isinstance(model, keras.Model):
710
+ explainer = shap.DeepExplainer(model, X_train_processed[:50] if data_type == "Tabular" else X_train_processed[:50])
711
+ shap_values = explainer.shap_values(X_flat[:50])
712
+ else:
713
+ explainer = shap.Explainer(model, X_flat)
714
+ shap_values = explainer.shap_values(X_flat[:50])
715
+ if problem_type == "Regression":
 
716
  shap_fig, ax = plt.subplots()
717
+ shap.summary_plot(shap_values, X_flat[:50], feature_names=feature_names, show=False)
718
  st.pyplot(shap_fig)
719
+ elif problem_type in ["Binary Classification", "Multi-Class", "Image Classification"]:
720
+ class_names = st.session_state.le.classes_ if st.session_state.le and data_type == "Tabular" else st.session_state.class_names if problem_type == "Image Classification" else [str(i) for i in range(y_train.shape[1])]
721
+ for i in range(min(len(class_names), len(shap_values))):
722
+ shap_fig, ax = plt.subplots()
723
+ shap.summary_plot(shap_values[i] if isinstance(shap_values, list) else shap_values, X_flat[:50], feature_names=feature_names, class_names=class_names, show=False)
724
+ st.pyplot(shap_fig)
725
+ except Exception as e:
726
+ st.error(f"Error generating SHAP plot: {e}")
727
 
728
  # Custom CSS
729
  st.markdown("""