Spaces:
Sleeping
Sleeping
File size: 5,177 Bytes
7eeb968 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | import gradio as gr
import torch
import spaces
import torch.nn as nn
import numpy as np
import os
import zipfile
import urllib.request
from tqdm import tqdm
# --- 1. MODEL ARCHITECTURE ---
class BiLSTMClassifier(nn.Module):
def __init__(self, input_dim=300, hidden_dim=256, num_classes=5):
super().__init__()
self.lstm = nn.LSTM(input_dim, hidden_dim, batch_first=True,
bidirectional=True, num_layers=2, dropout=0.3)
self.dropout = nn.Dropout(0.3)
self.fc = nn.Linear(hidden_dim * 2, num_classes)
def forward(self, x):
_, (h, _) = self.lstm(x)
h = torch.cat([h[-2], h[-1]], dim=-1)
h = self.dropout(h)
return self.fc(h)
# Helper class to display a download progress bar in the terminal
class DownloadProgressBar(tqdm):
def update_to(self, b=1, bsize=1, tsize=None):
if tsize is not None:
self.total = tsize
self.update(b * bsize - self.n)
def load_glove():
zip_path = "glove.6B.zip"
txt_path = "glove/glove.6B.300d.txt"
os.makedirs("glove", exist_ok=True)
# 1. DOWNLOAD WITH PROGRESS BAR
if not os.path.exists(txt_path):
if not os.path.exists(zip_path):
print("Downloading GloVe embeddings (~822 MB)...")
url = "https://huggingface.co/stanfordnlp/glove/resolve/main/glove.6B.zip"
with DownloadProgressBar(unit='B', unit_scale=True, miniters=1, desc="Downloading GloVe") as t:
urllib.request.urlretrieve(url, filename=zip_path, reporthook=t.update_to)
# 2. EXTRACT WITH FEEDBACK
print("Extracting GloVe zip file...")
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall("glove")
print("Extraction complete!")
# 3. LOAD TEXT FILE WITH LINE-BY-LINE PROGRESS BAR
embeddings = {}
print("Loading vectors into memory...")
with open(txt_path, encoding="utf-8") as f:
# GloVe 6B 300d has exactly 400,000 lines/words
for line in tqdm(f, total=400000, desc="Parsing GloVe Vectors"):
values = line.split()
embeddings[values[0]] = np.array(values[1:], dtype=np.float32)
print(f"Successfully loaded {len(embeddings):,} GloVe vectors!")
return embeddings
def text_to_seq(text, glove_dict, max_len=50, dim=300):
tokens = str(text).lower().split()[:max_len]
vecs = [glove_dict.get(t, np.zeros(dim, dtype=np.float32)) for t in tokens]
while len(vecs) < max_len:
vecs.append(np.zeros(dim, dtype=np.float32))
return np.array(vecs, dtype=np.float32)
device = torch.device("cpu")
model = BiLSTMClassifier()
if os.path.exists("bilstm_weights.pth"):
model.load_state_dict(torch.load("bilstm_weights.pth", map_location=device))
model.to(device)
model.eval()
glove = load_glove()
OPTIONS = ["A", "B", "C", "D", "E"]
# --- 3. PREDICTION FUNCTION ---
@spaces.GPU
def predict_mcq(prompt, opt_a, opt_b, opt_c, opt_d, opt_e):
if not os.path.exists("bilstm_weights.pth"):
return "Error: Model weights not found.", {}
input_options = [opt_a, opt_b, opt_c, opt_d, opt_e]
if not prompt or not all(input_options):
return "Please fill in the prompt and all 5 options.", {}
seqs = []
for opt in input_options:
combined = str(prompt) + " " + str(opt)
seqs.append(text_to_seq(combined, glove))
seqs_tensor = torch.tensor(np.array(seqs, dtype=np.float32))
with torch.no_grad():
scores = []
for i in range(5):
s = model(seqs_tensor[i].unsqueeze(0))[:, i].item()
scores.append(s)
# Format outputs
top_3_idx = np.argsort(scores)[::-1][:3]
top_3_preds = " ".join([OPTIONS[i] for i in top_3_idx])
# Create a dictionary of scores for the Gradio Label component
confidence_dict = {OPTIONS[i]: float(scores[i]) for i in range(5)}
return f"Top 3 Predictions: {top_3_preds}", confidence_dict
# --- 4. GRADIO UI LAYOUT ---
with gr.Blocks() as demo:
gr.Markdown("# 🧠 Smart MCQ Solver (BiLSTM)")
gr.Markdown("Enter a prompt and 5 options to get the top 3 predicted answers.")
with gr.Row():
with gr.Column(scale=2):
prompt_input = gr.Textbox(lines=3, label="Question / Prompt")
opt_a_input = gr.Textbox(label="Option A")
opt_b_input = gr.Textbox(label="Option B")
opt_c_input = gr.Textbox(label="Option C")
opt_d_input = gr.Textbox(label="Option D")
opt_e_input = gr.Textbox(label="Option E")
submit_btn = gr.Button("Predict Answer", variant="primary")
with gr.Column(scale=1):
text_output = gr.Textbox(label="Result", lines=2)
label_output = gr.Label(label="Raw Model Scores (Logits)")
submit_btn.click(
fn=predict_mcq,
inputs=[prompt_input, opt_a_input, opt_b_input, opt_c_input, opt_d_input, opt_e_input],
outputs=[text_output, label_output]
)
if __name__ == "__main__":
# Launch locally or on Spaces
demo.launch(theme=gr.themes.Soft(), ssr_mode=False) |