vanhai123 commited on
Commit
0016b8a
·
verified ·
1 Parent(s): 4465400

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +116 -305
app.py CHANGED
@@ -1,6 +1,7 @@
1
  import gradio as gr
2
  from transformers import pipeline
3
  import numpy as np
 
4
  import plotly.graph_objects as go
5
 
6
  # Load mô hình từ Hugging Face Model Hub
@@ -8,10 +9,10 @@ pipe = pipeline("text-classification", model="vanhai123/phobert-vi-comment-4clas
8
 
9
  # Map nhãn lại cho dễ đọc với emoji và màu sắc
10
  label_map = {
11
- "LABEL_0": {"name": "Tích cực", "emoji": "😊", "color": "#10b981"},
12
- "LABEL_1": {"name": "Tiêu cực", "emoji": "😞", "color": "#f59e0b"},
13
- "LABEL_2": {"name": "Trung tính", "emoji": "😐", "color": "#6b7280"},
14
- "LABEL_3": {"name": "Độc hại", "emoji": "😡", "color": "#ef4444"}
15
  }
16
 
17
  def classify_comment(comment):
@@ -24,7 +25,7 @@ def classify_comment(comment):
24
  results = results[0] if results else {}
25
 
26
  # Lấy kết quả chi tiết cho tất cả các nhãn
27
- all_scores = pipe(comment, top_k=None)
28
  if isinstance(all_scores, list):
29
  all_scores = all_scores[0] if all_scores else []
30
 
@@ -36,21 +37,20 @@ def classify_comment(comment):
36
  label_info = label_map[main_label]
37
  main_result = f"""
38
  <div style="
39
- background: linear-gradient(135deg, {label_info['color']}15, {label_info['color']}08);
40
- border: 1px solid {label_info['color']}40;
41
- border-radius: 16px;
42
- padding: 24px;
43
  text-align: center;
44
- margin: 16px 0;
45
- backdrop-filter: blur(10px);
46
- box-shadow: 0 8px 32px rgba(0,0,0,0.08);
47
  ">
48
- <div style="font-size: 52px; margin-bottom: 12px; filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));">{label_info['emoji']}</div>
49
- <div style="font-size: 26px; font-weight: 700; color: {label_info['color']}; margin-bottom: 8px; letter-spacing: -0.025em;">
50
  {label_info['name']}
51
  </div>
52
- <div style="font-size: 16px; color: #6b7280; font-weight: 500;">
53
- Độ tin cậy: <span style="color: {label_info['color']}; font-weight: 700;">{round(main_score*100, 1)}%</span>
54
  </div>
55
  </div>
56
  """
@@ -70,53 +70,46 @@ def classify_comment(comment):
70
  scores.append(item['score'])
71
  colors.append(label_map[label_key]['color'])
72
 
73
- # Tạo biểu đồ thanh ngang với gradient
74
  fig = go.Figure(data=[
75
  go.Bar(
76
  y=labels,
77
  x=scores,
78
  orientation='h',
79
- marker=dict(
80
- color=colors,
81
- line=dict(width=0),
82
- opacity=0.8
83
- ),
84
  text=[f"{s:.1%}" for s in scores],
85
  textposition='inside',
86
- textfont=dict(color='white', size=14, family='Inter'),
87
- hovertemplate='<b>%{y}</b><br>Điểm số: %{x:.1%}<extra></extra>'
88
  )
89
  ])
90
 
91
  fig.update_layout(
92
  title={
93
- 'text': '📊 Phân phối điểm số dự đoán',
94
  'x': 0.5,
95
- 'font': {'size': 18, 'family': 'Inter', 'color': '#1f2937'}
96
  },
97
  xaxis_title="Điểm số",
98
- yaxis_title="",
99
- height=280,
100
- margin=dict(l=20, r=20, t=60, b=40),
101
- plot_bgcolor='rgba(255,255,255,0)',
102
- paper_bgcolor='rgba(255,255,255,0)',
103
- font=dict(family="Inter", size=13, color='#4b5563'),
104
  xaxis=dict(
105
  showgrid=True,
106
  gridwidth=1,
107
- gridcolor='rgba(156,163,175,0.2)',
108
- range=[0, 1],
109
- tickformat=".0%"
110
  ),
111
  yaxis=dict(
112
- showgrid=False,
113
- tickfont=dict(size=14)
114
  )
115
  )
116
 
117
  # Chi tiết điểm số
118
- details = "<div style='margin-top: 20px; padding: 0 4px;'>"
119
- details += "<h4 style='color: #1f2937; margin-bottom: 16px; font-size: 18px; font-weight: 700; display: flex; align-items: center;'><span style='margin-right: 8px;'>📈</span> Chi tiết điểm số</h4>"
120
  for item in sorted(all_scores, key=lambda x: x['score'], reverse=True):
121
  label_key = item['label']
122
  if label_key in label_map:
@@ -124,13 +117,13 @@ def classify_comment(comment):
124
  percentage = item['score'] * 100
125
  bar_width = int(item['score'] * 100)
126
  details += f"""
127
- <div style="margin-bottom: 16px; padding: 16px; background: #f9fafb; border-radius: 12px; border: 1px solid #e5e7eb;">
128
- <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
129
- <span style="font-weight: 600; color: #374151; font-size: 15px;">{info['emoji']} {info['name']}</span>
130
- <span style="font-weight: 700; color: {info['color']}; font-size: 16px;">{percentage:.1f}%</span>
131
  </div>
132
- <div style="background: #e5e7eb; border-radius: 8px; height: 10px; overflow: hidden;">
133
- <div style="background: linear-gradient(90deg, {info['color']}, {info['color']}cc); height: 100%; width: {bar_width}%; border-radius: 8px; transition: all 0.3s ease;"></div>
134
  </div>
135
  </div>
136
  """
@@ -140,309 +133,128 @@ def classify_comment(comment):
140
 
141
  return main_result, None, None
142
 
143
- # Custom CSS cho giao diện tối ưu cả light và dark mode
144
  custom_css = """
145
  /* Import Google Fonts */
146
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap');
147
 
148
  /* Global styles */
149
  * {
150
  font-family: 'Inter', sans-serif !important;
151
  }
152
 
153
- /* Main container */
154
  .gradio-container {
155
- background: var(--background, #f9fafb) !important;
156
  min-height: 100vh;
157
  }
158
 
159
- @media (prefers-color-scheme: dark) {
160
- .gradio-container {
161
- --background: linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #334155 100%) !important;
162
- --text-color: #e5e7eb !important;
163
- --secondary-text: #9ca3af !important;
164
- --border-color: #4b5563 !important;
165
- --card-bg: #1f2937 !important;
166
- --card-border: #374151 !important;
167
- }
168
- }
169
-
170
- @media (prefers-color-scheme: light) {
171
- .gradio-container {
172
- --background: #f9fafb !important;
173
- --text-color: #1f2937 !important;
174
- --secondary-text: #6b7280 !important;
175
- --border-color: #e5e7eb !important;
176
- --card-bg: #ffffff !important;
177
- --card-border: #e5e7eb !important;
178
- }
179
- }
180
-
181
  #main_container {
182
- background: var(--card-bg) !important;
183
- backdrop-filter: blur(20px) !important;
184
- border-radius: 24px !important;
185
- box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25) !important;
186
- border: 1px solid var(--border-color) !important;
187
- margin: 20px !important;
188
- overflow: hidden !important;
189
  }
190
 
191
- /* Header với gradient hiện đại */
192
  .app-title {
193
- background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #a855f7 100%) !important;
194
- color: white !important;
195
- padding: 40px 30px !important;
196
- text-align: center !important;
197
- margin: -20px -20px 40px -20px !important;
198
- position: relative !important;
199
- }
200
-
201
- .app-title::before {
202
- content: '';
203
- position: absolute;
204
- top: 0;
205
- left: 0;
206
- right: 0;
207
- bottom: 0;
208
- background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grain" width="100" height="100" patternUnits="userSpaceOnUse"><circle cx="25" cy="25" r="1" fill="white" opacity="0.1"/><circle cx="75" cy="75" r="1" fill="white" opacity="0.1"/><circle cx="50" cy="10" r="0.5" fill="white" opacity="0.1"/></pattern></defs><rect width="100" height="100" fill="url(%23grain)"/></svg>') !important;
209
- opacity: 0.1 !important;
210
  }
211
 
212
  .app-title h1 {
213
- font-size: 2.75rem !important;
214
- font-weight: 800 !important;
215
- margin-bottom: 12px !important;
216
- text-shadow: 0 4px 12px rgba(0,0,0,0.3) !important;
217
- letter-spacing: -0.025em !important;
218
- position: relative !important;
219
- z-index: 1 !important;
220
  }
221
 
222
  .app-title p {
223
- font-size: 1.125rem !important;
224
- opacity: 0.9 !important;
225
- font-weight: 400 !important;
226
- position: relative !important;
227
- z-index: 1 !important;
228
  }
229
 
230
- /* Input styling hiện đại */
231
  .input-container textarea {
232
- border: 2px solid var(--border-color) !important;
233
- border-radius: 16px !important;
234
- padding: 20px !important;
235
  font-size: 16px !important;
236
- line-height: 1.6 !important;
237
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
238
  resize: vertical !important;
239
- background: var(--card-bg) !important;
240
- font-weight: 400 !important;
241
- color: var(--text-color) !important;
242
  }
243
 
244
  .input-container textarea:focus {
245
- border-color: #6366f1 !important;
246
- box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1) !important;
247
  outline: none !important;
248
- background: var(--card-bg) !important;
249
- transform: translateY(-1px) !important;
250
  }
251
 
252
- /* Button styling với gradient và animation */
253
  button.primary {
254
- background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%) !important;
255
  border: none !important;
256
- border-radius: 14px !important;
257
- padding: 16px 32px !important;
258
- font-weight: 700 !important;
259
- font-size: 15px !important;
260
- letter-spacing: 0.025em !important;
261
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
262
- box-shadow: 0 8px 25px rgba(99, 102, 241, 0.35) !important;
263
- position: relative !important;
264
- overflow: hidden !important;
265
- color: white !important;
266
- }
267
-
268
- button.primary::before {
269
- content: '';
270
- position: absolute;
271
- top: 0;
272
- left: -100%;
273
- width: 100%;
274
- height: 100%;
275
- background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
276
- transition: left 0.5s ease;
277
  }
278
 
279
  button.primary:hover {
280
- transform: translateY(-3px) !important;
281
- box-shadow: 0 12px 35px rgba(99, 102, 241, 0.4) !important;
282
- }
283
-
284
- button.primary:hover::before {
285
- left: 100%;
286
- }
287
-
288
- /* Section headers */
289
- h3 {
290
- color: var(--text-color) !important;
291
- font-weight: 700 !important;
292
- font-size: 18px !important;
293
- margin-bottom: 16px !important;
294
- display: flex !important;
295
- align-items: center !important;
296
  }
297
 
298
  /* Examples styling */
299
  .examples-container {
300
- background: var(--card-bg) !important;
301
- border: 1px solid var(--border-color) !important;
302
- border-radius: 16px !important;
303
- padding: 24px !important;
304
- margin-top: 24px !important;
305
  }
306
 
307
  .examples-container h3 {
308
- color: var(--text-color) !important;
309
- margin-bottom: 12px !important;
310
- font-weight: 700 !important;
311
- font-size: 16px !important;
312
- }
313
-
314
- .examples-container p {
315
- color: var(--secondary-text) !important;
316
- font-size: 14px !important;
317
- margin-bottom: 16px !important;
318
- }
319
-
320
- /* Examples buttons */
321
- .examples-container button {
322
- background: var(--card-bg) !important;
323
- border: 1px solid var(--border-color) !important;
324
- border-radius: 10px !important;
325
- padding: 12px 16px !important;
326
- margin: 4px !important;
327
- font-size: 13px !important;
328
- color: var(--text-color) !important;
329
- transition: all 0.2s ease !important;
330
- font-weight: 500 !important;
331
  }
332
 
333
- .examples-container button:hover {
334
- border-color: #6366f1 !important;
335
- color: #6366f1 !important;
336
- background: var(--card-bg) !important;
337
- transform: translateY(-1px) !important;
338
- box-shadow: 0 4px 12px rgba(99, 102, 241, 0.15) !important;
339
- }
340
-
341
- /* Tabs styling */
342
- .tab-nav {
343
- background: var(--card-bg) !important;
344
- border-radius: 12px !important;
345
- padding: 4px !important;
346
- margin-bottom: 20px !important;
347
  }
348
 
 
349
  .tab-nav button {
350
- border-radius: 8px !important;
351
- font-weight: 600 !important;
352
- font-size: 14px !important;
353
- padding: 12px 20px !important;
354
- transition: all 0.2s ease !important;
355
- border: none !important;
356
- background: transparent !important;
357
- color: var(--secondary-text) !important;
358
  }
359
 
360
  .tab-nav button.selected {
361
- background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%) !important;
362
  color: white !important;
363
- box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3) !important;
364
- }
365
-
366
- /* Output containers */
367
- .output-container {
368
- background: var(--card-bg) !important;
369
- border-radius: 16px !important;
370
- border: 1px solid var(--border-color) !important;
371
- margin-top: 20px !important;
372
- padding: 20px !important;
373
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1) !important;
374
  }
375
 
376
  /* Footer */
377
  .footer {
378
- text-align: center !important;
379
- padding: 32px 20px !important;
380
- color: var(--secondary-text) !important;
381
- font-size: 14px !important;
382
- background: var(--card-bg) !important;
383
- border-top: 1px solid var(--border-color) !important;
384
- margin: 40px -20px -20px -20px !important;
385
- line-height: 1.6 !important;
386
- }
387
-
388
- .footer strong {
389
- color: var(--text-color) !important;
390
- font-weight: 600 !important;
391
- }
392
-
393
- /* Responsive */
394
- @media (max-width: 768px) {
395
- .app-title h1 {
396
- font-size: 2rem !important;
397
- }
398
-
399
- .app-title p {
400
- font-size: 1rem !important;
401
- }
402
-
403
- #main_container {
404
- margin: 10px !important;
405
- border-radius: 20px !important;
406
- }
407
-
408
- .input-container textarea {
409
- padding: 16px !important;
410
- font-size: 15px !important;
411
- }
412
-
413
- button.primary {
414
- padding: 14px 24px !important;
415
- font-size: 14px !important;
416
- }
417
- }
418
-
419
- /* Loading animation */
420
- @keyframes pulse {
421
- 0%, 100% { opacity: 1; }
422
- 50% { opacity: 0.5; }
423
- }
424
-
425
- .loading {
426
- animation: pulse 2s infinite;
427
- }
428
-
429
- /* Scrollbar styling */
430
- ::-webkit-scrollbar {
431
- width: 8px;
432
- }
433
-
434
- ::-webkit-scrollbar-track {
435
- background: var(--card-bg);
436
- border-radius: 4px;
437
- }
438
-
439
- ::-webkit-scrollbar-thumb {
440
- background: linear-gradient(135deg, #6366f1, #8b5cf6);
441
- border-radius: 4px;
442
- }
443
-
444
- ::-webkit-scrollbar-thumb:hover {
445
- background: linear-gradient(135deg, #4f46e5, #7c3aed);
446
  }
447
  """
448
 
@@ -451,25 +263,25 @@ with gr.Blocks(css=custom_css, title="Phân loại bình luận tiếng Việt -
451
  # Header
452
  gr.HTML("""
453
  <div class="app-title">
454
- <h1>🤖 Phân loại bình luận tiếng Việt</h1>
455
- <p>Phân tích cảm xúc và độc hại trong bình luận với công nghệ PhoBERT</p>
456
  </div>
457
  """)
458
 
459
  with gr.Row():
460
  with gr.Column(scale=1):
461
  # Input section
462
- gr.HTML("<h3>✍️ Nhập bình luận để phân tích</h3>")
463
 
464
  input_text = gr.Textbox(
465
  lines=4,
466
- placeholder="Nhập bình luận tiếng Việt tại đây...\n\nVí dụ: 'Sản phẩm này thật tuyệt vời, tôi rất hài lòng!'",
467
  label="",
468
  elem_classes=["input-container"]
469
  )
470
 
471
  submit_btn = gr.Button(
472
- "🔍 Phân tích ngay",
473
  variant="primary",
474
  size="lg"
475
  )
@@ -477,8 +289,8 @@ with gr.Blocks(css=custom_css, title="Phân loại bình luận tiếng Việt -
477
  # Examples section
478
  gr.HTML("""
479
  <div class="examples-container">
480
- <h3>💡 Mẫu bình luận để thử nghiệm</h3>
481
- <p>Chọn một ví dụ bên dưới để xem cách hoạt động:</p>
482
  </div>
483
  """)
484
 
@@ -498,18 +310,18 @@ with gr.Blocks(css=custom_css, title="Phân loại bình luận tiếng Việt -
498
 
499
  with gr.Column(scale=1):
500
  # Output section
501
- gr.HTML("<h3>📊 Kết quả phân tích</h3>")
502
 
503
  with gr.Tabs():
504
  with gr.TabItem("🎯 Kết quả chính", elem_id="main_result_tab"):
505
  result_output = gr.HTML(
506
- value="<div style='text-align: center; padding: 60px 20px; color: var(--secondary-text); background: var(--card-bg); border-radius: 16px; border: 2px dashed var(--border-color);'><div style='font-size: 48px; margin-bottom: 16px;'>🤔</div><div style='font-size: 18px; font-weight: 500;'>Nhập bình luận và nhấn 'Phân tích ngay' để xem kết quả</div></div>"
507
  )
508
 
509
- with gr.TabItem("📈 Biểu đồ", elem_id="chart_tab"):
510
  chart_output = gr.Plot()
511
 
512
- with gr.TabItem("📋 Chi tiết", elem_id="details_tab"):
513
  details_output = gr.HTML()
514
 
515
  # Event handlers
@@ -529,10 +341,9 @@ with gr.Blocks(css=custom_css, title="Phân loại bình luận tiếng Việt -
529
  gr.HTML("""
530
  <div class="footer">
531
  <p>
532
- <strong>🧠 Mô hình:</strong> PhoBERT fine-tuned cho phân loại bình luận tiếng Việt<br>
533
- <strong>🏷️ Các nhãn:</strong> Tích cực • Tiêu cực • Trung tính • Độc hại<br>
534
- <strong>⚡ Công nghệ:</strong> Transformers Gradio • Plotly<br>
535
- <em>Được phát triển bởi Ha Van Hai</em>
536
  </p>
537
  </div>
538
  """)
 
1
  import gradio as gr
2
  from transformers import pipeline
3
  import numpy as np
4
+ import plotly.express as px
5
  import plotly.graph_objects as go
6
 
7
  # Load mô hình từ Hugging Face Model Hub
 
9
 
10
  # Map nhãn lại cho dễ đọc với emoji và màu sắc
11
  label_map = {
12
+ "LABEL_0": {"name": "Tích cực", "emoji": "😊", "color": "#22c55e"},
13
+ "LABEL_1": {"name": "Tiêu cực", "emoji": "😞", "color": "#ef4444"},
14
+ "LABEL_2": {"name": "Trung tính", "emoji": "😐", "color": "#64748b"},
15
+ "LABEL_3": {"name": "Độc hại", "emoji": "😡", "color": "#dc2626"}
16
  }
17
 
18
  def classify_comment(comment):
 
25
  results = results[0] if results else {}
26
 
27
  # Lấy kết quả chi tiết cho tất cả các nhãn
28
+ all_scores = pipe(comment, return_all_scores=True)
29
  if isinstance(all_scores, list):
30
  all_scores = all_scores[0] if all_scores else []
31
 
 
37
  label_info = label_map[main_label]
38
  main_result = f"""
39
  <div style="
40
+ background: linear-gradient(135deg, {label_info['color']}22, {label_info['color']}11);
41
+ border: 2px solid {label_info['color']};
42
+ border-radius: 15px;
43
+ padding: 20px;
44
  text-align: center;
45
+ margin: 10px 0;
46
+ box-shadow: 0 4px 15px rgba(0,0,0,0.1);
 
47
  ">
48
+ <div style="font-size: 48px; margin-bottom: 10px;">{label_info['emoji']}</div>
49
+ <div style="font-size: 24px; font-weight: bold; color: {label_info['color']}; margin-bottom: 5px;">
50
  {label_info['name']}
51
  </div>
52
+ <div style="font-size: 18px; color: #666;">
53
+ Độ tin cậy: <strong>{round(main_score*100, 1)}%</strong>
54
  </div>
55
  </div>
56
  """
 
70
  scores.append(item['score'])
71
  colors.append(label_map[label_key]['color'])
72
 
73
+ # Tạo biểu đồ thanh ngang
74
  fig = go.Figure(data=[
75
  go.Bar(
76
  y=labels,
77
  x=scores,
78
  orientation='h',
79
+ marker_color=colors,
 
 
 
 
80
  text=[f"{s:.1%}" for s in scores],
81
  textposition='inside',
82
+ textfont=dict(color='white', size=12, family='Arial Black')
 
83
  )
84
  ])
85
 
86
  fig.update_layout(
87
  title={
88
+ 'text': 'Phân phối điểm số dự đoán',
89
  'x': 0.5,
90
+ 'font': {'size': 16, 'family': 'Arial', 'color': '#333'}
91
  },
92
  xaxis_title="Điểm số",
93
+ yaxis_title="Loại bình luận",
94
+ height=300,
95
+ margin=dict(l=20, r=20, t=50, b=20),
96
+ plot_bgcolor='rgba(0,0,0,0)',
97
+ paper_bgcolor='rgba(0,0,0,0)',
98
+ font=dict(family="Arial", size=12),
99
  xaxis=dict(
100
  showgrid=True,
101
  gridwidth=1,
102
+ gridcolor='rgba(128,128,128,0.2)',
103
+ range=[0, 1]
 
104
  ),
105
  yaxis=dict(
106
+ showgrid=False
 
107
  )
108
  )
109
 
110
  # Chi tiết điểm số
111
+ details = "<div style='margin-top: 15px;'>"
112
+ details += "<h4 style='color: #333; margin-bottom: 10px;'>📊 Chi tiết điểm số:</h4>"
113
  for item in sorted(all_scores, key=lambda x: x['score'], reverse=True):
114
  label_key = item['label']
115
  if label_key in label_map:
 
117
  percentage = item['score'] * 100
118
  bar_width = int(item['score'] * 100)
119
  details += f"""
120
+ <div style="margin-bottom: 8px;">
121
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 3px;">
122
+ <span style="font-weight: 500;">{info['emoji']} {info['name']}</span>
123
+ <span style="font-weight: bold; color: {info['color']};">{percentage:.1f}%</span>
124
  </div>
125
+ <div style="background: #f0f0f0; border-radius: 10px; height: 8px; overflow: hidden;">
126
+ <div style="background: {info['color']}; height: 100%; width: {bar_width}%; border-radius: 10px;"></div>
127
  </div>
128
  </div>
129
  """
 
133
 
134
  return main_result, None, None
135
 
136
+ # Custom CSS cho giao diện
137
  custom_css = """
138
  /* Import Google Fonts */
139
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
140
 
141
  /* Global styles */
142
  * {
143
  font-family: 'Inter', sans-serif !important;
144
  }
145
 
146
+ /* Header styling */
147
  .gradio-container {
148
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
149
  min-height: 100vh;
150
  }
151
 
152
+ /* Main container */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  #main_container {
154
+ background: white;
155
+ border-radius: 20px;
156
+ box-shadow: 0 20px 40px rgba(0,0,0,0.1);
157
+ margin: 20px;
158
+ overflow: hidden;
 
 
159
  }
160
 
161
+ /* Title area */
162
  .app-title {
163
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
164
+ color: white;
165
+ padding: 30px;
166
+ text-align: center;
167
+ margin: -20px -20px 30px -20px;
 
 
 
 
 
 
 
 
 
 
 
 
168
  }
169
 
170
  .app-title h1 {
171
+ font-size: 2.5rem;
172
+ font-weight: 700;
173
+ margin-bottom: 10px;
174
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
 
 
 
175
  }
176
 
177
  .app-title p {
178
+ font-size: 1.1rem;
179
+ opacity: 0.9;
180
+ font-weight: 300;
 
 
181
  }
182
 
183
+ /* Input styling */
184
  .input-container textarea {
185
+ border: 2px solid #e2e8f0 !important;
186
+ border-radius: 12px !important;
187
+ padding: 15px !important;
188
  font-size: 16px !important;
189
+ transition: all 0.3s ease !important;
 
190
  resize: vertical !important;
 
 
 
191
  }
192
 
193
  .input-container textarea:focus {
194
+ border-color: #667eea !important;
195
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1) !important;
196
  outline: none !important;
 
 
197
  }
198
 
199
+ /* Button styling */
200
  button.primary {
201
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
202
  border: none !important;
203
+ border-radius: 12px !important;
204
+ padding: 12px 30px !important;
205
+ font-weight: 600 !important;
206
+ text-transform: uppercase !important;
207
+ letter-spacing: 0.5px !important;
208
+ transition: all 0.3s ease !important;
209
+ box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3) !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  }
211
 
212
  button.primary:hover {
213
+ transform: translateY(-2px) !important;
214
+ box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4) !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  }
216
 
217
  /* Examples styling */
218
  .examples-container {
219
+ background: #f8fafc;
220
+ border-radius: 12px;
221
+ padding: 20px;
222
+ margin-top: 20px;
 
223
  }
224
 
225
  .examples-container h3 {
226
+ color: #334155;
227
+ margin-bottom: 15px;
228
+ font-weight: 600;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  }
230
 
231
+ /* Output styling */
232
+ .output-container {
233
+ background: #ffffff;
234
+ border-radius: 12px;
235
+ border: 1px solid #e2e8f0;
236
+ margin-top: 20px;
 
 
 
 
 
 
 
 
237
  }
238
 
239
+ /* Tab styling */
240
  .tab-nav button {
241
+ border-radius: 8px 8px 0 0 !important;
242
+ font-weight: 500 !important;
 
 
 
 
 
 
243
  }
244
 
245
  .tab-nav button.selected {
246
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
247
  color: white !important;
 
 
 
 
 
 
 
 
 
 
 
248
  }
249
 
250
  /* Footer */
251
  .footer {
252
+ text-align: center;
253
+ padding: 20px;
254
+ color: #64748b;
255
+ font-size: 14px;
256
+ border-top: 1px solid #e2e8f0;
257
+ margin-top: 30px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
  }
259
  """
260
 
 
263
  # Header
264
  gr.HTML("""
265
  <div class="app-title">
266
+ <h1>🧠 Phân loại bình luận tiếng Việt</h1>
267
+ <p>Sử dụng mô hình PhoBERT để phân tích cảm xúc và độc hại trong bình luận mạng hội</p>
268
  </div>
269
  """)
270
 
271
  with gr.Row():
272
  with gr.Column(scale=1):
273
  # Input section
274
+ gr.HTML("<h3 style='color: #334155; font-weight: 600; margin-bottom: 15px;'>📝 Nhập bình luận cần phân tích</h3>")
275
 
276
  input_text = gr.Textbox(
277
  lines=4,
278
+ placeholder="Nhập bình luận tiếng Việt để phân tích cảm xúc và độ độc hại...\n\nVí dụ: 'Sản phẩm này thật tuyệt vời, tôi rất hài lòng!'",
279
  label="",
280
  elem_classes=["input-container"]
281
  )
282
 
283
  submit_btn = gr.Button(
284
+ "🔍 Phân tích bình luận",
285
  variant="primary",
286
  size="lg"
287
  )
 
289
  # Examples section
290
  gr.HTML("""
291
  <div class="examples-container">
292
+ <h3>💡 dụ mẫu:</h3>
293
+ <p style="color: #64748b; margin-bottom: 10px;">Nhấp vào các ví dụ bên dưới để thử nghiệm:</p>
294
  </div>
295
  """)
296
 
 
310
 
311
  with gr.Column(scale=1):
312
  # Output section
313
+ gr.HTML("<h3 style='color: #334155; font-weight: 600; margin-bottom: 15px;'>📊 Kết quả phân tích</h3>")
314
 
315
  with gr.Tabs():
316
  with gr.TabItem("🎯 Kết quả chính", elem_id="main_result_tab"):
317
  result_output = gr.HTML(
318
+ value="<div style='text-align: center; padding: 40px; color: #64748b;'>Nhập bình luận và nhấn 'Phân tích' để xem kết quả</div>"
319
  )
320
 
321
+ with gr.TabItem("📈 Biểu đồ phân phối", elem_id="chart_tab"):
322
  chart_output = gr.Plot()
323
 
324
+ with gr.TabItem("📋 Chi tiết điểm số", elem_id="details_tab"):
325
  details_output = gr.HTML()
326
 
327
  # Event handlers
 
341
  gr.HTML("""
342
  <div class="footer">
343
  <p>
344
+ <strong>Mô hình:</strong> PhoBERT fine-tuned cho phân loại bình luận tiếng Việt<br>
345
+ <strong>Các nhãn:</strong> Tích cực • Tiêu cực • Trung tính • Độc hại<br>
346
+ <em>Được xây dựng bởi Ha Van Hai</em>
 
347
  </p>
348
  </div>
349
  """)