Update app.py
Browse files
app.py
CHANGED
|
@@ -12,13 +12,19 @@ import pandas as pd
|
|
| 12 |
import numpy as np
|
| 13 |
import time
|
| 14 |
|
| 15 |
-
|
| 16 |
# ==========================================
|
| 17 |
-
# 1. PAGE CONFIG & UI SETUP
|
| 18 |
# ==========================================
|
| 19 |
st.set_page_config(page_title="Neural VAE Architecture", layout="wide")
|
| 20 |
|
| 21 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
st.title("🧠 Variational Autoencoder (VAE): Inside the Black Box")
|
| 23 |
st.markdown("Watch the mathematics of a Neural Network transform text into topics in real-time.")
|
| 24 |
|
|
@@ -35,7 +41,6 @@ st.divider()
|
|
| 35 |
def load_bert():
|
| 36 |
return SentenceTransformer("all-MiniLM-L6-v2")
|
| 37 |
|
| 38 |
-
|
| 39 |
# --- SIDEBAR CONTROLS ---
|
| 40 |
st.sidebar.header("1. Input & Text Prep")
|
| 41 |
n_topics = st.sidebar.slider("Number of Topics (Latent Dimensions)", 2, 10, 3)
|
|
@@ -69,7 +74,6 @@ class ContextualVAE(nn.Module):
|
|
| 69 |
super().__init__()
|
| 70 |
act_fn = nn.ReLU() if activation == "ReLU" else nn.Softplus() if activation == "Softplus" else nn.GELU()
|
| 71 |
|
| 72 |
-
# 1. ENCODER
|
| 73 |
self.encoder = nn.Sequential(
|
| 74 |
nn.Linear(input_dim, hidden_dim),
|
| 75 |
act_fn,
|
|
@@ -78,8 +82,6 @@ class ContextualVAE(nn.Module):
|
|
| 78 |
)
|
| 79 |
self.fc_mu = nn.Linear(hidden_dim, latent_dim)
|
| 80 |
self.fc_logvar = nn.Linear(hidden_dim, latent_dim)
|
| 81 |
-
|
| 82 |
-
# 2. DECODER
|
| 83 |
self.decoder = nn.Linear(latent_dim, vocab_size, bias=False)
|
| 84 |
|
| 85 |
def encode(self, x):
|
|
@@ -150,7 +152,6 @@ if st.sidebar.button("🚀 Start Backpropagation", type="primary", use_container
|
|
| 150 |
|
| 151 |
sentences = [s.strip() for s in raw_data.split('.') if len(s.strip()) > 10]
|
| 152 |
|
| 153 |
-
# Data Prep
|
| 154 |
stop_w = list(CountVectorizer(stop_words='english').get_stop_words()) + ['students', 'course']
|
| 155 |
vectorizer = CountVectorizer(stop_words=stop_w)
|
| 156 |
bow_matrix = vectorizer.fit_transform(sentences).toarray()
|
|
@@ -167,11 +168,7 @@ if st.sidebar.button("🚀 Start Backpropagation", type="primary", use_container
|
|
| 167 |
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
|
| 168 |
|
| 169 |
recon_hist, kl_hist = [], []
|
| 170 |
-
|
| 171 |
-
# Trackers for the expanding history tables
|
| 172 |
-
history_encoder = []
|
| 173 |
-
history_latent = []
|
| 174 |
-
history_decoder = []
|
| 175 |
|
| 176 |
# --- PYTORCH TRAINING LOOP ---
|
| 177 |
for epoch in range(1, epochs + 1):
|
|
@@ -189,20 +186,15 @@ if st.sidebar.button("🚀 Start Backpropagation", type="primary", use_container
|
|
| 189 |
total_loss.backward()
|
| 190 |
optimizer.step()
|
| 191 |
|
| 192 |
-
# ==========================================
|
| 193 |
-
# 1. RECORD DATA EVERY SINGLE EPOCH
|
| 194 |
-
# ==========================================
|
| 195 |
# Grab math for Sentence 1
|
| 196 |
s1_mu = np.round(mu[0].detach().numpy(), 3).tolist()
|
| 197 |
s1_var = np.round(torch.exp(0.5 * logvar[0]).detach().numpy(), 3).tolist()
|
| 198 |
s1_z = np.round(z[0].detach().numpy(), 3).tolist()
|
| 199 |
s1_soft = np.round(F.softmax(z[0], dim=-1).detach().numpy(), 3).tolist()
|
| 200 |
|
| 201 |
-
# Save to Encoder & Latent History
|
| 202 |
history_encoder.append({"Epoch": epoch, "Mean (μ)": str(s1_mu), "Variance (σ)": str(s1_var)})
|
| 203 |
history_latent.append({"Epoch": epoch, "Sampled Vector (Z)": str(s1_z), "Topic Probs": str(s1_soft)})
|
| 204 |
|
| 205 |
-
# Dynamically create separate columns for EACH topic in the Decoder history!
|
| 206 |
weights = model.decoder.weight.T
|
| 207 |
dec_dict = {"Epoch": epoch}
|
| 208 |
dec_text = ""
|
|
@@ -210,10 +202,7 @@ if st.sidebar.button("🚀 Start Backpropagation", type="primary", use_container
|
|
| 210 |
for i in range(n_topics):
|
| 211 |
top_idx = torch.argsort(weights[i], descending=True)[:words_per_theme]
|
| 212 |
theme_words = [vocab[j] for j in top_idx]
|
| 213 |
-
|
| 214 |
-
# Format for the UI Box
|
| 215 |
dec_text += f"**T{i+1}:** {', '.join(theme_words)}\n\n"
|
| 216 |
-
# Format for the DataFrame (Separate column for Topic 1, Topic 2, etc.)
|
| 217 |
dec_dict[f"Topic {i+1}"] = ", ".join(theme_words)
|
| 218 |
|
| 219 |
history_decoder.append(dec_dict)
|
|
@@ -222,34 +211,28 @@ if st.sidebar.button("🚀 Start Backpropagation", type="primary", use_container
|
|
| 222 |
kl_hist.append((current_beta * kl_loss).item())
|
| 223 |
|
| 224 |
# ==========================================
|
| 225 |
-
# 2. SAFE UI RENDER (
|
| 226 |
# ==========================================
|
| 227 |
if epoch % 20 == 0 or epoch == epochs:
|
| 228 |
|
| 229 |
-
# Update Live Values
|
| 230 |
ui_mu.info(f"**Mean (μ) for Sentence 1:**\n`{s1_mu}`")
|
| 231 |
ui_var.warning(f"**Variance (σ) for Sentence 1:**\n`{s1_var}`")
|
| 232 |
ui_z.success(f"**Sampled 'Z' for Sentence 1:**\n`{s1_z}`")
|
| 233 |
ui_softmax.error(f"**Topic Probabilities:**\n`{s1_soft}`")
|
| 234 |
ui_decoder.info(dec_text)
|
| 235 |
|
| 236 |
-
#
|
| 237 |
-
ui_encoder_table.dataframe(pd.DataFrame(history_encoder), hide_index=True, use_container_width=True)
|
| 238 |
-
ui_latent_table.dataframe(pd.DataFrame(history_latent), hide_index=True, use_container_width=True)
|
| 239 |
-
ui_decoder_table.dataframe(pd.DataFrame(history_decoder), hide_index=True, use_container_width=True)
|
| 240 |
|
| 241 |
-
# Update Optimization Metrics
|
| 242 |
ui_beta.metric("Current Beta (Annealing)", f"{current_beta:.4f}")
|
| 243 |
ui_recon_loss.metric("Reconstruction Loss", f"{recon_loss.item():.4f}")
|
| 244 |
ui_kl_loss.metric("KL Penalty", f"{(current_beta * kl_loss).item():.4f}")
|
| 245 |
|
| 246 |
-
|
| 247 |
-
chart_df = pd.DataFrame({
|
| 248 |
-
"Recon Loss": recon_hist,
|
| 249 |
-
"KL Penalty": kl_hist
|
| 250 |
-
})
|
| 251 |
loss_chart_placeholder.line_chart(chart_df)
|
| 252 |
|
| 253 |
-
time.sleep(0.
|
| 254 |
|
| 255 |
st.success("Neural Network Training Complete!")
|
|
|
|
| 12 |
import numpy as np
|
| 13 |
import time
|
| 14 |
|
|
|
|
| 15 |
# ==========================================
|
| 16 |
+
# 1. PAGE CONFIG & UI SETUP (WITH ANTI-GLITCH CSS)
|
| 17 |
# ==========================================
|
| 18 |
st.set_page_config(page_title="Neural VAE Architecture", layout="wide")
|
| 19 |
|
| 20 |
+
# FIX: CSS injection to permanently lock the scrollbar and prevent horizontal jumping!
|
| 21 |
+
st.markdown("""
|
| 22 |
+
<style>
|
| 23 |
+
html { overflow-y: scroll; }
|
| 24 |
+
.block-container { overflow-x: hidden; }
|
| 25 |
+
</style>
|
| 26 |
+
""", unsafe_allow_html=True)
|
| 27 |
+
|
| 28 |
st.title("🧠 Variational Autoencoder (VAE): Inside the Black Box")
|
| 29 |
st.markdown("Watch the mathematics of a Neural Network transform text into topics in real-time.")
|
| 30 |
|
|
|
|
| 41 |
def load_bert():
|
| 42 |
return SentenceTransformer("all-MiniLM-L6-v2")
|
| 43 |
|
|
|
|
| 44 |
# --- SIDEBAR CONTROLS ---
|
| 45 |
st.sidebar.header("1. Input & Text Prep")
|
| 46 |
n_topics = st.sidebar.slider("Number of Topics (Latent Dimensions)", 2, 10, 3)
|
|
|
|
| 74 |
super().__init__()
|
| 75 |
act_fn = nn.ReLU() if activation == "ReLU" else nn.Softplus() if activation == "Softplus" else nn.GELU()
|
| 76 |
|
|
|
|
| 77 |
self.encoder = nn.Sequential(
|
| 78 |
nn.Linear(input_dim, hidden_dim),
|
| 79 |
act_fn,
|
|
|
|
| 82 |
)
|
| 83 |
self.fc_mu = nn.Linear(hidden_dim, latent_dim)
|
| 84 |
self.fc_logvar = nn.Linear(hidden_dim, latent_dim)
|
|
|
|
|
|
|
| 85 |
self.decoder = nn.Linear(latent_dim, vocab_size, bias=False)
|
| 86 |
|
| 87 |
def encode(self, x):
|
|
|
|
| 152 |
|
| 153 |
sentences = [s.strip() for s in raw_data.split('.') if len(s.strip()) > 10]
|
| 154 |
|
|
|
|
| 155 |
stop_w = list(CountVectorizer(stop_words='english').get_stop_words()) + ['students', 'course']
|
| 156 |
vectorizer = CountVectorizer(stop_words=stop_w)
|
| 157 |
bow_matrix = vectorizer.fit_transform(sentences).toarray()
|
|
|
|
| 168 |
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
|
| 169 |
|
| 170 |
recon_hist, kl_hist = [], []
|
| 171 |
+
history_encoder, history_latent, history_decoder = [], [], []
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
|
| 173 |
# --- PYTORCH TRAINING LOOP ---
|
| 174 |
for epoch in range(1, epochs + 1):
|
|
|
|
| 186 |
total_loss.backward()
|
| 187 |
optimizer.step()
|
| 188 |
|
|
|
|
|
|
|
|
|
|
| 189 |
# Grab math for Sentence 1
|
| 190 |
s1_mu = np.round(mu[0].detach().numpy(), 3).tolist()
|
| 191 |
s1_var = np.round(torch.exp(0.5 * logvar[0]).detach().numpy(), 3).tolist()
|
| 192 |
s1_z = np.round(z[0].detach().numpy(), 3).tolist()
|
| 193 |
s1_soft = np.round(F.softmax(z[0], dim=-1).detach().numpy(), 3).tolist()
|
| 194 |
|
|
|
|
| 195 |
history_encoder.append({"Epoch": epoch, "Mean (μ)": str(s1_mu), "Variance (σ)": str(s1_var)})
|
| 196 |
history_latent.append({"Epoch": epoch, "Sampled Vector (Z)": str(s1_z), "Topic Probs": str(s1_soft)})
|
| 197 |
|
|
|
|
| 198 |
weights = model.decoder.weight.T
|
| 199 |
dec_dict = {"Epoch": epoch}
|
| 200 |
dec_text = ""
|
|
|
|
| 202 |
for i in range(n_topics):
|
| 203 |
top_idx = torch.argsort(weights[i], descending=True)[:words_per_theme]
|
| 204 |
theme_words = [vocab[j] for j in top_idx]
|
|
|
|
|
|
|
| 205 |
dec_text += f"**T{i+1}:** {', '.join(theme_words)}\n\n"
|
|
|
|
| 206 |
dec_dict[f"Topic {i+1}"] = ", ".join(theme_words)
|
| 207 |
|
| 208 |
history_decoder.append(dec_dict)
|
|
|
|
| 211 |
kl_hist.append((current_beta * kl_loss).item())
|
| 212 |
|
| 213 |
# ==========================================
|
| 214 |
+
# 2. SAFE UI RENDER (Every 20 epochs)
|
| 215 |
# ==========================================
|
| 216 |
if epoch % 20 == 0 or epoch == epochs:
|
| 217 |
|
|
|
|
| 218 |
ui_mu.info(f"**Mean (μ) for Sentence 1:**\n`{s1_mu}`")
|
| 219 |
ui_var.warning(f"**Variance (σ) for Sentence 1:**\n`{s1_var}`")
|
| 220 |
ui_z.success(f"**Sampled 'Z' for Sentence 1:**\n`{s1_z}`")
|
| 221 |
ui_softmax.error(f"**Topic Probabilities:**\n`{s1_soft}`")
|
| 222 |
ui_decoder.info(dec_text)
|
| 223 |
|
| 224 |
+
# FIX: Added `height=200` to prevent the dataframe from stretching the webpage!
|
| 225 |
+
ui_encoder_table.dataframe(pd.DataFrame(history_encoder), hide_index=True, use_container_width=True, height=200)
|
| 226 |
+
ui_latent_table.dataframe(pd.DataFrame(history_latent), hide_index=True, use_container_width=True, height=200)
|
| 227 |
+
ui_decoder_table.dataframe(pd.DataFrame(history_decoder), hide_index=True, use_container_width=True, height=200)
|
| 228 |
|
|
|
|
| 229 |
ui_beta.metric("Current Beta (Annealing)", f"{current_beta:.4f}")
|
| 230 |
ui_recon_loss.metric("Reconstruction Loss", f"{recon_loss.item():.4f}")
|
| 231 |
ui_kl_loss.metric("KL Penalty", f"{(current_beta * kl_loss).item():.4f}")
|
| 232 |
|
| 233 |
+
chart_df = pd.DataFrame({"Recon Loss": recon_hist, "KL Penalty": kl_hist})
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
loss_chart_placeholder.line_chart(chart_df)
|
| 235 |
|
| 236 |
+
time.sleep(0.01) # Slightly faster sleep to prevent HF WebSocket timeouts
|
| 237 |
|
| 238 |
st.success("Neural Network Training Complete!")
|