Harika22's picture
Update app.py
5c2140f verified
import streamlit as st
import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.datasets import make_moons, make_circles, make_classification
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score
import numpy as np
import matplotlib.pyplot as plt
import io
import time
st.set_page_config(page_title="ANN Visualizer", layout="wide")
st.title("🧠 Interactive ANN Visualizer")
with st.sidebar:
st.header("βš™οΈ Configure Your Model")
dataset_type = st.selectbox("Dataset", ["moons", "circles", "classification"])
n_samples = st.slider("Data Points", 100, 20000, 500, step=100)
noise = st.slider("Noise Level", 0.0, 1.0, 0.2, step=0.05)
num_hidden = st.number_input("Hidden Layers", 1, 10, 2)
hidden_layers, activations, dropout_rates = [], [], []
activation_map = {"ReLU": nn.ReLU(), "Tanh": nn.Tanh(), "Sigmoid": nn.Sigmoid()}
for i in range(num_hidden):
st.markdown(f"### Hidden Layer {i+1}")
units = st.number_input(f"Units (Layer {i+1})", 1, 512, 8, key=f"units_{i}")
act = st.selectbox(f"Activation (Layer {i+1})", list(activation_map.keys()), key=f"act_{i}")
drop = st.slider(f"Dropout (Layer {i+1})", 0.0, 0.9, 0.0, 0.05, key=f"dropout_{i}")
hidden_layers.append(units)
activations.append(act)
dropout_rates.append(drop)
lr = st.number_input("Learning Rate", 0.0001, 1.0, 0.01, format="%f")
epochs = st.slider("Epochs", 100, 5000, 500, step=100)
weight_decay = st.number_input("L2 Regularization (Weight Decay)", 0.0, 0.1, 0.0001, format="%f")
early_stop = st.checkbox("Enable Early Stopping", True)
patience = st.slider("Patience", 1, 20, 5) if early_stop else None
min_delta = st.number_input("Min Improvement Delta", 0.0001, 0.1, 0.001, format="%f") if early_stop else None
start = st.button("πŸš€ Start Training")
if start:
# Data Generator
def generate_data():
if dataset_type == "moons":
return make_moons(n_samples=n_samples, noise=noise, random_state=42)
elif dataset_type == "circles":
return make_circles(n_samples=n_samples, noise=noise, factor=0.5, random_state=42)
else:
return make_classification(n_samples=n_samples, n_features=2, n_informative=2,
n_redundant=0, n_clusters_per_class=1, random_state=42)
X, y = generate_data()
scaler = StandardScaler()
X = scaler.fit_transform(X)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
X_train_tensor = torch.tensor(X_train, dtype=torch.float32)
y_train_tensor = torch.tensor(y_train, dtype=torch.long)
X_test_tensor = torch.tensor(X_test, dtype=torch.float32)
y_test_tensor = torch.tensor(y_test, dtype=torch.long)
# Build Model Dynamically
layers = []
input_dim = X.shape[1]
for h, act, dr in zip(hidden_layers, activations, dropout_rates):
layers.append(nn.Linear(input_dim, h))
layers.append(activation_map[act])
if dr > 0:
layers.append(nn.Dropout(dr))
input_dim = h
layers.append(nn.Linear(input_dim, 2))
model = nn.Sequential(*layers)
# Loss, Optimizer
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=lr, weight_decay=weight_decay)
# Plotting Setup
st.subheader("🌍 Initial Decision Boundary")
x_min, x_max = X[:,0].min()-0.5, X[:,0].max()+0.5
y_min, y_max = X[:,1].min()-0.5, X[:,1].max()+0.5
xx, yy = np.meshgrid(np.linspace(x_min, x_max, 400), np.linspace(y_min, y_max, 400))
grid = np.c_[xx.ravel(), yy.ravel()]
grid_tensor = torch.tensor(grid, dtype=torch.float32)
model.eval()
with torch.no_grad():
probs = torch.softmax(model(grid_tensor), dim=1)[:,1].numpy().reshape(xx.shape)
fig, ax = plt.subplots()
ax.contourf(xx, yy, probs, levels=50, cmap="Spectral", alpha=0.8)
ax.scatter(X[:,0], X[:,1], c=y, edgecolor='k', s=15, cmap="Spectral")
ax.set_title("Initial Decision Surface")
ax.set_xticks([]); ax.set_yticks([])
st.pyplot(fig)
# Training Loop
st.subheader("⏳ Training Progress")
progress = st.progress(0)
best_loss = float('inf')
patience_counter = 0
train_losses, test_losses = [], []
plot_interval = max(1, epochs // 10)
start_time = time.time()
for epoch in range(1, epochs+1):
model.train()
optimizer.zero_grad()
output = model(X_train_tensor)
loss = criterion(output, y_train_tensor)
loss.backward()
optimizer.step()
train_losses.append(loss.item())
model.eval()
with torch.no_grad():
val_output = model(X_test_tensor)
val_loss = criterion(val_output, y_test_tensor)
test_losses.append(val_loss.item())
# Early Stopping
if early_stop:
if val_loss.item() < best_loss - min_delta:
best_loss = val_loss.item()
patience_counter = 0
else:
patience_counter += 1
if patience_counter >= patience:
st.warning(f"Early stopping at epoch {epoch}")
break
# Plot decision surface every few epochs
if epoch % plot_interval == 0 or epoch == epochs:
st.markdown(f"### Epoch {epoch}")
with torch.no_grad():
probs = torch.softmax(model(grid_tensor), dim=1)[:,1].numpy().reshape(xx.shape)
fig, ax = plt.subplots()
ax.contourf(xx, yy, probs, levels=50, cmap="Spectral", alpha=0.8)
ax.scatter(X[:,0], X[:,1], c=y, edgecolor='k', s=15, cmap="Spectral")
ax.set_title(f"Decision Surface at Epoch {epoch}")
ax.set_xticks([]); ax.set_yticks([])
st.pyplot(fig)
progress.progress(epoch / epochs)
duration = time.time() - start_time
st.success(f"Training finished in {duration:.2f} seconds")
# Plot Loss Curves
st.subheader("πŸ“‰ Final Loss Curves")
fig1, ax1 = plt.subplots()
ax1.plot(train_losses, label="Train Loss")
ax1.plot(test_losses, label="Test Loss")
ax1.set_xlabel("Epochs")
ax1.set_ylabel("Loss")
ax1.legend()
ax1.grid(True)
st.pyplot(fig1)
buf_loss = io.BytesIO()
fig1.savefig(buf_loss, format="png")
st.download_button("Download Loss Curve", buf_loss.getvalue(), file_name="loss_curve.png", mime="image/png")
# Final Decision Surface
st.subheader("🌟 Final Decision Boundary")
with torch.no_grad():
probs = torch.softmax(model(grid_tensor), dim=1)[:,1].numpy().reshape(xx.shape)
fig2, ax2 = plt.subplots()
ax2.contourf(xx, yy, probs, levels=50, cmap="Spectral", alpha=0.8)
ax2.scatter(X[:,0], X[:,1], c=y, edgecolor='k', s=15, cmap="Spectral")
ax2.set_title("Final Decision Surface")
ax2.set_xticks([]); ax2.set_yticks([])
st.pyplot(fig2)
buf_boundary = io.BytesIO()
fig2.savefig(buf_boundary, format="png")
st.download_button("Download Decision Surface", buf_boundary.getvalue(), file_name="decision_surface.png", mime="image/png")
# Accuracy
with torch.no_grad():
train_preds = model(X_train_tensor).argmax(dim=1).numpy()
test_preds = model(X_test_tensor).argmax(dim=1).numpy()
st.metric("Train Accuracy", f"{accuracy_score(y_train, train_preds)*100:.2f}%")
st.metric("Test Accuracy", f"{accuracy_score(y_test, test_preds)*100:.2f}%")
st.info("πŸ”„ Change settings from the sidebar to retrain with different configurations!")