Update app.py
Browse files
app.py
CHANGED
@@ -3,93 +3,136 @@ import pickle
|
|
3 |
import re
|
4 |
import numpy as np
|
5 |
|
6 |
-
# β
|
7 |
-
|
8 |
-
vectorizer = pickle.load(f)
|
9 |
|
10 |
-
|
11 |
-
model = pickle.load(f)
|
12 |
-
|
13 |
-
# β
Streamlit page configuration
|
14 |
-
st.set_page_config(page_title="Stack Overflow Tags Predictor", layout="centered")
|
15 |
-
|
16 |
-
# β
Custom CSS for better UI
|
17 |
st.markdown("""
|
18 |
<style>
|
19 |
-
|
20 |
-
background
|
|
|
|
|
|
|
|
|
|
|
21 |
padding: 2rem;
|
22 |
-
border-radius:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
}
|
24 |
-
|
25 |
-
|
26 |
-
font-size:
|
27 |
text-align: center;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
}
|
29 |
-
|
30 |
-
|
|
|
|
|
|
|
|
|
31 |
font-weight: 600;
|
|
|
|
|
32 |
}
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
display: inline-block;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
}
|
41 |
</style>
|
42 |
""", unsafe_allow_html=True)
|
43 |
|
44 |
-
|
45 |
-
st.markdown('<h1 class="title">Stack Overflow Tags Predictor</h1>', unsafe_allow_html=True)
|
46 |
-
|
47 |
-
# β
Input text
|
48 |
-
user_input = st.text_area("Enter your Stack Overflow question:", height=180)
|
49 |
-
|
50 |
-
# β
Preprocess function
|
51 |
def clean_text(text):
|
52 |
-
text =
|
53 |
-
text = re.sub(r"
|
54 |
-
text = re.sub(r"
|
55 |
return text
|
56 |
|
57 |
-
# β
|
58 |
-
st.
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
st.
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
if
|
84 |
-
|
85 |
-
|
86 |
-
else:
|
87 |
-
tags = predict_tags(user_input, threshold)
|
88 |
-
if len(tags) > 0:
|
89 |
-
st.markdown('<h3 class="tag-title">Predicted Tags:</h3>', unsafe_allow_html=True)
|
90 |
-
for tag in tags:
|
91 |
-
st.markdown(f'<span class="predicted-tag">{tag}</span>', unsafe_allow_html=True)
|
92 |
else:
|
93 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
|
95 |
-
st.markdown(
|
|
|
3 |
import re
|
4 |
import numpy as np
|
5 |
|
6 |
+
# β
Page Config
|
7 |
+
st.set_page_config(page_title="Stack Overflow Tags Predictor", layout="wide")
|
|
|
8 |
|
9 |
+
# β
Enhanced CSS for a modern UI
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
st.markdown("""
|
11 |
<style>
|
12 |
+
body {
|
13 |
+
background: linear-gradient(to right, #e3f2fd, #ffffff);
|
14 |
+
}
|
15 |
+
|
16 |
+
.main-container {
|
17 |
+
max-width: 850px;
|
18 |
+
margin: auto;
|
19 |
padding: 2rem;
|
20 |
+
border-radius: 16px;
|
21 |
+
background: rgba(255, 255, 255, 0.9);
|
22 |
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
|
23 |
+
}
|
24 |
+
|
25 |
+
.title-text {
|
26 |
+
font-size: 2.4rem;
|
27 |
+
font-weight: bold;
|
28 |
+
text-align: center;
|
29 |
+
color: #1e3a8a;
|
30 |
+
margin-bottom: 0.5rem;
|
31 |
}
|
32 |
+
|
33 |
+
.description-text {
|
34 |
+
font-size: 1.1rem;
|
35 |
text-align: center;
|
36 |
+
color: #333;
|
37 |
+
margin-bottom: 2rem;
|
38 |
+
}
|
39 |
+
|
40 |
+
.stTextInput input, .stTextArea textarea {
|
41 |
+
border-radius: 8px;
|
42 |
+
padding: 12px;
|
43 |
+
border: 1px solid #ccc;
|
44 |
+
background-color: #fafafa;
|
45 |
}
|
46 |
+
|
47 |
+
.stButton button {
|
48 |
+
background: linear-gradient(to right, #1e88e5, #1976d2);
|
49 |
+
color: white;
|
50 |
+
border-radius: 8px;
|
51 |
+
padding: 0.6rem 1.4rem;
|
52 |
font-weight: 600;
|
53 |
+
transition: 0.3s ease;
|
54 |
+
border: none;
|
55 |
}
|
56 |
+
|
57 |
+
.stButton button:hover {
|
58 |
+
background: linear-gradient(to right, #1565c0, #0d47a1);
|
59 |
+
}
|
60 |
+
|
61 |
+
.tag {
|
62 |
display: inline-block;
|
63 |
+
margin: 5px 6px 0 0;
|
64 |
+
background-color: #0ea5e9;
|
65 |
+
padding: 6px 14px;
|
66 |
+
border-radius: 20px;
|
67 |
+
font-size: 0.85rem;
|
68 |
+
color: white;
|
69 |
+
font-weight: 500;
|
70 |
+
}
|
71 |
+
|
72 |
+
.result-container {
|
73 |
+
text-align: center;
|
74 |
+
margin-top: 2rem;
|
75 |
}
|
76 |
</style>
|
77 |
""", unsafe_allow_html=True)
|
78 |
|
79 |
+
# β
Text Preprocessing
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
def clean_text(text):
|
81 |
+
text = re.sub(r"<.*?>", " ", text)
|
82 |
+
text = re.sub(r"\W", " ", text)
|
83 |
+
text = re.sub(r"\s+", " ", text.lower()).strip()
|
84 |
return text
|
85 |
|
86 |
+
# β
Load Pickled Artifacts
|
87 |
+
@st.cache_resource
|
88 |
+
def load_artifacts():
|
89 |
+
with open("model12.pkl", "rb") as f:
|
90 |
+
model = pickle.load(f)
|
91 |
+
with open("tfidf12.pkl", "rb") as f:
|
92 |
+
vectorizer = pickle.load(f)
|
93 |
+
with open("mlb12.pkl", "rb") as f:
|
94 |
+
mlb = pickle.load(f)
|
95 |
+
return model, vectorizer, mlb
|
96 |
+
|
97 |
+
model, vectorizer, mlb = load_artifacts()
|
98 |
+
|
99 |
+
# β
UI Container
|
100 |
+
with st.container():
|
101 |
+
st.markdown("<div class='main-container'>", unsafe_allow_html=True)
|
102 |
+
|
103 |
+
st.markdown("<div class='title-text'>π Stack Overflow Tags Predictor</div>", unsafe_allow_html=True)
|
104 |
+
st.markdown("<div class='description-text'>Enter your question title and description to generate the most relevant tags using Machine Learning.</div>", unsafe_allow_html=True)
|
105 |
+
|
106 |
+
with st.form(key="tag_prediction_form"):
|
107 |
+
title = st.text_input("π Enter Question Title")
|
108 |
+
body = st.text_area("π Enter Question Description", height=180)
|
109 |
+
threshold = st.slider("π§ Tag Confidence Threshold", min_value=0.1, max_value=0.9, value=0.3, step=0.05)
|
110 |
+
submitted = st.form_submit_button("π Predict Tags")
|
111 |
+
|
112 |
+
if submitted:
|
113 |
+
if not title.strip() or not body.strip():
|
114 |
+
st.warning("β οΈ Please fill in both the title and description.")
|
|
|
|
|
|
|
|
|
|
|
|
|
115 |
else:
|
116 |
+
with st.spinner("π Predicting the most relevant tags..."):
|
117 |
+
input_text = clean_text(title + " " + body)
|
118 |
+
X_input = vectorizer.transform([input_text])
|
119 |
+
|
120 |
+
try:
|
121 |
+
y_prob = model.predict_proba(X_input)
|
122 |
+
y_pred = (y_prob >= threshold).astype(int)
|
123 |
+
except AttributeError:
|
124 |
+
y_pred = model.predict(X_input)
|
125 |
+
|
126 |
+
predicted_tags = mlb.inverse_transform(y_pred)
|
127 |
+
|
128 |
+
with st.container():
|
129 |
+
st.markdown("<div class='result-container'>", unsafe_allow_html=True)
|
130 |
+
if predicted_tags and predicted_tags[0]:
|
131 |
+
st.success("β
Tags Predicted Successfully!")
|
132 |
+
tag_html = "".join([f"<span class='tag'>{tag}</span>" for tag in predicted_tags[0]])
|
133 |
+
st.markdown(tag_html, unsafe_allow_html=True)
|
134 |
+
else:
|
135 |
+
st.info("π€ No tags predicted. Try refining your input or lowering the threshold.")
|
136 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
137 |
|
138 |
+
st.markdown("</div>", unsafe_allow_html=True)
|