| import os |
| import re |
| import torch |
| import gradio as gr |
| from transformers import AutoTokenizer, AutoModelForSequenceClassification |
| from peft import PeftModel |
|
|
| |
| |
| |
| LORA_DIR = "./lora_climate_misinfo" |
| BASE_MODEL_NAME = "klue/roberta-base" |
|
|
| 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 |
|
|
| |
| |
| |
| 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 "โ ๏ธ ํ
์คํธ๋ฅผ ์
๋ ฅํด์ฃผ์ธ์.", "", "", "", "๋ด์ฉ์ ์
๋ ฅํ์ธ์." |
|
|
| |
| cleaned_text = preprocess_text(input_text) |
| |
| |
| 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 |
|
|
| |
| src_w, tgt_w = extract_xai_attention(cleaned_text) |
| |
| |
| 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)" |
|
|
| |
| |
| |
| 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): |
| |
| 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) |
|
|
| |
| out_highlight = gr.HTML(label="Highlight Word") |
|
|
| |
| out_risk = gr.Textbox(label="๊ฒฐ๊ณผ", elem_classes=["risk-box"], interactive=False) |
|
|
| |
| out_disclaimer = gr.Textbox( |
| label="ํ๊ณ ๊ณ ์ง ์๋ฆผ", |
| elem_classes=["disclaimer-box"], |
| interactive=False |
| ) |
|
|
| |
| 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() |
|
|