Nattapong Tapachoom
Enhance CSS styles for improved visual consistency and responsiveness in Gradio app
cadbbaa
raw
history blame
17.5 kB
import gradio as gr
from transformers import pipeline
import re
from functools import lru_cache
import logging
from typing import List, Dict, Tuple
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# รายชื่อโมเดลที่ให้เลือก
MODEL_LIST = [
"SandboxBhh/sentiment-thai-text-model",
"poom-sci/WangchanBERTa-finetuned-sentiment",
"Thaweewat/wangchanberta-hyperopt-sentiment-01",
"cardiffnlp/twitter-xlm-roberta-base-sentiment",
"phoner45/wangchan-sentiment-thai-text-model",
"ZombitX64/Sentiment-01",
"ZombitX64/Sentiment-02",
"ZombitX64/Sentiment-03",
"ZombitX64/MultiSent-E5-Pro",
"ZombitX64/MultiSent-E5",
"ZombitX64/Thai-sentiment-e5",
"ZombitX64/sentiment-103",
"nlptown/bert-base-multilingual-uncased-sentiment"
]
# ใช้ cache เพื่อไม่ต้องโหลดโมเดลซ้ำ
@lru_cache(maxsize=3)
def get_nlp(model_name: str):
try:
return pipeline("sentiment-analysis", model=model_name)
except Exception as e:
logger.error(f"Error loading model {model_name}: {e}")
raise gr.Error(f"ไม่สามารถโหลดโมเดล {model_name} ได้: {str(e)}")
# Enhanced label mapping with more comprehensive support
LABEL_MAPPINGS = {
# Standard labels
"LABEL_0": {"code": 0, "name": "question", "emoji": "❓", "color": "#2196F3", "bg": "#E3F2FD"},
"LABEL_1": {"code": 1, "name": "negative", "emoji": "😔", "color": "#F44336", "bg": "#FFEBEE"},
"LABEL_2": {"code": 2, "name": "neutral", "emoji": "😐", "color": "#FF9800", "bg": "#FFF3E0"},
"LABEL_3": {"code": 3, "name": "positive", "emoji": "😊", "color": "#4CAF50", "bg": "#E8F5E8"},
# Alternative label formats
"POSITIVE": {"code": 3, "name": "positive", "emoji": "😊", "color": "#4CAF50", "bg": "#E8F5E8"},
"NEGATIVE": {"code": 1, "name": "negative", "emoji": "😔", "color": "#F44336", "bg": "#FFEBEE"},
"NEUTRAL": {"code": 2, "name": "neutral", "emoji": "😐", "color": "#FF9800", "bg": "#FFF3E0"},
# Numerical labels
"0": {"code": 0, "name": "negative", "emoji": "😔", "color": "#F44336", "bg": "#FFEBEE"},
"1": {"code": 1, "name": "positive", "emoji": "😊", "color": "#4CAF50", "bg": "#E8F5E8"},
}
def get_label_info(label: str) -> Dict:
"""Get label information with fallback for unknown labels"""
return LABEL_MAPPINGS.get(label, {
"code": -1,
"name": label.lower(),
"emoji": "🔍",
"color": "#666666",
"bg": "#F5F5F5"
})
def split_sentences(text: str) -> List[str]:
"""Enhanced sentence splitting with better Thai support"""
# Split by various sentence endings and normalize
sentences = re.split(r'[.!?。\n]+', text)
# Clean and filter empty sentences
sentences = [s.strip() for s in sentences if s.strip() and len(s.strip()) > 2]
return sentences
def create_progress_bar(score: float, width: int = 20) -> str:
"""Create a visual progress bar"""
filled = int(score * width)
return "█" * filled + "░" * (width - filled)
def analyze_text(text: str, model_name: str) -> str:
"""Enhanced text analysis with better error handling and formatting"""
if not text or not text.strip():
return "❗ กรุณาใส่ข้อความที่ต้องการวิเคราะห์"
sentences = split_sentences(text)
if not sentences:
return "❗ ไม่พบประโยคที่สามารถวิเคราะห์ได้ กรุณาใส่ข้อความที่ยาวกว่านี้"
try:
nlp = get_nlp(model_name)
except Exception as e:
return f"❌ เกิดข้อผิดพลาดในการโหลดโมเดล: {str(e)}"
results = []
results.append("📊 **ผลการวิเคราะห์ความรู้สึก**\n" + "="*60 + "\n")
results.append(f"🤖 **โมเดล:** {model_name}\n")
sentiment_counts = {"positive": 0, "negative": 0, "neutral": 0, "question": 0, "other": 0}
total_confidence = 0
for i, sentence in enumerate(sentences, 1):
try:
result = nlp(sentence)[0]
label = result['label']
score = result['score']
label_info = get_label_info(label)
label_name = label_info["name"]
# Count sentiments
if label_name in sentiment_counts:
sentiment_counts[label_name] += 1
else:
sentiment_counts["other"] += 1
total_confidence += score
# Create formatted result
progress_bar = create_progress_bar(score)
confidence_percent = score * 100
result_text = f"""
🔸 **ประโยคที่ {i}:** "{sentence[:100]}{'...' if len(sentence) > 100 else ''}"
{label_info['emoji']} **ผลวิเคราะห์:** {label_name.upper()} (รหัส: {label_info['code']})
📈 **ความมั่นใจ:** {score:.3f} ({confidence_percent:.1f}%)
{progress_bar} {score:.3f}
{'─' * 70}
"""
results.append(result_text)
except Exception as e:
logger.error(f"Error analyzing sentence {i}: {e}")
results.append(f"\n❌ เกิดข้อผิดพลาดในการวิเคราะห์ประโยคที่ {i}: {str(e)}\n{'─' * 70}\n")
# Enhanced summary
total_sentences = len(sentences)
avg_confidence = total_confidence / total_sentences if total_sentences > 0 else 0
summary = f"""
📋 **สรุปผลการวิเคราะห์**
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 จำนวนประโยคทั้งหมด: {total_sentences} ประโยค
📈 ความมั่นใจเฉลี่ย: {avg_confidence:.3f} ({avg_confidence*100:.1f}%)
🎯 **การกระจายของความรู้สึก:**
"""
for sentiment, count in sentiment_counts.items():
if count > 0:
percentage = (count / total_sentences) * 100
emoji = {"positive": "😊", "negative": "😔", "neutral": "😐", "question": "❓", "other": "🔍"}
summary += f" {emoji.get(sentiment, '🔍')} {sentiment.title()}: {count} ประโยค ({percentage:.1f}%)\n"
results.append(summary)
return "\n".join(results)
# Modern minimal CSS
CUSTOM_CSS = """
body, .gradio-container {
background: #f7faff !important;
}
.gradio-container {
max-width: 1100px !important;
margin: 32px auto 32px auto !important;
border-radius: 22px !important;
box-shadow: 0 8px 32px 0 #bdbdbd55;
border: 1.5px solid #e3e3e3;
min-height: 96vh;
padding-bottom: 32px;
}
.main-card {
background: #fff !important;
border-radius: 18px;
box-shadow: 0 2px 12px 0 #bdbdbd55;
padding: 28px 24px 20px 24px;
margin: 18px 0;
border: 1.5px solid #d1d1d1;
color: #222 !important;
transition: box-shadow 0.2s;
}
.main-card:hover {
box-shadow: 0 6px 24px 0 #bdbdbd77;
}
.output-markdown {
font-family: 'Segoe UI', 'Noto Sans Thai', sans-serif !important;
line-height: 1.7;
font-size: 1.08em;
color: #222 !important;
}
.gr-button {
font-size: 1.13em;
padding: 0.9em 2.7em;
border-radius: 13px;
font-weight: 600;
transition: all 0.2s;
background: linear-gradient(90deg, #4a63e7 0%, #764ba2 100%);
color: #fff !important;
border: none;
box-shadow: 0 2px 8px #bdbdbd33;
}
.gr-button.secondary, .gr-button[variant="secondary"] {
background: #eaeaea !important;
color: #333 !important;
border: 1px solid #cccccc;
}
.gr-button:hover {
transform: translateY(-2px) scale(1.03);
box-shadow: 0 6px 18px #bdbdbd55;
filter: brightness(1.04);
}
.gr-textbox textarea {
font-size: 1.13em;
min-height: 150px;
font-family: 'Segoe UI', 'Noto Sans Thai', sans-serif;
background: #fff;
border-radius: 10px;
border: 1.5px solid #cccccc;
color: #222;
padding: 14px;
}
.gr-dropdown input {
font-size: 1.1em;
border-radius: 8px;
background: #fff;
border: 1.5px solid #cccccc;
color: #222;
}
.gr-markdown h1, .gr-markdown h3 {
color: #2a2a4d !important;
}
.gr-markdown, .gr-markdown ul, .gr-markdown p, .gr-markdown li {
color: #222 !important;
}
.gr-markdown ul {
margin-left: 1.2em;
}
@media (max-width: 900px) {
.gradio-container { max-width: 99vw !important; padding: 0 2vw; }
.main-card { padding: 16px 6px; }
}
"""
# Enhanced Gradio interface
with gr.Blocks(
theme=gr.themes.Soft(primary_hue="blue", secondary_hue="purple", neutral_hue="gray"),
css=CUSTOM_CSS,
title="Thai Sentiment Analyzer - AI วิเคราะห์ความรู้สึกภาษาไทย"
) as demo:
gr.Markdown("""
<div style="text-align: center; padding: 38px 0 18px 0;">
<h1 style="font-size:2.7em; margin-bottom: 0.18em; color:#3b3b6d; letter-spacing:0.5px;">🧠 Thai Sentiment Analyzer</h1>
<div style="font-size:1.18em; color:#4a4a7d; opacity:0.92;">
AI วิเคราะห์ความรู้สึกในข้อความภาษาไทย รองรับหลายโมเดลและหลายประโยค
</div>
</div>
""")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("""
<div class='main-card'>
<h3 style='color:#3b3b6d; margin-bottom:15px; font-size:1.18em;'>🤖 เลือกโมเดลวิเคราะห์</h3>
<p style='color:#666; font-size:0.97em; margin-bottom:10px;'>เลือกโมเดล AI ที่ต้องการใช้ในการวิเคราะห์ความรู้สึก</p>
</div>
""")
model_dropdown = gr.Dropdown(
choices=MODEL_LIST,
value="ZombitX64/MultiSent-E5-Pro",
label="โมเดลที่ต้องการใช้",
info="แนะนำ: MultiSent-E5-Pro สำหรับความแม่นยำสูง"
)
gr.Markdown("""
<div class='main-card' style='margin-top:20px;'>
<h3 style='color:#3b3b6d; margin-bottom:15px; font-size:1.13em;'>📖 คำแนะนำการใช้งาน</h3>
<ul style='color:#555; font-size:1em; line-height:1.7; margin-bottom:0;'>
<li>พิมพ์ข้อความภาษาไทยที่ต้องการวิเคราะห์</li>
<li>แยกประโยคด้วยจุด (.) หรือขึ้นบรรทัดใหม่</li>
<li>รองรับการวิเคราะห์หลายประโยคพร้อมกัน</li>
<li>ผลลัพธ์จะแสดงความมั่นใจและสรุปภาพรวม</li>
</ul>
</div>
""")
with gr.Column(scale=2):
gr.Markdown("""
<div class='main-card'>
<h3 style='color:#3b3b6d; margin-bottom:15px; font-size:1.15em;'>📝 ข้อความที่ต้องการวิเคราะห์</h3>
</div>
""")
text_input = gr.Textbox(
lines=8,
placeholder="ตัวอย่าง:\nวันนี้อากาศดีมาก ฉันรู้สึกมีความสุข\nแต่การจราจรติดมาก น่าเบื่อจริงๆ\nโดยรวมแล้วก็โอเคนะ",
label="",
show_label=False
)
with gr.Row():
analyze_btn = gr.Button("🔍 วิเคราะห์ข้อความ", variant="primary", size="lg")
clear_btn = gr.Button("🗑️ ล้างข้อความ", variant="secondary")
output_box = gr.Textbox(
label="📊 ผลการวิเคราะห์ความรู้สึก",
lines=20,
show_copy_button=True,
show_label=True
)
# Enhanced examples
examples = gr.Examples(
examples=[
["วันนี้อากาศดีมาก ฉันรู้สึกมีความสุขมาก สีฟ้าสวยจริงๆ"],
["ฉันไม่ชอบอาหารนี้เลย รสชาติแปลกมาก เค็มเกินไป"],
["วันนี้เป็นยังไงบ้าง\nเรียนหนังสือกันไหม\nมีงานอะไรให้ช่วยไหม"],
["บริการดีมาก พนักงานใจดีและเป็นกันเอง\nแต่ของมีราคาแพงไปหน่อย\nโดยรวมแล้วพอใจครับ แนะนำให้เพื่อนมาลอง"],
["เมื่อไหร่จะได้เจอกันอีก คิดถึงมากเลย\nแต่ตอนนี้ต้องทำงานหนักก่อน เพื่ออนาคตที่ดี"]
],
inputs=[text_input],
label="💡 คลิกเพื่อลองใช้ตัวอย่าง"
)
# Sentiment legend with enhanced styling
gr.Markdown("""
<div class='main-card' style='margin-top:30px; background: #5b3b6d;'>
<h3 style='color:#3b3b6d; text-align:center; margin-bottom:20px;'>🎯 คำอธิบายผลการวิเคราะห์</h3>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 18px; padding: 12px 0;">
<div style="text-align: center; padding: 13px; background: rgba(76, 175, 80, 0.08); border-radius: 11px;">
<div style="font-size: 26px; margin-bottom: 7px;">😊</div>
<strong style="color: #4CAF50; font-size: 1.08em;">Positive</strong><br>
<small style="color: #666;">ความรู้สึกเชิงบวก<br>ดี, สุข, ชอบ</small>
</div>
<div style="text-align: center; padding: 13px; background: rgba(244, 67, 54, 0.08); border-radius: 11px;">
<div style="font-size: 26px; margin-bottom: 7px;">😔</div>
<strong style="color: #F44336; font-size: 1.08em;">Negative</strong><br>
<small style="color: #666;">ความรู้สึกเชิงลบ<br>เศร้า, โกรธ, ไม่ชอบ</small>
</div>
<div style="text-align: center; padding: 13px; background: rgba(255, 152, 0, 0.08); border-radius: 11px;">
<div style="font-size: 26px; margin-bottom: 7px;">😐</div>
<strong style="color: #FF9800; font-size: 1.08em;">Neutral</strong><br>
<small style="color: #666;">ความรู้สึกเป็นกลาง<br>ปกติ, พอใช้ได้</small>
</div>
<div style="text-align: center; padding: 13px; background: rgba(33, 150, 243, 0.08); border-radius: 11px;">
<div style="font-size: 26px; margin-bottom: 7px;">❓</div>
<strong style="color: #2196F3; font-size: 1.08em;">Question</strong><br>
<small style="color: #666;">ประโยคคำถาม<br>อะไร, ไหน, เมื่อไหร่</small>
</div>
</div>
</div>
""")
# Enhanced event handlers
def analyze_wrapper(text, model_name):
if not text.strip():
return "❗ กรุณาใส่ข้อความที่ต้องการวิเคราะห์"
return analyze_text(text, model_name)
def clear_text():
return ""
# Connect events
analyze_btn.click(analyze_wrapper, inputs=[text_input, model_dropdown], outputs=output_box)
text_input.submit(analyze_wrapper, inputs=[text_input, model_dropdown], outputs=output_box)
model_dropdown.change(analyze_wrapper, inputs=[text_input, model_dropdown], outputs=output_box)
clear_btn.click(clear_text, outputs=text_input)
# Launch with enhanced settings
if __name__ == "__main__":
demo.queue(max_size=20).launch(
server_name="0.0.0.0",
server_port=7860,
share=False, # Set to True if you want to create a public link
show_error=True,
favicon_path=None, # Add your favicon path here
ssl_verify=False
)