20416 / app.py
kdhs's picture
Upload 7 files
f384d3e 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()