Update app.py
Browse files
app.py
CHANGED
|
@@ -1,107 +1,57 @@
|
|
| 1 |
-
#
|
| 2 |
-
#
|
| 3 |
-
# -
|
| 4 |
-
# -
|
| 5 |
-
# - ์ธ๋ถ: (์ ํ) ๋ค์ด๋ฒ ๋ด์ค API๋ก ๊ธฐ์ฌ ๊ฒ์, (์ ํ) OpenAI๋ก ์ฌ์ค ๊ธฐ๋ฐ ์ฌ๊ตฌ์ฑ
|
| 6 |
|
| 7 |
import os
|
| 8 |
-
import
|
| 9 |
-
import time
|
| 10 |
-
from typing import List, Dict, Tuple, Optional
|
| 11 |
|
| 12 |
-
import requests
|
| 13 |
import streamlit as st
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
import
|
| 17 |
-
from sklearn.feature_extraction.text import TfidfVectorizer
|
| 18 |
-
from sklearn.linear_model import SGDClassifier
|
| 19 |
-
from sklearn.metrics import classification_report, accuracy_score
|
| 20 |
-
|
| 21 |
-
# HF datasets
|
| 22 |
-
from datasets import load_dataset
|
| 23 |
|
| 24 |
# =========================
|
| 25 |
-
#
|
| 26 |
# =========================
|
| 27 |
-
APP_TITLE = "์ ์น ํธํฅ
|
| 28 |
-
MODEL_PATH = "incremental_model.pkl"
|
| 29 |
-
VECT_PATH = "tfidf_vectorizer.pkl"
|
| 30 |
-
|
| 31 |
OPENAI_API_KEY = os.getenv("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") # ์ ํ: ์ฌ์ค ๊ธฐ๋ฐ ์ฌ๊ตฌ์ฑ์ ์ฌ์ฉ
|
| 32 |
NAVER_ID = os.getenv("I_8koTJh3R5l4wLurQbG") # ์ ํ: ๋ค์ด๋ฒ ๋ด์ค ๊ฒ์
|
| 33 |
NAVER_SECRET = os.getenv("W5oWYlAgur")
|
| 34 |
|
|
|
|
|
|
|
|
|
|
| 35 |
st.set_page_config(page_title=APP_TITLE, page_icon="๐งญ", layout="wide")
|
| 36 |
st.title(APP_TITLE)
|
| 37 |
-
st.caption("
|
| 38 |
|
| 39 |
# =========================
|
| 40 |
-
#
|
| 41 |
# =========================
|
| 42 |
-
@st.cache_resource(show_spinner=
|
| 43 |
-
def
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
train = [to_example(r) for r in ds["train"]]
|
| 57 |
-
test = [to_example(r) for r in ds["test"]]
|
| 58 |
-
return train, test
|
| 59 |
-
|
| 60 |
-
def label_to_id(lbl: str) -> int:
|
| 61 |
-
lbl = (lbl or "").strip().lower()
|
| 62 |
-
if "dem" in lbl: return 0
|
| 63 |
-
if "rep" in lbl: return 1
|
| 64 |
-
return 2
|
| 65 |
-
|
| 66 |
-
def id_to_label(i: int) -> str:
|
| 67 |
-
return ["Democrat","Republican","NEUTRAL"][i]
|
| 68 |
-
|
| 69 |
-
def split_xy(rows: List[Dict[str,str]]) -> Tuple[List[str], List[int]]:
|
| 70 |
-
X = [(r["text"] or "") for r in rows]
|
| 71 |
-
y = [label_to_id(r["label"]) for r in rows]
|
| 72 |
-
return X, y
|
| 73 |
|
| 74 |
-
|
| 75 |
-
#
|
| 76 |
-
#
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
vect = joblib.load(VECT_PATH)
|
| 81 |
-
else:
|
| 82 |
-
model = SGDClassifier(loss="log_loss")
|
| 83 |
-
vect = TfidfVectorizer(max_features=10000, ngram_range=(1,2), stop_words="english")
|
| 84 |
-
return model, vect
|
| 85 |
-
|
| 86 |
-
def incremental_fit(model, vect, texts: List[str], labels: List[int]):
|
| 87 |
-
X = vect.fit_transform(texts)
|
| 88 |
-
model.partial_fit(X, labels, classes=[0,1,2])
|
| 89 |
-
joblib.dump(model, MODEL_PATH)
|
| 90 |
-
joblib.dump(vect, VECT_PATH)
|
| 91 |
-
return model, vect
|
| 92 |
-
|
| 93 |
-
def predict(model, vect, text: str) -> Tuple[int, List[float]]:
|
| 94 |
-
X = vect.transform([text])
|
| 95 |
-
probs = getattr(model, "predict_proba", None)
|
| 96 |
-
if probs is None:
|
| 97 |
-
y = int(model.predict(X)[0])
|
| 98 |
-
return y, [0.0, 0.0, 0.0]
|
| 99 |
-
pr = model.predict_proba(X)[0]
|
| 100 |
-
y = int(pr.argmax())
|
| 101 |
-
return y, pr.tolist()
|
| 102 |
|
| 103 |
# =========================
|
| 104 |
-
#
|
| 105 |
# =========================
|
| 106 |
def fetch_naver_news(query: str, display: int = 10) -> List[Dict[str,str]]:
|
| 107 |
if not (NAVER_ID and NAVER_SECRET):
|
|
@@ -114,14 +64,11 @@ def fetch_naver_news(query: str, display: int = 10) -> List[Dict[str,str]]:
|
|
| 114 |
if r.status_code != 200:
|
| 115 |
return []
|
| 116 |
items = r.json().get("items", [])
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
"link": it.get("link","")
|
| 123 |
-
})
|
| 124 |
-
return out
|
| 125 |
except Exception:
|
| 126 |
return []
|
| 127 |
|
|
@@ -129,24 +76,15 @@ def fetch_naver_news(query: str, display: int = 10) -> List[Dict[str,str]]:
|
|
| 129 |
# OpenAI: Fact-based neutral rewrite (optional)
|
| 130 |
# =========================
|
| 131 |
def generate_fact_based(text: str) -> str:
|
| 132 |
-
"""
|
| 133 |
-
์ ์น์ ํด์/ํ๊ฐ ์ ๊ฑฐ, ์ฌ์ค ์ค์ฌ ์ค๋ฆฝ ๊ธฐ์ฌ๋ก ์ฌ๊ตฌ์ฑ
|
| 134 |
-
"""
|
| 135 |
if not OPENAI_API_KEY:
|
| 136 |
return "(OPENAI_API_KEY ๋ฏธ์ค์ : ์ฌ๊ตฌ์ฑ ์๋ต๋จ)"
|
| 137 |
import openai
|
| 138 |
openai.api_key = OPENAI_API_KEY
|
| 139 |
-
|
| 140 |
prompt = (
|
| 141 |
-
"๋ค์ ํ
์คํธ๋ฅผ ์ ์น์
|
| 142 |
-
"
|
| 143 |
-
"1) ๋๊ฐ(ํ์์)ยท์ธ์ ยท์ด๋์ยท๋ฌด์์ยท์ด๋ป๊ฒยท์ ์ค ์ฌ์ค ์ ๋ณด๋ง ์์ \n"
|
| 144 |
-
"2) ํ๊ฐ/๊ฐ์ /์ถ์ธก ํํ ์ญ์ \n"
|
| 145 |
-
"3) ์์นยท๋ ์งยท์ธ์ฉ์ ์๋ฌธ์ ์๋ ๋ฒ์์์๋ง ์ฌ์ฉ\n"
|
| 146 |
-
"4) ํ๊ตญ์ด 5~7๋ฌธ์ฅ, ์ ๋ชฉ ์์ด ๋ณธ๋ฌธ๋ง\n\n"
|
| 147 |
f"[์๋ฌธ]\n{text}\n\n[์ค๋ฆฝ ๊ธฐ์ฌ]"
|
| 148 |
)
|
| 149 |
-
|
| 150 |
try:
|
| 151 |
resp = openai.ChatCompletion.create(
|
| 152 |
model="gpt-4o-mini",
|
|
@@ -159,89 +97,54 @@ def generate_fact_based(text: str) -> str:
|
|
| 159 |
return f"(์ฌ๊ตฌ์ฑ ์คํจ: {e})"
|
| 160 |
|
| 161 |
# =========================
|
| 162 |
-
# Sidebar
|
| 163 |
# =========================
|
| 164 |
with st.sidebar:
|
| 165 |
-
st.subheader("
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
model, vect = init_or_load_model()
|
| 171 |
-
model, vect = incremental_fit(model, vect, Xtr, ytr)
|
| 172 |
-
st.success("ํ์ต ์๋ฃ. ๋ชจ๋ธ/๋ฒกํฐ ์ ์ฅ๋จ.")
|
| 173 |
-
|
| 174 |
-
if st.button("โก ์ฑ๋ฅ ํ๊ฐ", use_container_width=True):
|
| 175 |
-
try:
|
| 176 |
-
_, test = load_political_tweets()
|
| 177 |
-
Xte_texts, yte = split_xy(test)
|
| 178 |
-
model, vect = init_or_load_model()
|
| 179 |
-
Xte = vect.transform(Xte_texts)
|
| 180 |
-
ypred = model.predict(Xte)
|
| 181 |
-
acc = accuracy_score(yte, ypred)
|
| 182 |
-
st.write(f"์ ํ๋: **{acc:.3f}**")
|
| 183 |
-
st.code(classification_report(yte, ypred, target_names=["Democrat","Republican","NEUTRAL"]))
|
| 184 |
-
except Exception as e:
|
| 185 |
-
st.error(e)
|
| 186 |
-
|
| 187 |
-
st.markdown("---")
|
| 188 |
-
st.caption("์ฃผ์: ๋ฐ์ดํฐ๋ ๋ฏธ๊ตญ ์ ์น ํธ์(์๋ฌธ). ํ๊ตญ์ด ๊ธฐ์ฌ ์ผ๋ฐํ์ ํ๊ณ๊ฐ ์์.")
|
| 189 |
|
| 190 |
# =========================
|
| 191 |
-
# Main
|
| 192 |
# =========================
|
| 193 |
st.markdown("### 1) (์ ํ) ๋ค์ด๋ฒ ๋ด์ค ๊ฒ์")
|
| 194 |
q = st.text_input("๊ฒ์์ด", value="๋ฏธ ๋์ ")
|
| 195 |
cnt = st.slider("ํ์ ๊ฐ์", 1, 20, 10)
|
| 196 |
news_items: List[Dict[str,str]] = []
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
if
|
| 201 |
-
|
| 202 |
-
news_items = fetch_naver_news(q, cnt)
|
| 203 |
-
if not news_items:
|
| 204 |
-
st.info("๋ค์ด๋ฒ API ํค๊ฐ ์๊ฑฐ๋ ํธ์ถ ์คํจ. ์๋ ์์ ์
๋ ฅ์ผ๋ก ํ
์คํธํ์ธ์.")
|
| 205 |
|
| 206 |
st.markdown("### 2) ํ
์คํธ ๋ถ์ & ์ฌ๏ฟฝ๏ฟฝ๏ฟฝ ๊ธฐ๋ฐ ์ค๋ฆฝ ์ฌ๊ตฌ์ฑ")
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
sample = ""
|
| 211 |
-
if news_items:
|
| 212 |
-
# ์ฒซ ์์ดํ
์ ์ํ๋ก ๋ฃ์ด์ค (์ฌ์ฉ์๊ฐ ์์ ๊ฐ๋ฅ)
|
| 213 |
-
sample = f"{news_items[0]['title']} โ {news_items[0]['desc']}"
|
| 214 |
text = st.text_area("๋ถ์ํ ํ
์คํธ(๋ด์ค ์ ๋ชฉ+์์ฝ ๋ฑ)", value=sample, height=220)
|
| 215 |
|
| 216 |
-
with
|
| 217 |
if st.button("๋ถ์ ๋ฐ ์ค๋ฆฝ ์ฌ๊ตฌ์ฑ ์คํ"):
|
| 218 |
if not text.strip():
|
| 219 |
st.warning("ํ
์คํธ๋ฅผ ์
๋ ฅํ์ธ์.")
|
| 220 |
else:
|
| 221 |
-
#
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
st.write(
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
st.write(f"์์ธก ๋ผ๋ฒจ: **{pred}**")
|
| 237 |
-
st.write(f"ํ๋ฅ (๋ฏผยท๊ณตยท์ค): {pr}")
|
| 238 |
-
|
| 239 |
-
if news_items:
|
| 240 |
-
with st.expander("์๋ฌธ ๋งํฌ"):
|
| 241 |
-
st.write(news_items[0].get("link","(๋งํฌ ์์)"))
|
| 242 |
|
| 243 |
st.markdown("---")
|
| 244 |
-
st.caption(
|
| 245 |
-
"๋ฐ๋ชจ ์ฉ๋. ์ค์ ์๋น์ค์์๋ ์ถ์ฒ ์๋์ถ์ถ, ์ฌ์ค ๊ฒ์ฆ(์: ์ธ์ฉยท์ซ์ ๊ต์ฐจ๊ฒ์ฆ), "
|
| 246 |
-
"์ ์น์ ํํ ํํฐ๋ง, ๊ฐ์ธ์ ๋ณด/๋ช
์ํผ์ ์์ ์ฅ์น๊ฐ ์ถ๊ฐ๋์ด์ผ ํฉ๋๋ค."
|
| 247 |
-
)
|
|
|
|
| 1 |
+
# ์ ์น ํธํฅ ๋ฐฉ์ง ยท ์ฌ์ค ๊ธฐ๋ฐ ์ค๋ฆฝ ์ฌ๊ตฌ์ฑ (BERT ๋ถ๋ฅ๊ธฐ ๋ฒ์ )
|
| 2 |
+
# - ๋ถ๋ฅ: bucketresearch/politicalBiasBERT (left/center/right)
|
| 3 |
+
# - ์ฌ๊ตฌ์ฑ: OpenAI๋ก ์ฌ์ค ์ค์ฌ ์์ฝ/์ฌ์์ฑ
|
| 4 |
+
# - ์ต์
: ๋ค์ด๋ฒ ๋ด์ค ๊ฒ์
|
|
|
|
| 5 |
|
| 6 |
import os
|
| 7 |
+
from typing import List, Dict, Tuple
|
|
|
|
|
|
|
| 8 |
|
|
|
|
| 9 |
import streamlit as st
|
| 10 |
+
import requests
|
| 11 |
+
import torch
|
| 12 |
+
from transformers import AutoTokenizer, AutoModelForSequenceClassification, pipeline
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
# =========================
|
| 15 |
+
# Config
|
| 16 |
# =========================
|
| 17 |
+
APP_TITLE = "์ ์น ํธํฅ ๋ถ์(BERT) ยท ์ฌ์ค ๊ธฐ๋ฐ ์ค๋ฆฝ ์ฌ๊ตฌ์ฑ"
|
|
|
|
|
|
|
|
|
|
| 18 |
OPENAI_API_KEY = os.getenv("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") # ์ ํ: ์ฌ์ค ๊ธฐ๋ฐ ์ฌ๊ตฌ์ฑ์ ์ฌ์ฉ
|
| 19 |
NAVER_ID = os.getenv("I_8koTJh3R5l4wLurQbG") # ์ ํ: ๋ค์ด๋ฒ ๋ด์ค ๊ฒ์
|
| 20 |
NAVER_SECRET = os.getenv("W5oWYlAgur")
|
| 21 |
|
| 22 |
+
MODEL_ID = "bucketresearch/politicalBiasBERT"
|
| 23 |
+
LABELS = ["left", "center", "right"] # ๋ชจ๋ธ ์นด๋ ์ ์
|
| 24 |
+
|
| 25 |
st.set_page_config(page_title=APP_TITLE, page_icon="๐งญ", layout="wide")
|
| 26 |
st.title(APP_TITLE)
|
| 27 |
+
st.caption("PoliticalBiasBERT๋ก ํธํฅ(์ข/์ค/์ฐ) ๋ถ๋ฅ โ ํ๋ฉด์๋ '์ฌ์ค ๊ธฐ๋ฐ ์ค๋ฆฝ ์ฌ๊ตฌ์ฑ'๋ง ๋
ธ์ถ")
|
| 28 |
|
| 29 |
# =========================
|
| 30 |
+
# Model (cached)
|
| 31 |
# =========================
|
| 32 |
+
@st.cache_resource(show_spinner=True)
|
| 33 |
+
def load_bias_pipeline():
|
| 34 |
+
tok = AutoTokenizer.from_pretrained(MODEL_ID)
|
| 35 |
+
mdl = AutoModelForSequenceClassification.from_pretrained(MODEL_ID)
|
| 36 |
+
device = 0 if torch.cuda.is_available() else -1
|
| 37 |
+
clf = pipeline(
|
| 38 |
+
"text-classification",
|
| 39 |
+
model=mdl,
|
| 40 |
+
tokenizer=tok,
|
| 41 |
+
return_all_scores=True,
|
| 42 |
+
device=device
|
| 43 |
+
)
|
| 44 |
+
return clf
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
|
| 46 |
+
def classify_bias(clf, text: str) -> Tuple[str, List[float]]:
|
| 47 |
+
scores = clf(text)[0] # list of dicts: [{"label":"LABEL_0","score":...}, ...]
|
| 48 |
+
# ๋ชจ๋ธ์ด LABEL_0/1/2๋ฅผ ์ฐ๋ฏ๋ก index ๊ธฐ์ค์ผ๋ก ์ ๋ ฌ๋์ด ๋ค์ด์จ๋ค๋ ๊ฐ์
|
| 49 |
+
probs = [s["score"] for s in scores] # [left, center, right] ์
|
| 50 |
+
pred_idx = int(torch.tensor(probs).argmax().item())
|
| 51 |
+
return LABELS[pred_idx], probs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
# =========================
|
| 54 |
+
# Naver News (optional)
|
| 55 |
# =========================
|
| 56 |
def fetch_naver_news(query: str, display: int = 10) -> List[Dict[str,str]]:
|
| 57 |
if not (NAVER_ID and NAVER_SECRET):
|
|
|
|
| 64 |
if r.status_code != 200:
|
| 65 |
return []
|
| 66 |
items = r.json().get("items", [])
|
| 67 |
+
return [{
|
| 68 |
+
"title": it.get("title",""),
|
| 69 |
+
"desc": it.get("description",""),
|
| 70 |
+
"link": it.get("link","")
|
| 71 |
+
} for it in items]
|
|
|
|
|
|
|
|
|
|
| 72 |
except Exception:
|
| 73 |
return []
|
| 74 |
|
|
|
|
| 76 |
# OpenAI: Fact-based neutral rewrite (optional)
|
| 77 |
# =========================
|
| 78 |
def generate_fact_based(text: str) -> str:
|
|
|
|
|
|
|
|
|
|
| 79 |
if not OPENAI_API_KEY:
|
| 80 |
return "(OPENAI_API_KEY ๋ฏธ์ค์ : ์ฌ๊ตฌ์ฑ ์๋ต๋จ)"
|
| 81 |
import openai
|
| 82 |
openai.api_key = OPENAI_API_KEY
|
|
|
|
| 83 |
prompt = (
|
| 84 |
+
"๋ค์ ํ
์คํธ๋ฅผ ์ ์น์ ํด์/์๊ฒฌ ์์ด, ์ฌ์ค ์ค์ฌ์ ์ค๋ฆฝ ๊ธฐ์ฌ๋ก ์ฌ๊ตฌ์ฑํ์ธ์.\n"
|
| 85 |
+
"๊ท์น: 1) ๋๊ฐยท์ธ์ ยท์ด๋์ยท๋ฌด์์ ์ค์ฌ 2) ํ๊ฐ/์ถ์ธก ์ญ์ 3) ์์น/๋ ์ง๋ ์๋ฌธ ๋ฒ์ 4) ํ๊ตญ์ด 5~7๋ฌธ์ฅ\n\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
f"[์๋ฌธ]\n{text}\n\n[์ค๋ฆฝ ๊ธฐ์ฌ]"
|
| 87 |
)
|
|
|
|
| 88 |
try:
|
| 89 |
resp = openai.ChatCompletion.create(
|
| 90 |
model="gpt-4o-mini",
|
|
|
|
| 97 |
return f"(์ฌ๊ตฌ์ฑ ์คํจ: {e})"
|
| 98 |
|
| 99 |
# =========================
|
| 100 |
+
# Sidebar
|
| 101 |
# =========================
|
| 102 |
with st.sidebar:
|
| 103 |
+
st.subheader("๋ชจ๋ธ ์ํ")
|
| 104 |
+
with st.spinner("BERT ๋ชจ๋ธ ๋ก๋ฉ ์คโฆ ์ฒ์ ํ ๋ฒ๋ง ๊ธฐ๋ค๋ฆฌ๋ฉด ๋จ"):
|
| 105 |
+
clf = load_bias_pipeline()
|
| 106 |
+
st.success("PoliticalBiasBERT ๋ก๋ ์๋ฃ")
|
| 107 |
+
st.caption("์ข/์ค/์ฐ ๋ถ๋ฅ๋ ๋ด๋ถ ์ง๋จ์ฉ์ผ๋ก๋ง ์ฌ์ฉ. ํ๋ฉด์ ์ฌ์ค ๊ธฐ๋ฐ ์ฌ๊ตฌ์ฑ ์์ฃผ.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
|
| 109 |
# =========================
|
| 110 |
+
# Main
|
| 111 |
# =========================
|
| 112 |
st.markdown("### 1) (์ ํ) ๋ค์ด๋ฒ ๋ด์ค ๊ฒ์")
|
| 113 |
q = st.text_input("๊ฒ์์ด", value="๋ฏธ ๋์ ")
|
| 114 |
cnt = st.slider("ํ์ ๊ฐ์", 1, 20, 10)
|
| 115 |
news_items: List[Dict[str,str]] = []
|
| 116 |
+
if st.button("๋ด์ค ๋ถ๋ฌ์ค๊ธฐ"):
|
| 117 |
+
with st.spinner("๋ค์ด๋ฒ ๋ด์ค ์์ง ์คโฆ"):
|
| 118 |
+
news_items = fetch_naver_news(q, cnt)
|
| 119 |
+
if not news_items:
|
| 120 |
+
st.info("๋ค์ด๋ฒ API ํค๊ฐ ์๊ฑฐ๋ ํธ์ถ ์คํจ. ์๋ ์์ ์
๋ ฅ์ผ๋ก ํ
์คํธํ์ธ์.")
|
|
|
|
|
|
|
|
|
|
| 121 |
|
| 122 |
st.markdown("### 2) ํ
์คํธ ๋ถ์ & ์ฌ๏ฟฝ๏ฟฝ๏ฟฝ ๊ธฐ๋ฐ ์ค๋ฆฝ ์ฌ๊ตฌ์ฑ")
|
| 123 |
+
c1, c2 = st.columns(2)
|
| 124 |
+
with c1:
|
| 125 |
+
sample = f"{news_items[0]['title']} โ {news_items[0]['desc']}" if news_items else ""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
text = st.text_area("๋ถ์ํ ํ
์คํธ(๋ด์ค ์ ๋ชฉ+์์ฝ ๋ฑ)", value=sample, height=220)
|
| 127 |
|
| 128 |
+
with c2:
|
| 129 |
if st.button("๋ถ์ ๋ฐ ์ค๋ฆฝ ์ฌ๊ตฌ์ฑ ์คํ"):
|
| 130 |
if not text.strip():
|
| 131 |
st.warning("ํ
์คํธ๋ฅผ ์
๋ ฅํ์ธ์.")
|
| 132 |
else:
|
| 133 |
+
# ๋ด๋ถ ๋ถ๋ฅ(์ง๋จ์ฉ)
|
| 134 |
+
pred, probs = classify_bias(clf, text)
|
| 135 |
+
|
| 136 |
+
# ํ๋ฉด ๋
ธ์ถ: ์ฌ์ค ๊ธฐ๋ฐ ์ฌ๊ตฌ์ฑ
|
| 137 |
+
st.markdown("#### โ
์ฌ์ค ๊ธฐ๋ฐ ์ค๋ฆฝ ์ฌ๊ตฌ์ฑ ๊ฒฐ๊ณผ")
|
| 138 |
+
article = generate_fact_based(text)
|
| 139 |
+
st.write(article)
|
| 140 |
+
|
| 141 |
+
# ์ง๋จ/์ถ์ฒ
|
| 142 |
+
with st.expander("์ง๋จ ๋ณด๊ธฐ(๋ด๋ถ ํธํฅ ํ๋ฅ )"):
|
| 143 |
+
st.write(f"์์ธก: **{pred}**")
|
| 144 |
+
st.write(f"ํ๋ฅ [left, center, right]: {probs}")
|
| 145 |
+
if news_items:
|
| 146 |
+
with st.expander("์๋ฌธ ๋งํฌ"):
|
| 147 |
+
st.write(news_items[0].get("link","(๋งํฌ ์์)"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
|
| 149 |
st.markdown("---")
|
| 150 |
+
st.caption("๋ฐ๋ชจ ์ฉ๋. ์ค์ ์๋น์ค๋ ์ถ์ฒ ์ถ์ถยท์ฌ์ค ๊ฒ์ฆยท์ ์ฑ
ํํฐ๋ง์ ์ถ๊ฐํด์ผ ํจ.")
|
|
|
|
|
|
|
|
|