roncc13 commited on
Commit
aeb0e6c
·
verified ·
1 Parent(s): 06b8083

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +381 -11
app.py CHANGED
@@ -2,15 +2,20 @@ import gradio as gr
2
  from transformers import AutoTokenizer, AutoModelForSequenceClassification
3
  import torch
4
 
 
 
 
5
  MODEL_ID = "roncc13/trainCMDBERT-sample"
6
 
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",
@@ -23,13 +28,378 @@ def classify(text):
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()
 
2
  from transformers import AutoTokenizer, AutoModelForSequenceClassification
3
  import torch
4
 
5
+ # --------------------------
6
+ # Model setup
7
+ # --------------------------
8
  MODEL_ID = "roncc13/trainCMDBERT-sample"
9
 
10
  tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
11
  model = AutoModelForSequenceClassification.from_pretrained(MODEL_ID)
12
 
13
+ label_names = ["fake", "real"] # adjust to match your label order
 
14
 
15
+
16
+ def classify(text: str):
17
+ if not text.strip():
18
+ return {"fake": 0.0, "real": 0.0}
19
  inputs = tokenizer(
20
  text,
21
  return_tensors="pt",
 
28
  probs = torch.softmax(outputs.logits, dim=-1)[0].tolist()
29
  return {label_names[i]: float(probs[i]) for i in range(len(label_names))}
30
 
31
+
32
+ # --------------------------
33
+ # Shared CSS (glass design)
34
+ # --------------------------
35
+ custom_css = """
36
+ body {
37
+ background: radial-gradient(circle at top left, #2b2c4a 0, #121320 45%, #080910 100%);
38
+ }
39
+ .gradio-container {
40
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text",
41
+ "Segoe UI", sans-serif;
42
+ max-width: 1100px !important;
43
+ margin: 0 auto !important;
44
+ padding: 20px 10px 40px 10px !important;
45
+ color: #f5f6ff;
46
+ }
47
+ .glass-card {
48
+ background: linear-gradient(145deg, rgba(255,255,255,0.12), rgba(255,255,255,0.03));
49
+ border-radius: 18px;
50
+ border: 1px solid rgba(255,255,255,0.16);
51
+ box-shadow: 0 18px 45px rgba(0,0,0,0.6);
52
+ padding: 18px 20px;
53
+ backdrop-filter: blur(14px);
54
+ }
55
+ .nav-logo {
56
+ width: 24px;
57
+ height: 24px;
58
+ border-radius: 8px;
59
+ background: linear-gradient(135deg, #ffffff, #c1c5ff);
60
+ }
61
+ .nav-title {
62
+ font-weight: 600;
63
+ letter-spacing: 0.04em;
64
+ font-size: 14px;
65
+ }
66
+ .nav-subtitle {
67
+ font-size: 11px;
68
+ opacity: 0.7;
69
+ }
70
+ .nav-link {
71
+ font-size: 13px;
72
+ opacity: 0.85;
73
+ }
74
+ .nav-link-active {
75
+ font-weight: 600;
76
+ }
77
+ .btn-primary-custom {
78
+ background: linear-gradient(135deg, #21c38f, #1ab07f) !important;
79
+ color: #041110 !important;
80
+ border-radius: 999px !important;
81
+ border: none !important;
82
+ padding: 8px 22px !important;
83
+ font-weight: 600 !important;
84
+ box-shadow: 0 12px 25px rgba(8, 160, 110, 0.55);
85
+ }
86
+ .btn-secondary-custom {
87
+ background: transparent !important;
88
+ color: rgba(245,246,255,0.85) !important;
89
+ border-radius: 999px !important;
90
+ border: 1px solid rgba(255,255,255,0.2) !important;
91
+ padding: 8px 18px !important;
92
+ font-size: 11px !important;
93
+ }
94
+ .hero-title {
95
+ font-size: 30px;
96
+ font-weight: 700;
97
+ letter-spacing: 0.02em;
98
+ line-height: 1.15;
99
+ }
100
+ .hero-subtitle {
101
+ margin-top: 8px;
102
+ font-size: 13px;
103
+ max-width: 520px;
104
+ opacity: 0.85;
105
+ }
106
+ .conf-bar-bg {
107
+ margin-top: 10px;
108
+ width: 100%;
109
+ height: 8px;
110
+ border-radius: 999px;
111
+ background: rgba(255,255,255,0.1);
112
+ }
113
+ .conf-bar-fill {
114
+ height: 100%;
115
+ border-radius: inherit;
116
+ background: linear-gradient(90deg, #ff9c8b, #ff6e87);
117
+ }
118
+ .badge-pill {
119
+ display: inline-flex;
120
+ align-items: center;
121
+ padding: 4px 14px;
122
+ border-radius: 999px;
123
+ font-size: 12px;
124
+ font-weight: 600;
125
+ }
126
+ .badge-fake {
127
+ background: #ff9c8b;
128
+ color: #3b1b19;
129
+ }
130
+ .badge-real {
131
+ background: #7fe6c2;
132
+ color: #07352a;
133
+ }
134
+ textarea {
135
+ background: #050515 !important;
136
+ border-radius: 10px !important;
137
+ border: 1px solid rgba(255,255,255,0.08) !important;
138
+ color: #f5f6ff !important;
139
+ font-size: 13px !important;
140
+ }
141
+ @media (max-width: 900px) {
142
+ .two-col {
143
+ flex-direction: column !important;
144
+ }
145
+ }
146
+ """
147
+
148
+
149
+ def navbar(active: str):
150
+ """Small helper to render the top nav HTML with active link highlighted."""
151
+ def cls(name):
152
+ return "nav-link nav-link-active" if name == active else "nav-link"
153
+
154
+ return gr.HTML(
155
+ f"""
156
+ <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:18px;">
157
+ <div style="display:flex;align-items:center;gap:10px;">
158
+ <div class="nav-logo"></div>
159
+ <div>
160
+ <div class="nav-title">CMD‑BERT</div>
161
+ <div class="nav-subtitle">Cebuano Misinformation Detector</div>
162
+ </div>
163
+ </div>
164
+ <div style="display:flex;gap:24px;">
165
+ <a class="{cls('Analyzer')}" href="#/analyzer">Analyzer</a>
166
+ <a class="{cls('How')}" href="#/how-it-works">How it works</a>
167
+ <a class="{cls('About')}" href="#/about">About</a>
168
+ <a class="{cls('Feedback')}" href="#/feedback">Feedback</a>
169
+ </div>
170
+ </div>
171
+ """
172
+ )
173
+
174
+
175
+ # --------------------------
176
+ # Build multipage app
177
+ # --------------------------
178
+ with gr.Blocks(fill_height=True) as demo:
179
+
180
+ # ------------- Analyzer page -------------
181
+ with gr.Page(name="analyzer"):
182
+ navbar("Analyzer")
183
+
184
+ gr.HTML(
185
+ """
186
+ <section style="margin-bottom:18px;">
187
+ <div class="hero-title">
188
+ Check Cebuano text for a misleading writing style.
189
+ </div>
190
+ <div class="hero-subtitle">
191
+ Paste a headline, post, or short article. CMD‑BERT returns a label
192
+ (Fake or Legit) and a confidence score based on writing patterns.
193
+ </div>
194
+ </section>
195
+ """
196
+ )
197
+
198
+ with gr.Row(elem_classes=["two-col"]):
199
+ # Left: input card
200
+ with gr.Column(scale=3):
201
+ with gr.Group(elem_classes=["glass-card"]):
202
+ gr.Markdown(
203
+ "### Text input\n"
204
+ "Cebuano only. This tool checks linguistic patterns; it does not verify facts."
205
+ )
206
+ gr.Markdown(
207
+ "> **Example** \n"
208
+ "> “Nakadisubre og milagro nga tambal sa COVID‑19 ang usa ka local doktor, "
209
+ "giingon nga walay side effects ug dili kinahanglan og bakuna.”"
210
+ )
211
+ news_text = gr.Textbox(
212
+ lines=7,
213
+ label="",
214
+ placeholder="Paste Cebuano news text here..."
215
+ )
216
+ with gr.Row():
217
+ analyze_btn = gr.Button("Analyze", elem_classes=["btn-primary-custom"])
218
+ clear_btn = gr.Button("Clear", elem_classes=["btn-secondary-custom"])
219
+ gr.Markdown(
220
+ "Tip: Keep inputs under 1,000 characters for faster results."
221
+ )
222
+
223
+ # Right: result card
224
+ with gr.Column(scale=2):
225
+ with gr.Group(elem_classes=["glass-card"]):
226
+ gr.Markdown("### Result")
227
+ result_label_html = gr.HTML(
228
+ '<span class="badge-pill badge-fake">FAKE</span>'
229
+ )
230
+ conf_text = gr.HTML(
231
+ '<div style="display:flex;align-items:flex-end;gap:6px;margin-top:8px;">'
232
+ '<span style="font-size:26px;font-weight:600;" id="conf-val">0.00</span>'
233
+ '<span style="font-size:12px;opacity:0.8;">confidence</span>'
234
+ '</div>'
235
+ )
236
+ conf_bar = gr.HTML(
237
+ '<div class="conf-bar-bg">'
238
+ '<div class="conf-bar-fill" style="width:0%;"></div>'
239
+ '</div>'
240
+ )
241
+ gr.Markdown(
242
+ "Model: CMD‑BERT (fine‑tuned BERT‑base). \n"
243
+ "Output: Label and confidence score for the submitted text."
244
+ )
245
+
246
+ # backend → UI glue
247
+ def analyze_ui(text):
248
+ probs = classify(text)
249
+ fake_p = probs.get("fake", 0.0)
250
+ real_p = probs.get("real", 0.0)
251
+ if fake_p >= real_p:
252
+ label = "FAKE"
253
+ css_class = "badge-pill badge-fake"
254
+ conf = fake_p
255
+ else:
256
+ label = "LEGIT"
257
+ css_class = "badge-pill badge-real"
258
+ conf = real_p
259
+ conf_pct = int(conf * 100)
260
+ label_html = f'<span class="{css_class}">{label}</span>'
261
+ conf_html = (
262
+ '<div style="display:flex;align-items:flex-end;gap:6px;margin-top:8px;">'
263
+ f'<span style="font-size:26px;font-weight:600;" id="conf-val">{conf:.2f}</span>'
264
+ '<span style="font-size:12px;opacity:0.8;">confidence</span>'
265
+ '</div>'
266
+ )
267
+ bar_html = (
268
+ '<div class="conf-bar-bg">'
269
+ f'<div class="conf-bar-fill" style="width:{conf_pct}%;"></div>'
270
+ '</div>'
271
+ )
272
+ return label_html, conf_html, bar_html
273
+
274
+ analyze_btn.click(
275
+ fn=analyze_ui,
276
+ inputs=news_text,
277
+ outputs=[result_label_html, conf_text, conf_bar],
278
+ )
279
+ clear_btn.click(fn=lambda: "", inputs=None, outputs=[news_text])
280
+
281
+ # ------------- How it works page -------------
282
+ with gr.Page(name="how-it-works"):
283
+ navbar("How")
284
+ with gr.Group(elem_classes=["glass-card"]):
285
+ gr.Markdown(
286
+ "## How CMD‑BERT works\n"
287
+ "CMD‑BERT is an AI‑augmented linguistic model that focuses on writing style, "
288
+ "not literal truth. It looks for patterns such as exaggerated wording, "
289
+ "over‑confident claims, and framing that often appear in misleading content."
290
+ )
291
+ with gr.Row():
292
+ with gr.Column():
293
+ with gr.Group(elem_classes=["glass-card"]):
294
+ gr.Markdown(
295
+ "### 1. Input and preprocessing\n"
296
+ "- User pastes a Cebuano headline, post, or short article.\n"
297
+ "- The text is tokenized and trimmed to a safe maximum length.\n"
298
+ "- Inputs are processed in memory and not stored permanently."
299
+ )
300
+ with gr.Column():
301
+ with gr.Group(elem_classes=["glass-card"]):
302
+ gr.Markdown(
303
+ "### 2. CMD‑BERT analysis\n"
304
+ "- CMD‑BERT is a fine‑tuned BERT‑base model trained on Cebuano news.\n"
305
+ "- It computes probabilities for two classes: **Fake** and **Legit**.\n"
306
+ "- The highest‑probability class becomes the predicted label."
307
+ )
308
+ with gr.Group(elem_classes=["glass-card"]):
309
+ gr.Markdown(
310
+ "### 3. Result and interpretation\n"
311
+ "- The interface shows the predicted label and confidence bar.\n"
312
+ "- Users are reminded that this is a screening tool only.\n"
313
+ "- Final judgment should always involve human critical thinking."
314
+ )
315
+
316
+ # ------------- About page -------------
317
+ with gr.Page(name="about"):
318
+ navbar("About")
319
+ with gr.Group(elem_classes=["glass-card"]):
320
+ gr.Markdown(
321
+ "## About CMD‑BERT\n"
322
+ "**CMD‑BERT: An AI Augmented Linguistic Recognition Model for Cebuano Fake News Detection**\n\n"
323
+ "CMD‑BERT is a thesis project in the Department of Computer Engineering at "
324
+ "Cebu Technological University–Main Campus. The tool aims to support Cebuano readers "
325
+ "by highlighting potentially misleading writing patterns in online news and posts."
326
+ )
327
+ with gr.Group(elem_classes=["glass-card"]):
328
+ gr.Markdown(
329
+ "### Thesis information\n"
330
+ "_A Thesis Project presented to the Faculty of the Department of Computer Engineering_\n\n"
331
+ "Cebu Technological University–Main Campus \n"
332
+ "Cebu City, Philippines \n\n"
333
+ "_In partial fulfillment of the requirements for the degree_ \n"
334
+ "**Bachelor of Science in Computer Engineering**\n\n"
335
+ "**By:** \n"
336
+ "- Cabag, Ronilo Jose Jr. S. \n"
337
+ "- Libron, Andio Mart \n"
338
+ "- Omega, Noel \n\n"
339
+ "**Adviser:** Engr. Jueco, M.Eng. \n"
340
+ "January 2026"
341
+ )
342
+
343
+ # ------------- Feedback page -------------
344
+ with gr.Page(name="feedback"):
345
+ navbar("Feedback")
346
+ with gr.Group(elem_classes=["glass-card"]):
347
+ gr.Markdown(
348
+ "## Feedback and model improvement\n"
349
+ "CMD‑BERT is experimental and continuously improving. Your feedback can help "
350
+ "identify model mistakes, usability issues, and opportunities to refine the dataset."
351
+ )
352
+
353
+ with gr.Row():
354
+ with gr.Column():
355
+ with gr.Group(elem_classes=["glass-card"]):
356
+ fb_type = gr.Dropdown(
357
+ ["Bug / technical issue", "Model mistake", "UI suggestion", "Other"],
358
+ label="Feedback type"
359
+ )
360
+ fb_text = gr.Textbox(
361
+ lines=6,
362
+ label="Your message or example text",
363
+ placeholder="Describe the issue or paste an example of text the model misclassified."
364
+ )
365
+ fb_email = gr.Textbox(
366
+ label="Email (optional, for follow‑up)",
367
+ placeholder="you@example.com"
368
+ )
369
+ fb_checkbox = gr.Checkbox(
370
+ label="Allow us to use this text anonymously for future model improvements.",
371
+ value=True
372
+ )
373
+ fb_submit = gr.Button("Submit feedback", elem_classes=["btn-primary-custom"])
374
+
375
+ with gr.Column():
376
+ with gr.Group(elem_classes=["glass-card"]):
377
+ fb_status = gr.Markdown(
378
+ "No feedback submitted yet.",
379
+ label=""
380
+ )
381
+ gr.Markdown(
382
+ "### FAQ\n"
383
+ "**What happens to my feedback?** \n"
384
+ "It is stored securely and reviewed by the CMD‑BERT thesis team.\n\n"
385
+ "**Will CMD‑BERT replace human fact‑checkers?** \n"
386
+ "No. It is a support tool to encourage critical reading.\n\n"
387
+ "**Who maintains this tool?** \n"
388
+ "The CMD‑BERT thesis team at Cebu Technological University–Main Campus."
389
+ )
390
+
391
+ def save_feedback(ftype, text, email, consent):
392
+ if not text.strip():
393
+ return "Please enter a message before submitting."
394
+ # For now just acknowledge; you can extend this to write to a file or database.
395
+ return "Thank you for your feedback! It has been recorded."
396
+
397
+ fb_submit.click(
398
+ fn=save_feedback,
399
+ inputs=[fb_type, fb_text, fb_email, fb_checkbox],
400
+ outputs=fb_status,
401
+ )
402
+
403
 
404
  if __name__ == "__main__":
405
+ demo.launch(css=custom_css, theme=gr.themes.Soft())