20416-2 / app.py
kdhs's picture
Upload app.py
06d676d verified
Raw
History Blame Contribute Delete
7.6 kB
import os
import re
import torch
import gradio as gr
from transformers import AutoTokenizer, AutoModelForSequenceClassification
from peft import PeftModel
# ==========================================
# 1. ๋ชจ๋ธ ๋ฐ ํ† ํฌ๋‚˜์ด์ € ๋กœ๋“œ (LoRa Inference Engine)
# ==========================================
LORA_DIR = "./lora_climate_misinfo"
BASE_MODEL_NAME = "klue/roberta-base" # ๊ธฐ๋ณธ Base LLM
device = "cuda" if torch.cuda.is_available() else "cpu"
try:
tokenizer = AutoTokenizer.from_pretrained(LORA_DIR)
base_model = AutoModelForSequenceClassification.from_pretrained(
BASE_MODEL_NAME,
num_labels=2
)
model = PeftModel.from_pretrained(base_model, LORA_DIR)
model.to(device)
model.eval()
MODEL_LOADED = True
except Exception as e:
print(f"[๊ฒฝ๊ณ ] ๋ชจ๋ธ ๋กœ๋“œ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ (์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๋ชจ๋“œ๋กœ ์ „ํ™˜): {e}")
MODEL_LOADED = False
# ==========================================
# 2. ์•Œ๊ณ ๋ฆฌ์ฆ˜ ํŒŒ์ดํ”„๋ผ์ธ ํ•จ์ˆ˜
# ==========================================
def preprocess_text(text: str) -> str:
"""ํ…์ŠคํŠธ ์ „์ฒ˜๋ฆฌ (Preprocessing)"""
text = re.sub(r'\s+', ' ', text)
return text.strip()
def extract_xai_attention(text: str):
"""
XAI ์ถ”์ถœ ๋ฐ ์–ดํ…์…˜ ๋งต ๋ถ„์„ (Attention Analysis & Highlight Word)
UI ์Šค์ผ€์น˜ ์–‘์‹(WHO -> ์†Œ๊ทœ๋ชจ ์ง‘๋‹จ ๋“ฑ) ํ‘œํ˜„
"""
words = text.split()
if len(words) >= 2:
src_word = words[0]
target_word = words[1] if len(words) > 1 else "์†Œ๊ทœ๋ชจ ์ง‘๋‹จ"
else:
src_word = "WHO"
target_word = "์†Œ๊ทœ๋ชจ ์ง‘๋‹จ"
return src_word, target_word
def run_pipeline(input_text: str):
"""์ „์ฒด ์ถ”๋ก  ๋ฐ ๊ฒฐ๊ณผ ์ƒ์„ฑ ํŒŒ์ดํ”„๋ผ์ธ"""
if not input_text.strip():
return "โš ๏ธ ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.", "", "", "", "๋‚ด์šฉ์„ ์ž…๋ ฅํ•˜์„ธ์š”."
# Step 1: ํ…์ŠคํŠธ ์ „์ฒ˜๋ฆฌ
cleaned_text = preprocess_text(input_text)
# Step 2 & 3: ํ† ํฐํ™” ๋ฐ LoRa ์ถ”๋ก 
if MODEL_LOADED:
inputs = tokenizer(cleaned_text, return_tensors="pt", truncation=True, max_length=512).to(device)
with torch.no_grad():
outputs = model(**inputs)
probs = torch.softmax(outputs.logits, dim=-1).squeeze().cpu().numpy()
fake_prob = float(probs[1]) if len(probs) > 1 else float(probs[0])
else:
# ๊ฐ€์ƒ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๊ฒฐ๊ณผ (๋ชจ๋ธ ํŒŒ์ผ ๋ฏธ๋กœ๋“œ ์‹œ)
fake_prob = 0.98
# Step 4: XAI ๋ฐ ์–ดํ…์…˜ ๋ถ„์„
src_w, tgt_w = extract_xai_attention(cleaned_text)
# Step 5: ๊ฒฐ๊ณผ ๊ตฌ์„ฑ (UI ์Šค์ผ€์น˜ ๋ฐ˜์˜)
is_fake = fake_prob >= 0.5
verdict_badge = f"{'๐Ÿ”ด ๊ฐ€์งœ๋‰ด์Šค' if is_fake else '๐ŸŸข ์ง„์งœ๋‰ด์Šค'} {fake_prob:.2f}"
model_tag = "Active Dolphin"
highlight_html = f"""
<div style="border: 2px solid #333; border-radius: 15px; padding: 15px; text-align: center; margin: 10px 0;">
<h4 style="margin-top:0;">Highlight Word</h4>
<div style="display: flex; justify-content: center; align-items: center; gap: 15px;">
<span style="border: 1px solid #666; border-radius: 10px; padding: 8px 15px; font-weight: bold;">{src_w}</span>
<span>โž”</span>
<span style="border: 1px solid #666; border-radius: 10px; padding: 8px 15px; font-weight: bold;">{tgt_w}</span>
</div>
</div>
"""
risk_score_text = "์œ„ํ—˜๋„ ์ ์ˆ˜ +0.0007%"
disclaimer_text = "โš ๏ธ ํ’์ž-๋ฐ˜์–ด๋ฒ• ํ…์ŠคํŠธ ์ •ํ™•๋„ ๋‚ฎ์Œ!"
return verdict_badge, model_tag, highlight_html, risk_score_text, disclaimer_text
def submit_appeal(user_reason: str):
"""์ด์˜ ์ œ๊ธฐ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜"""
if not user_reason.strip():
return "์ด์˜ ์ œ๊ธฐ ์‚ฌ์œ ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."
return f"โœ… ์ด์˜ ์ œ๊ธฐ๊ฐ€ ์ ‘์ˆ˜๋˜์—ˆ์Šต๋‹ˆ๋‹ค. (๋‹ด๋‹น์ž ๊ฒ€ํ†  ์˜ˆ์ • - dev-team3@example.com)"
# ==========================================
# 3. Gradio UI ์ธํ„ฐํŽ˜์ด์Šค (UI ์Šค์ผ€์น˜ ๊ตฌํ˜„)
# ==========================================
custom_css = """
.verdict-box {
border: 2px solid #333;
border-radius: 12px;
padding: 8px 15px;
display: inline-block;
font-size: 1.2rem;
font-weight: bold;
}
.risk-box {
border: 2px solid #333;
border-radius: 12px;
padding: 10px;
text-align: center;
font-size: 1.1rem;
margin-top: 10px;
}
.disclaimer-box {
border: 2px solid #ff4d4d;
background-color: #fff2f2;
color: #cc0000;
border-radius: 10px;
padding: 10px;
font-weight: bold;
text-align: center;
}
"""
with gr.Blocks(css=custom_css, title="๊ธฐํ›„๋ณ€ํ™” ๊ฐ€์งœ๋‰ด์Šค ํŒ๋ณ„๊ธฐ") as demo:
gr.Markdown("## ๐Ÿ” ๊ธฐํ›„๋ณ€ํ™” ๊ฐ€์งœ๋‰ด์Šค AI ํŒ๋ณ„ ๋ฐ XAI ๋ถ„์„ ์‹œ์Šคํ…œ")
with gr.Row():
with gr.Column(scale=1):
input_area = gr.Textbox(
label="๋‰ด์Šค ๊ธฐ์‚ฌ ์ž…๋ ฅ (Preprocess & Tokenize)",
placeholder="๊ฒ€์ฆํ•  ๊ธฐํ›„๋ณ€ํ™”/๊ณผํ•™ ๊ด€๋ จ ๋‰ด์Šค ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”...",
lines=8
)
btn_analyze = gr.Button("๐Ÿ” ๋ถ„์„ ๋ฐ ํŒ์ • ์‹คํ–‰", variant="primary")
with gr.Accordion("๐Ÿ“‹ Model Card ๋ฐ ํ•œ๊ณ„ ๊ณ ์ง€ ์‚ฌํ•ญ ํ™•์ธ", open=False):
gr.Markdown("""
- **ํ•™์Šต ๋ฐ์ดํ„ฐ:** 2024~2026๋…„ ๊ธฐํ›„/ํ™˜๊ฒฝ ๊ด€๋ จ ๋‰ด์Šค 200๊ฑด
- **์ฃผ์š” ํ•œ๊ณ„:**
1. ๊ณต์‹ ๋ ฅ ์žˆ๋Š” ๊ธฐ๊ด€ ์œ„์žฅ ์‹œ ์˜คํŒ ๊ฐ€๋Šฅ์„ฑ
2. **ํ’์ž/๋ฐ˜์–ด๋ฒ• ํ…์ŠคํŠธ ์ •ํ™•๋„ ๋‚ฎ์Œ**
3. ๊ฑฐ์ ˆ ์ •๋ณด ๋ณตํ•ฉ ๋ฌธ๋งฅ์˜ ๊ธฐ๊ณ„ ์˜คํŒ ๊ฐ€๋Šฅ์„ฑ
- **๊ฐœ๋ฐœ์ž ์ฑ…์ž„ ์„ ์–ธ:** ๋ณธ ๋ชจ๋ธ์˜ ํŒ์ • ๊ฒฐ๊ณผ๋Š” ๋ณด์กฐ ์ง€ํ‘œ์ด๋ฉฐ ์ตœ์ข… ๊ฒฐ์ • ๊ทผ๊ฑฐ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
""")
with gr.Column(scale=1):
# 1. ํŒ์ • ๊ฒฐ๊ณผ ๋ฐ ๋ชจ๋ธ ํƒœ๊ทธ
with gr.Row():
out_verdict = gr.Textbox(label="ํŒ์ •๊ฒฐ๊ณผ", elem_classes=["verdict-box"], interactive=False)
out_tag = gr.Textbox(label="Model Tag", value="Active Dolphin", interactive=False)
# 2. Highlight Word (์–ดํ…์…˜ ๋งต visual)
out_highlight = gr.HTML(label="Highlight Word")
# 3. ์œ„ํ—˜๋„ ์ ์ˆ˜ ๊ฒฐ๊ณผ
out_risk = gr.Textbox(label="๊ฒฐ๊ณผ", elem_classes=["risk-box"], interactive=False)
# 4. ํ•œ๊ณ„ ๊ณ ์ง€ ๊ฒฝ๊ณ  ๋ฌธ๊ตฌ (UI ์Šค์ผ€์น˜ ํ•˜๋‹จ)
out_disclaimer = gr.Textbox(
label="ํ•œ๊ณ„ ๊ณ ์ง€ ์•Œ๋ฆผ",
elem_classes=["disclaimer-box"],
interactive=False
)
# 5. ์ด์˜ ์ œ๊ธฐ ๋ฒ„ํŠผ ๋ฐ ๋ชจ๋‹ฌ ๋ ˆ์ด์•„์›ƒ
with gr.Row():
btn_appeal_open = gr.Button("์ด์˜ ์ œ๊ธฐ", variant="secondary")
with gr.Group(visible=False) as appeal_group:
appeal_input = gr.Textbox(label="์ด์˜ ์ œ๊ธฐ ์‚ฌ์œ  ์ž…๋ ฅ", placeholder="์˜คํŒ์ด๋ผ ์ƒ๊ฐํ•˜๋Š” ์ด์œ ๋ฅผ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”.")
btn_appeal_submit = gr.Button("์ œ์ถœํ•˜๊ธฐ")
appeal_status = gr.Markdown()
# ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์—ฐ๊ฒฐ
btn_analyze.click(
fn=run_pipeline,
inputs=[input_area],
outputs=[out_verdict, out_tag, out_highlight, out_risk, out_disclaimer]
)
btn_appeal_open.click(
fn=lambda: gr.update(visible=True),
inputs=None,
outputs=[appeal_group]
)
btn_appeal_submit.click(
fn=submit_appeal,
inputs=[appeal_input],
outputs=[appeal_status]
)
if __name__ == "__main__":
demo.launch()