Spaces:
Sleeping
Sleeping
File size: 5,520 Bytes
3f20604 |
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 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
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()
|