MahaTrabelsi's picture
Upload app.py
3f20604 verified
import gradio as gr
import joblib
import numpy as np
import re
# ----------------------------
# Load the trained model
# ----------------------------
model = joblib.load("spam_model.joblib")
# ----------------------------
# Text normalization (optional but helps)
# ----------------------------
_url = re.compile(r'https?://\S+|www\.\S+')
_email = re.compile(r'\b[\w\.-]+@[\w\.-]+\.\w+\b')
_num = re.compile(r'\b\d[\d,.\-:/]*\b')
def normalize_text(t: str) -> str:
t = t.strip().lower()
t = _url.sub(' __url__ ', t)
t = _email.sub(' __email__ ', t)
t = _num.sub(' __number__ ', t)
return t
# ----------------------------
# Prediction function with confidence
# ----------------------------
def predict_with_confidence(text: str):
text_norm = normalize_text(text)
_input = [text_norm if text_norm else text]
pred = int(model.predict(_input)[0])
if hasattr(model, "predict_proba"):
proba = float(model.predict_proba(_input)[0][1]) # spam prob
else:
score = float(model.decision_function(_input)[0])
proba = 1 / (1 + np.exp(-score)) # sigmoid
label = "SPAM 🚨" if pred == 1 else "HAM βœ…"
return label, proba
# ----------------------------
# Custom CSS
# ----------------------------
CUSTOM_CSS = """
:root {
--brand: #0ea5e9;
--brand-2: #0284c7;
--success: #22c55e;
--danger: #ef4444;
--card-bg: #0b1220;
--ink: #e5e7eb;
}
.gradio-container {max-width: 2100px !important;}
#app-card {
background: linear-gradient(135deg, #0b1220 0%, #0f172a 100%);
color: var(--ink);
border-radius: 18px;
padding: 28px;
box-shadow: 0 10px 30px rgba(0,0,0,0.35);
border: 1px solid rgba(255,255,255,0.08);
}
#title {font-size: 28px;font-weight: 800;letter-spacing: 0.2px;}
.subtitle {opacity: 0.85;margin-top: 2px;font-size: 14px;}
.badge {
display: inline-block;padding: 4px 10px;border-radius: 999px;
background: linear-gradient(135deg, var(--brand) 0%, var(--brand-2) 100%);
color: white;font-weight: 600;font-size: 12px;
}
#predict-btn > button {
background: linear-gradient(135deg, var(--brand) 0%, var(--brand-2) 100%) !important;
border: none !important;color: white !important;font-weight: 700 !important;
border-radius: 10px !important;
}
#clear-btn > button {
background: transparent !important;border: 1px solid rgba(255,255,255,0.15) !important;
color: var(--ink) !important;font-weight: 600 !important;border-radius: 10px !important;
}
.result-card {
border-radius: 14px;padding: 14px 16px;border: 1px solid rgba(255,255,255,0.08);
display: flex;align-items: center;gap: 10px;
}
.result-label {font-size: 18px;font-weight: 800;}
.confidence-wrap {display: flex;align-items: center;gap: 10px;}
.confidence-bar {
height: 10px;border-radius: 999px;flex: 1;
background: linear-gradient(90deg, var(--danger), var(--success));
position: relative;overflow: hidden;
}
.confidence-fill {
position: absolute;top: 0;left: 0;bottom: 0;
background: rgba(255,255,255,0.9);
mix-blend-mode: overlay;
}
.footer {opacity: 0.7;font-size: 12px;margin-top: 8px;}
"""
# ----------------------------
# UI Prediction wrapper
# ----------------------------
def ui_predict(msg):
label, proba = predict_with_confidence(msg)
pct = int(round(proba * 100))
color = "var(--danger)" if "SPAM" in label else "var(--success)"
styled_label = f"<span style='color:{color}'>{label}</span>"
html = f"""
<div class="result-card">
<div class="result-label">{styled_label}</div>
<div class="confidence-wrap" style="flex:1">
<div style="min-width:120px; text-align:right; font-weight:700;">{pct}%</div>
<div class="confidence-bar">
<div class="confidence-fill" style="width:{pct}%;"></div>
</div>
</div>
</div>
"""
return html
# ----------------------------
# Gradio App
# ----------------------------
with gr.Blocks(css=CUSTOM_CSS, theme=gr.themes.Soft()) as demo:
with gr.Column(elem_id="app-card"):
gr.HTML("""
<div id="title">πŸ“© SMS Spam Classifier <span class="badge">TF-IDF + Logistic Regression</span></div>
<div class="subtitle">Type an SMS below and get an instant prediction with confidence.</div>
""")
with gr.Row():
msg = gr.Textbox(
label="Your message",
placeholder="e.g., Congratulations! You have won a FREE vacation. Text WIN to 90909 now!",
lines=5
)
with gr.Row():
predict_btn = gr.Button("Predict", elem_id="predict-btn")
clear_btn = gr.Button("Clear", elem_id="clear-btn")
result = gr.HTML(label="Result")
with gr.Accordion("Try examples", open=False):
gr.Examples(
examples=[
["Hey, are we still meeting at 7 tonight?"],
["Don’t forget to bring your homework tomorrow."],
["URGENT! Claim your Β£1000 cash prize by calling 0800-123-456."],
["Free entry in 2 a weekly comp for a chance to win an iPhone. Text WIN to 88888."]
],
inputs=[msg]
)
gr.Markdown('<div class="footer">Tip: spam words like "free", "win", "claim", "prize" increase the spam score.</div>')
predict_btn.click(fn=ui_predict, inputs=msg, outputs=result)
clear_btn.click(lambda: "", [], [msg])
msg.submit(fn=ui_predict, inputs=msg, outputs=result)
if __name__ == "__main__":
demo.launch()