hmusman2804045-max commited on
Commit
77d0745
·
1 Parent(s): 7da5178

Phase 9: Add Gradio Space app (gradio_app.py), HF Spaces README frontmatter, updated requirements.txt + CI/CD workflow

Browse files
Files changed (4) hide show
  1. .github/workflows/deploy.yml +2 -2
  2. README.md +13 -0
  3. gradio_app.py +268 -0
  4. requirements.txt +1 -0
.github/workflows/deploy.yml CHANGED
@@ -7,7 +7,7 @@ on:
7
 
8
  jobs:
9
  deploy:
10
- name: Push to HuggingFace Spaces
11
  runs-on: ubuntu-latest
12
 
13
  steps:
@@ -17,7 +17,7 @@ jobs:
17
  fetch-depth: 0
18
  lfs: true
19
 
20
- - name: Push to HuggingFace Space
21
  env:
22
  HF_TOKEN: ${{ secrets.HF_TOKEN }}
23
  run: |
 
7
 
8
  jobs:
9
  deploy:
10
+ name: Push to HuggingFace Spaces (Gradio)
11
  runs-on: ubuntu-latest
12
 
13
  steps:
 
17
  fetch-depth: 0
18
  lfs: true
19
 
20
+ - name: Push to HuggingFace Gradio Space
21
  env:
22
  HF_TOKEN: ${{ secrets.HF_TOKEN }}
23
  run: |
README.md CHANGED
@@ -1,3 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # Urdu Sentiment and Emotion Analysis Engine
2
 
3
  Welcome to the Urdu Sentiment and Emotion Analysis Engine project! This repository contains the code for a multilingual NLP system that classifies sentiment (Positive, Negative, Neutral) and emotion (Joy, Anger, Fear, Sadness) from Urdu, Roman Urdu, and mixed-language text using a fine-tuned XLM-RoBERTa transformer.
 
1
+ ---
2
+ title: Urdu Sentiment and Emotion Engine
3
+ emoji: 🇵🇰
4
+ colorFrom: violet
5
+ colorTo: blue
6
+ sdk: gradio
7
+ sdk_version: 4.44.0
8
+ app_file: gradio_app.py
9
+ pinned: true
10
+ license: apache-2.0
11
+ short_description: XLM-RoBERTa fine-tuned for Urdu & Roman Urdu sentiment + emotion
12
+ ---
13
+
14
  # Urdu Sentiment and Emotion Analysis Engine
15
 
16
  Welcome to the Urdu Sentiment and Emotion Analysis Engine project! This repository contains the code for a multilingual NLP system that classifies sentiment (Positive, Negative, Neutral) and emotion (Joy, Anger, Fear, Sadness) from Urdu, Roman Urdu, and mixed-language text using a fine-tuned XLM-RoBERTa transformer.
gradio_app.py ADDED
@@ -0,0 +1,268 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from predictor import SentimentEmotionPredictor
3
+
4
+ # ── Load models once at startup ──────────────────────────────────────────────
5
+ print("Initialising Urdu Sentiment & Emotion Engine…")
6
+ engine = SentimentEmotionPredictor()
7
+ print("Engine ready.")
8
+
9
+ # ── Emoji / colour maps ───────────────────────────────────────────────────────
10
+ SENTIMENT_EMOJI = {"Positive": "😊", "Negative": "😞", "Neutral": "😐"}
11
+ EMOTION_EMOJI = {"Joy": "🎉", "Anger": "😡", "Fear": "😨", "Sadness": "😢"}
12
+
13
+ SENTIMENT_COLOR = {
14
+ "Positive": "#22c55e",
15
+ "Negative": "#ef4444",
16
+ "Neutral": "#facc15",
17
+ }
18
+ EMOTION_COLOR = {
19
+ "Joy": "#f59e0b",
20
+ "Anger": "#ef4444",
21
+ "Fear": "#8b5cf6",
22
+ "Sadness": "#3b82f6",
23
+ }
24
+
25
+ # ── Example inputs ─────────────────────────────────────────────────────────────
26
+ EXAMPLES = [
27
+ ["آج کا دن بہت اچھا ہے، بہت خوشی ہوئی"],
28
+ ["mujhe bohat gussa aa raha hai is cheez par"],
29
+ ["یہ صورتحال بہت خطرناک اور ڈراؤنی ہے"],
30
+ ["Aaj mera dil bohat udaas hai, kuch bhi acha nahi lag raha"],
31
+ ["بالکل ٹھیک ہے، کوئی خاص بات نہیں"],
32
+ ["Yeh sab dekh kar dil khush ho gaya, wah wah!"],
33
+ ]
34
+
35
+
36
+ def build_attention_html(attention_list):
37
+ if not attention_list:
38
+ return "<p style='color:#9ca3af;font-size:0.85rem'>No attention data.</p>"
39
+ max_score = max(a["score"] for a in attention_list) or 1.0
40
+ html = "<div style='display:flex;flex-wrap:wrap;gap:6px;padding:8px 0;'>"
41
+ for item in attention_list:
42
+ intensity = item["score"] / max_score
43
+ alpha = 0.15 + intensity * 0.75
44
+ font_w = 400 + int(intensity * 300)
45
+ html += (
46
+ f"<span style='background:rgba(139,92,246,{alpha:.2f});color:#e9d5ff;"
47
+ f"padding:3px 8px;border-radius:12px;font-size:0.9rem;"
48
+ f"font-weight:{font_w};border:1px solid rgba(139,92,246,0.3);'>"
49
+ f"{item['word']}</span>"
50
+ )
51
+ html += "</div>"
52
+ return html
53
+
54
+
55
+ def build_bar(label, score, color):
56
+ pct = round(score * 100, 1)
57
+ return (
58
+ f"<div style='margin-bottom:8px;'>"
59
+ f"<div style='display:flex;justify-content:space-between;font-size:0.82rem;"
60
+ f"color:#d1d5db;margin-bottom:3px;'><span>{label}</span><span>{pct}%</span></div>"
61
+ f"<div style='background:#1f2937;border-radius:999px;height:8px;overflow:hidden;'>"
62
+ f"<div style='width:{pct}%;background:{color};height:100%;border-radius:999px;"
63
+ f"transition:width 0.6s ease;'></div></div></div>"
64
+ )
65
+
66
+
67
+ def analyse(text):
68
+ if not text or not text.strip():
69
+ return (
70
+ "<p style='color:#ef4444'>Please enter some Urdu or Roman Urdu text.</p>",
71
+ "", "", "",
72
+ )
73
+
74
+ result = engine.predict(text)
75
+
76
+ if "error" in result:
77
+ return (f"<p style='color:#ef4444'>{result['error']}</p>", "", "", "")
78
+
79
+ sentiment = result["sentiment"]
80
+ emotion = result["emotion"]
81
+ s_scores = result["sentiment_scores"]
82
+ e_scores = result["emotion_scores"]
83
+ attention = result["attention"]
84
+
85
+ s_emoji = SENTIMENT_EMOJI.get(sentiment, "")
86
+ e_emoji = EMOTION_EMOJI.get(emotion, "")
87
+ s_color = SENTIMENT_COLOR.get(sentiment, "#6b7280")
88
+ e_color = EMOTION_COLOR.get(emotion, "#6b7280")
89
+
90
+ result_html = f"""
91
+ <div style='background:linear-gradient(135deg,#1e1b4b 0%,#111827 100%);
92
+ border:1px solid rgba(139,92,246,0.35);border-radius:16px;padding:20px 24px;
93
+ font-family:Inter,sans-serif;'>
94
+ <div style='display:flex;gap:16px;flex-wrap:wrap;'>
95
+ <div style='flex:1;min-width:140px;background:rgba(0,0,0,0.3);
96
+ border:2px solid {s_color};border-radius:12px;padding:14px 18px;text-align:center;'>
97
+ <div style='font-size:2rem;'>{s_emoji}</div>
98
+ <div style='font-size:0.72rem;letter-spacing:0.1em;color:#9ca3af;margin:4px 0 2px;'>SENTIMENT</div>
99
+ <div style='font-size:1.25rem;font-weight:700;color:{s_color};'>{sentiment}</div>
100
+ </div>
101
+ <div style='flex:1;min-width:140px;background:rgba(0,0,0,0.3);
102
+ border:2px solid {e_color};border-radius:12px;padding:14px 18px;text-align:center;'>
103
+ <div style='font-size:2rem;'>{e_emoji}</div>
104
+ <div style='font-size:0.72rem;letter-spacing:0.1em;color:#9ca3af;margin:4px 0 2px;'>EMOTION</div>
105
+ <div style='font-size:1.25rem;font-weight:700;color:{e_color};'>{emotion}</div>
106
+ </div>
107
+ </div>
108
+ </div>
109
+ """
110
+
111
+ s_bars_html = "<div style='padding:4px 0;'>"
112
+ for lbl, sc in s_scores.items():
113
+ s_bars_html += build_bar(lbl, sc, SENTIMENT_COLOR.get(lbl, "#6b7280"))
114
+ s_bars_html += "</div>"
115
+
116
+ e_bars_html = "<div style='padding:4px 0;'>"
117
+ for lbl, sc in e_scores.items():
118
+ e_bars_html += build_bar(lbl, sc, EMOTION_COLOR.get(lbl, "#6b7280"))
119
+ e_bars_html += "</div>"
120
+
121
+ attn_html = build_attention_html(attention)
122
+
123
+ return result_html, s_bars_html, e_bars_html, attn_html
124
+
125
+
126
+ CSS = """
127
+ body, .gradio-container {
128
+ background: #0f0c29 !important;
129
+ font-family: 'Inter', sans-serif !important;
130
+ }
131
+ #header-banner {
132
+ background: linear-gradient(135deg,#1a0533 0%,#0f172a 50%,#0c1445 100%);
133
+ border-bottom: 1px solid rgba(139,92,246,0.3);
134
+ padding: 28px 24px 18px;
135
+ text-align: center;
136
+ border-radius: 16px 16px 0 0;
137
+ margin-bottom: 4px;
138
+ }
139
+ #header-banner h1 {
140
+ font-size: clamp(1.4rem, 4vw, 2rem);
141
+ font-weight: 800;
142
+ background: linear-gradient(90deg, #a78bfa, #60a5fa, #34d399);
143
+ -webkit-background-clip: text;
144
+ -webkit-text-fill-color: transparent;
145
+ margin: 0 0 6px;
146
+ letter-spacing: -0.02em;
147
+ }
148
+ #header-banner p { color: #94a3b8; font-size: 0.9rem; margin: 0; }
149
+ #input-box textarea {
150
+ background: #1e1b4b !important;
151
+ border: 1.5px solid rgba(139,92,246,0.4) !important;
152
+ border-radius: 12px !important;
153
+ color: #e2e8f0 !important;
154
+ font-size: 1rem !important;
155
+ line-height: 1.6 !important;
156
+ padding: 14px !important;
157
+ }
158
+ #input-box textarea:focus {
159
+ border-color: #a78bfa !important;
160
+ box-shadow: 0 0 0 3px rgba(167,139,250,0.15) !important;
161
+ }
162
+ #analyse-btn {
163
+ background: linear-gradient(135deg,#7c3aed,#4f46e5) !important;
164
+ border: none !important;
165
+ border-radius: 10px !important;
166
+ font-weight: 700 !important;
167
+ font-size: 1rem !important;
168
+ color: #fff !important;
169
+ padding: 10px 0 !important;
170
+ transition: opacity 0.2s !important;
171
+ }
172
+ #analyse-btn:hover { opacity: 0.88 !important; }
173
+ #clear-btn {
174
+ background: rgba(31,41,55,0.8) !important;
175
+ border: 1px solid rgba(139,92,246,0.3) !important;
176
+ border-radius: 10px !important;
177
+ color: #9ca3af !important;
178
+ }
179
+ .section-label {
180
+ font-size: 0.72rem;
181
+ letter-spacing: 0.12em;
182
+ color: #7c3aed;
183
+ font-weight: 700;
184
+ text-transform: uppercase;
185
+ margin-bottom: 6px;
186
+ }
187
+ .output-panel {
188
+ background: rgba(17,24,39,0.85) !important;
189
+ border: 1px solid rgba(139,92,246,0.25) !important;
190
+ border-radius: 14px !important;
191
+ padding: 16px !important;
192
+ }
193
+ #footer {
194
+ text-align: center;
195
+ color: #4b5563;
196
+ font-size: 0.78rem;
197
+ margin-top: 16px;
198
+ padding: 12px 0 4px;
199
+ border-top: 1px solid rgba(139,92,246,0.15);
200
+ }
201
+ """
202
+
203
+ with gr.Blocks(
204
+ theme=gr.themes.Base(
205
+ primary_hue="violet",
206
+ neutral_hue="slate",
207
+ font=gr.themes.GoogleFont("Inter"),
208
+ ),
209
+ css=CSS,
210
+ title="Urdu Sentiment & Emotion Engine",
211
+ ) as demo:
212
+
213
+ gr.HTML("""
214
+ <div id="header-banner">
215
+ <h1>🇵🇰 Urdu Sentiment &amp; Emotion Analysis Engine</h1>
216
+ <p>XLM-RoBERTa fine-tuned on Urdu · Roman Urdu · Mixed language text</p>
217
+ </div>
218
+ """)
219
+
220
+ with gr.Row():
221
+ with gr.Column(scale=5):
222
+ gr.HTML("<p class='section-label'>✍️ Enter Text</p>")
223
+ text_input = gr.Textbox(
224
+ placeholder="اردو یا Roman Urdu میں لکھیں…\nYa Roman Urdu mein likhein…",
225
+ lines=5,
226
+ max_lines=10,
227
+ show_label=False,
228
+ elem_id="input-box",
229
+ )
230
+ with gr.Row():
231
+ analyse_btn = gr.Button("🔍 Analyse", variant="primary", elem_id="analyse-btn")
232
+ clear_btn = gr.Button("✕ Clear", variant="secondary", elem_id="clear-btn")
233
+
234
+ gr.HTML("<p class='section-label' style='margin-top:18px;'>💡 Try an Example</p>")
235
+ gr.Examples(examples=EXAMPLES, inputs=text_input, label="")
236
+
237
+ with gr.Column(scale=5):
238
+ gr.HTML("<p class='section-label'>🎯 Prediction</p>")
239
+ result_out = gr.HTML(elem_classes=["output-panel"])
240
+
241
+ with gr.Row():
242
+ with gr.Column():
243
+ gr.HTML("<p class='section-label' style='margin-top:14px;'>📊 Sentiment Confidence</p>")
244
+ sent_bars = gr.HTML(elem_classes=["output-panel"])
245
+ with gr.Column():
246
+ gr.HTML("<p class='section-label' style='margin-top:14px;'>📊 Emotion Confidence</p>")
247
+ emot_bars = gr.HTML(elem_classes=["output-panel"])
248
+
249
+ gr.HTML("<p class='section-label' style='margin-top:14px;'>🔦 Word Attention Highlights</p>")
250
+ attn_out = gr.HTML(elem_classes=["output-panel"])
251
+
252
+ gr.HTML("""
253
+ <div id="footer">
254
+ Powered by <strong>XLM-RoBERTa</strong> · Fine-tuned by <strong>Muhammad Usman</strong> ·
255
+ <a href="https://github.com/hmusman2804045-max/Urdu-Sentiment-and-Emotion-Analysis-Engine"
256
+ style="color:#7c3aed;" target="_blank">GitHub ↗</a>
257
+ </div>
258
+ """)
259
+
260
+ analyse_btn.click(fn=analyse, inputs=text_input,
261
+ outputs=[result_out, sent_bars, emot_bars, attn_out])
262
+ text_input.submit(fn=analyse, inputs=text_input,
263
+ outputs=[result_out, sent_bars, emot_bars, attn_out])
264
+ clear_btn.click(fn=lambda: ("", "", "", ""), inputs=None,
265
+ outputs=[result_out, sent_bars, emot_bars, attn_out])
266
+
267
+ if __name__ == "__main__":
268
+ demo.launch()
requirements.txt CHANGED
@@ -8,3 +8,4 @@ numpy==1.26.0
8
  pandas==2.2.0
9
  scikit-learn==1.4.0
10
  accelerate==0.29.0
 
 
8
  pandas==2.2.0
9
  scikit-learn==1.4.0
10
  accelerate==0.29.0
11
+ gradio==4.44.0