roncc13 commited on
Commit
11bc4db
·
verified ·
1 Parent(s): 5620660

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +423 -11
app.py CHANGED
@@ -7,10 +7,12 @@ MODEL_ID = "roncc13/trainCMDBERT-sample"
7
  tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
8
  model = AutoModelForSequenceClassification.from_pretrained(MODEL_ID)
9
 
10
- # Adjust these to match your label order
11
- label_names = ["fake", "real"]
12
 
13
  def classify(text):
 
 
 
14
  inputs = tokenizer(
15
  text,
16
  return_tensors="pt",
@@ -21,15 +23,425 @@ def classify(text):
21
  with torch.no_grad():
22
  outputs = model(**inputs)
23
  probs = torch.softmax(outputs.logits, dim=-1)[0].tolist()
24
- return {label_names[i]: float(probs[i]) for i in range(len(label_names))}
25
-
26
- demo = gr.Interface(
27
- fn=classify,
28
- inputs=gr.Textbox(lines=4, label="Enter Cebuano news text"),
29
- outputs=gr.Label(num_top_classes=2, label="Prediction"),
30
- title="CMD-BERT Cebuano Fake News Detector",
31
- description="Paste Cebuano news text to check if it is likely fake or real."
32
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
  if __name__ == "__main__":
35
  demo.launch()
 
7
  tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
8
  model = AutoModelForSequenceClassification.from_pretrained(MODEL_ID)
9
 
10
+ label_names = ["fake", "real"] # adjust if your order is different
 
11
 
12
  def classify(text):
13
+ if not text.strip():
14
+ return "—", 0.0, "", [], "Please enter some text."
15
+
16
  inputs = tokenizer(
17
  text,
18
  return_tensors="pt",
 
23
  with torch.no_grad():
24
  outputs = model(**inputs)
25
  probs = torch.softmax(outputs.logits, dim=-1)[0].tolist()
26
+
27
+ best_idx = int(torch.argmax(torch.tensor(probs)))
28
+ best_label = label_names[best_idx].upper()
29
+ best_score = float(probs[best_idx])
30
+
31
+ # For now, mock some highlighted phrases & tags.
32
+ # Later you can replace this with real explanation logic.
33
+ phrases = ["walay side effects", "dili kinahanglan og bakuna"]
34
+ tags = ["Sensational claim", "Absolute wording", "Authority framing"]
35
+
36
+ explanation = (
37
+ "Highlighted phrases are examples of strong cues the model may rely on "
38
+ "when estimating whether the text is misleading."
39
+ )
40
+
41
+ return best_label, best_score, text, phrases, explanation, tags
42
+
43
+ custom_css = """
44
+ body {
45
+ background: radial-gradient(circle at top left, #2b2c4a 0, #121320 45%, #080910 100%);
46
+ }
47
+
48
+ /* overall container */
49
+ .gradio-container {
50
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text",
51
+ "Segoe UI", sans-serif;
52
+ max-width: 1100px !important;
53
+ margin: 0 auto !important;
54
+ padding: 20px 10px 40px 10px !important;
55
+ color: #f5f6ff;
56
+ }
57
+
58
+ /* hide default borders */
59
+ .gradio-container .block {
60
+ border: none;
61
+ box-shadow: none;
62
+ }
63
+
64
+ /* top nav bar */
65
+ #top-nav {
66
+ display: flex;
67
+ align-items: center;
68
+ justify-content: space-between;
69
+ margin-bottom: 18px;
70
+ }
71
+
72
+ #top-nav-left {
73
+ display: flex;
74
+ align-items: center;
75
+ gap: 10px;
76
+ }
77
+
78
+ .nav-logo {
79
+ width: 24px;
80
+ height: 24px;
81
+ border-radius: 8px;
82
+ background: linear-gradient(135deg, #ffffff, #c1c5ff);
83
+ }
84
+
85
+ .nav-title {
86
+ font-weight: 600;
87
+ letter-spacing: 0.04em;
88
+ font-size: 14px;
89
+ }
90
+
91
+ .nav-subtitle {
92
+ font-size: 11px;
93
+ opacity: 0.7;
94
+ }
95
+
96
+ #top-nav-right {
97
+ display: flex;
98
+ gap: 24px;
99
+ font-size: 13px;
100
+ opacity: 0.8;
101
+ }
102
+
103
+ /* big hero text */
104
+ #hero {
105
+ margin: 16px 0 18px 0;
106
+ }
107
+
108
+ .hero-title {
109
+ font-size: 30px;
110
+ font-weight: 700;
111
+ letter-spacing: 0.02em;
112
+ line-height: 1.15;
113
+ }
114
+
115
+ .hero-subtitle {
116
+ margin-top: 8px;
117
+ font-size: 13px;
118
+ max-width: 520px;
119
+ opacity: 0.8;
120
+ }
121
+
122
+ /* 2-column main layout */
123
+ #main-row {
124
+ display: grid;
125
+ grid-template-columns: minmax(0, 2.1fr) minmax(0, 1.3fr);
126
+ gap: 20px;
127
+ }
128
+
129
+ /* glass panels */
130
+ .glass-card {
131
+ background: linear-gradient(145deg, rgba(255,255,255,0.12), rgba(255,255,255,0.02));
132
+ border-radius: 18px;
133
+ border: 1px solid rgba(255,255,255,0.12);
134
+ box-shadow: 0 18px 45px rgba(0,0,0,0.6);
135
+ padding: 18px 20px;
136
+ backdrop-filter: blur(14px);
137
+ }
138
+
139
+ /* left column inner layout */
140
+ #left-column {
141
+ display: grid;
142
+ grid-template-rows: auto auto;
143
+ gap: 14px;
144
+ }
145
+
146
+ /* text input card */
147
+ .card-title {
148
+ font-size: 15px;
149
+ font-weight: 600;
150
+ margin-bottom: 6px;
151
+ }
152
+
153
+ .card-subtitle {
154
+ font-size: 11px;
155
+ opacity: 0.75;
156
+ margin-bottom: 8px;
157
+ }
158
+
159
+ #example-box {
160
+ background: #050515;
161
+ border-radius: 10px;
162
+ padding: 10px 12px;
163
+ font-size: 11px;
164
+ line-height: 1.4;
165
+ color: rgba(245,246,255,0.86);
166
+ margin-bottom: 10px;
167
+ }
168
+
169
+ /* gradio textbox */
170
+ textarea {
171
+ background: #050515 !important;
172
+ border-radius: 10px !important;
173
+ border: 1px solid rgba(255,255,255,0.08) !important;
174
+ color: #f5f6ff !important;
175
+ font-size: 13px !important;
176
+ }
177
+
178
+ /* buttons row */
179
+ #input-buttons {
180
+ display: flex;
181
+ align-items: center;
182
+ justify-content: space-between;
183
+ margin-top: 10px;
184
+ font-size: 11px;
185
+ }
186
+
187
+ .btn-primary-custom {
188
+ background: linear-gradient(135deg, #21c38f, #1ab07f) !important;
189
+ color: #041110 !important;
190
+ border-radius: 999px !important;
191
+ border: none !important;
192
+ padding: 6px 20px !important;
193
+ font-weight: 600 !important;
194
+ box-shadow: 0 12px 25px rgba(8, 160, 110, 0.55);
195
+ }
196
+
197
+ .btn-secondary-custom {
198
+ background: transparent !important;
199
+ color: rgba(245,246,255,0.85) !important;
200
+ border-radius: 999px !important;
201
+ border: 1px solid rgba(255,255,255,0.2) !important;
202
+ padding: 6px 18px !important;
203
+ font-size: 11px !important;
204
+ }
205
+
206
+ /* result + why flagged stack */
207
+ #right-column {
208
+ display: grid;
209
+ grid-template-rows: auto auto;
210
+ gap: 14px;
211
+ }
212
+
213
+ /* Result panel */
214
+ .result-label {
215
+ display: inline-flex;
216
+ align-items: center;
217
+ padding: 4px 14px;
218
+ border-radius: 999px;
219
+ font-size: 12px;
220
+ font-weight: 600;
221
+ background: #ff9c8b;
222
+ color: #3b1b19;
223
+ margin-bottom: 4px;
224
+ }
225
+
226
+ .result-meta {
227
+ font-size: 11px;
228
+ opacity: 0.75;
229
+ }
230
+
231
+ .result-confidence {
232
+ font-size: 26px;
233
+ font-weight: 600;
234
+ }
235
+
236
+ /* bar */
237
+ .conf-bar-bg {
238
+ margin-top: 10px;
239
+ width: 100%;
240
+ height: 8px;
241
+ border-radius: 999px;
242
+ background: rgba(255,255,255,0.1);
243
+ }
244
+
245
+ .conf-bar-fill {
246
+ height: 100%;
247
+ border-radius: inherit;
248
+ background: linear-gradient(90deg, #ff9c8b, #ff6e87);
249
+ }
250
+
251
+ /* Why flagged */
252
+ .badge-phrase {
253
+ background: #050515;
254
+ padding: 6px 10px;
255
+ border-radius: 10px;
256
+ font-size: 11px;
257
+ margin: 4px 6px 4px 0;
258
+ display: inline-block;
259
+ }
260
+
261
+ .badge-tag {
262
+ display: inline-block;
263
+ padding: 4px 10px;
264
+ border-radius: 999px;
265
+ font-size: 11px;
266
+ margin-right: 6px;
267
+ margin-top: 6px;
268
+ background: rgba(255,255,255,0.08);
269
+ }
270
+
271
+ /* Mobile tweaks */
272
+ @media (max-width: 900px) {
273
+ #main-row {
274
+ grid-template-columns: minmax(0, 1fr);
275
+ }
276
+ }
277
+ """
278
+
279
+ with gr.Blocks(css=custom_css, fill_height=True, theme=gr.themes.Soft()) as demo:
280
+ # Top bar
281
+ with gr.Row(elem_id="top-nav"):
282
+ with gr.Column(scale=3, elem_id="top-nav-left"):
283
+ gr.HTML(
284
+ """
285
+ <div style="display:flex;align-items:center;gap:10px;">
286
+ <div class="nav-logo"></div>
287
+ <div>
288
+ <div class="nav-title">CMD‑BERT</div>
289
+ <div class="nav-subtitle">Cebuano Misinformation Detector</div>
290
+ </div>
291
+ </div>
292
+ """
293
+ )
294
+ with gr.Column(scale=2, elem_id="top-nav-right"):
295
+ gr.HTML(
296
+ """
297
+ <div id="top-nav-right">
298
+ <span>Analyzer</span>
299
+ <span>How it works</span>
300
+ <span>About</span>
301
+ <span>Feedback</span>
302
+ </div>
303
+ """
304
+ )
305
+
306
+ # Hero
307
+ gr.HTML(
308
+ """
309
+ <section id="hero">
310
+ <div class="hero-title">
311
+ Check Cebuano text for<br>a misleading writing style.
312
+ </div>
313
+ <div class="hero-subtitle">
314
+ Paste a headline, post, or short article. CMD‑BERT returns a label
315
+ (Fake or Legit), confidence score, and key text cues to support the result.
316
+ </div>
317
+ </section>
318
+ """
319
+ )
320
+
321
+ # Main content
322
+ with gr.Row(elem_id="main-row"):
323
+ # Left: text input
324
+ with gr.Column(elem_id="left-column"):
325
+ with gr.Group(elem_classes=["glass-card"]):
326
+ gr.HTML(
327
+ """
328
+ <div class="card-title">Text input</div>
329
+ <div class="card-subtitle">
330
+ Cebuano only. This tool checks linguistic patterns; it does not verify facts.
331
+ </div>
332
+ <div id="example-box">
333
+ <b>Example:</b><br>
334
+ “Nakadisubre og milagro nga tambal sa COVID‑19 ang usa ka local doktor,
335
+ giingon nga walay side effects ug dili kinahanglan og bakuna.”
336
+ </div>
337
+ """
338
+ )
339
+ input_text = gr.Textbox(
340
+ lines=6,
341
+ label="",
342
+ placeholder="Paste Cebuano news here..."
343
+ )
344
+ with gr.Row(elem_id="input-buttons"):
345
+ with gr.Row():
346
+ analyze_btn = gr.Button("Analyze", elem_classes=["btn-primary-custom"])
347
+ clear_btn = gr.Button("Clear", elem_classes=["btn-secondary-custom"])
348
+ gr.Markdown("Tip: Keep inputs under 1,000 characters for faster results.")
349
+
350
+ # Right: result + explanation
351
+ with gr.Column(elem_id="right-column"):
352
+ # Result card
353
+ with gr.Group(elem_classes=["glass-card"]):
354
+ gr.HTML('<div class="card-title">Result</div>')
355
+ result_label = gr.HTML("<div class='result-label'>FAKE</div>")
356
+ result_conf_html = gr.HTML(
357
+ """
358
+ <div style="display:flex;align-items:flex-end;gap:6px;margin-top:2px;">
359
+ <span class="result-confidence" id="conf-val">0.87</span>
360
+ <span style="font-size:11px;opacity:0.8;">confidence</span>
361
+ </div>
362
+ """
363
+ )
364
+ bar_bg = gr.HTML(
365
+ """
366
+ <div class="conf-bar-bg">
367
+ <div id="conf-bar-fill" class="conf-bar-fill" style="width:87%;"></div>
368
+ </div>
369
+ """
370
+ )
371
+ result_meta = gr.Markdown(
372
+ "Model: CMD‑BERT (fine‑tuned BERT‑base).\n\n"
373
+ "Output: Label + confidence score for this text."
374
+ )
375
+
376
+ # Why flagged card
377
+ with gr.Group(elem_classes=["glass-card"]):
378
+ gr.HTML('<div class="card-title">Why this was flagged</div>')
379
+ gr.Markdown(
380
+ "Highlighted phrases are shown as the strongest signals used by the model."
381
+ )
382
+ highlighted_text = gr.HTML(
383
+ "<div class='badge-phrase'>walay side effects</div>"
384
+ )
385
+ tags_md = gr.HTML(
386
+ """
387
+ <div>
388
+ <span class="badge-tag">Sensational claim</span>
389
+ <span class="badge-tag">Absolute wording</span>
390
+ <span class="badge-tag">Authority framing</span>
391
+ </div>
392
+ """
393
+ )
394
+ explanation_md = gr.Markdown(
395
+ "Use this as a screening tool. Always verify with trusted sources."
396
+ )
397
+
398
+ # Wiring function to UI (update label, confidence, bars, explanations)
399
+ def update_ui(text):
400
+ label, score, _, phrases, explanation, tags = classify(text)
401
+ score_pct = int(score * 100)
402
+
403
+ label_html = f"<div class='result-label'>{label}</div>"
404
+ conf_html = (
405
+ f\"\"\"\n<div style="display:flex;align-items:flex-end;gap:6px;margin-top:2px;">
406
+ <span class="result-confidence">{score:.2f}</span>
407
+ <span style="font-size:11px;opacity:0.8;">confidence</span>
408
+ </div>\n\"\"\"
409
+ )
410
+ bar_html = (
411
+ f\"\"\"<div class="conf-bar-bg">
412
+ <div id="conf-bar-fill" class="conf-bar-fill" style="width:{score_pct}%;"></div>
413
+ </div>\"\"\"
414
+ )
415
+
416
+ phrase_html = "".join(
417
+ [f"<span class='badge-phrase'>{p}</span>" for p in phrases]
418
+ )
419
+ tags_html = "".join(
420
+ [f"<span class='badge-tag'>{t}</span>" for t in tags]
421
+ )
422
+
423
+ return (
424
+ label_html,
425
+ conf_html,
426
+ bar_html,
427
+ phrase_html,
428
+ explanation,
429
+ tags_html,
430
+ )
431
+
432
+ analyze_btn.click(
433
+ fn=update_ui,
434
+ inputs=input_text,
435
+ outputs=[
436
+ result_label,
437
+ result_conf_html,
438
+ bar_bg,
439
+ highlighted_text,
440
+ explanation_md,
441
+ tags_md,
442
+ ],
443
+ )
444
+ clear_btn.click(None, [], [input_text], _js="() => ''")
445
 
446
  if __name__ == "__main__":
447
  demo.launch()