| import os |
| import pickle |
| import sys |
| import traceback |
| import numpy as np |
| import streamlit as st |
| import torch |
| import torch.nn.functional as F |
| from transformers import AutoModelForSequenceClassification, AutoTokenizer |
|
|
| |
| BASE_DIR = os.path.dirname(__file__) |
| KC_DIR = os.path.join(BASE_DIR, "kcbert_web") |
| DEBERTA_DIR = os.path.join(BASE_DIR, "deberta_web") |
| CNN_PATH = os.path.join(BASE_DIR, "char_cnn_web.pt") |
| VOCAB_PATH = os.path.join(BASE_DIR, "vocab.pkl") |
|
|
| |
| @st.cache_resource(show_spinner=False) |
| def load_verification_models(): |
| try: |
| |
| kc_tokenizer = AutoTokenizer.from_pretrained(KC_DIR) |
| kc_model = AutoModelForSequenceClassification.from_pretrained(KC_DIR) |
| kc_model.eval() |
| |
| |
| deberta_tokenizer = AutoTokenizer.from_pretrained(DEBERTA_DIR) |
| deberta_model = AutoModelForSequenceClassification.from_pretrained(DEBERTA_DIR) |
| deberta_model.eval() |
| |
| |
| vocab = None |
| if os.path.exists(VOCAB_PATH): |
| with open(VOCAB_PATH, "rb") as f: |
| vocab = pickle.load(f) |
| cnn_model = None |
| |
| return kc_tokenizer, kc_model, deberta_tokenizer, deberta_model, cnn_model |
| except Exception as e: |
| raise RuntimeError( |
| "๋ชจ๋ธ ๋ก๋ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค. ๋ชจ๋ธ ํด๋ ๊ฒฝ๋ก ๋ฐ ํ์ผ๋ค์ ํ์ธํด์ฃผ์ธ์.\n" |
| f"์์ธ ์ค๋ฅ: {e}" |
| ) |
|
|
| |
| try: |
| kc_tok, kc_mod, deb_tok, deb_mod, cnn_mod = load_verification_models() |
| device = "cuda" if torch.cuda.is_available() else "cpu" |
| |
| kc_mod.to(device) |
| deb_mod.to(device) |
| except Exception as e: |
| st.error("โ ๏ธ ์์คํ
์ด๊ธฐํ ์คํจ (๋ชจ๋ธ ๋ก๋ ์๋ฌ)") |
| st.code(str(e)) |
| st.stop() |
|
|
| |
| def calculate_similarity(text_a, text_b): |
| with torch.no_grad(): |
| |
| inputs_a = kc_tok(text_a, return_tensors="pt", truncation=True, max_length=128).to(device) |
| outputs_a = kc_mod(**inputs_a) |
| prob_a_kc = F.softmax(outputs_a.logits, dim=-1).flatten() |
|
|
| inputs_b = kc_tok(text_b, return_tensors="pt", truncation=True, max_length=128).to(device) |
| outputs_b = kc_mod(**inputs_b) |
| prob_b_kc = F.softmax(outputs_b.logits, dim=-1).flatten() |
|
|
| kc_sim = F.cosine_similarity(prob_a_kc.unsqueeze(0), prob_b_kc.unsqueeze(0)).item() * 100 |
|
|
| |
| deb_inputs_a = deb_tok(text_a, return_tensors="pt", truncation=True, max_length=128).to(device) |
| deb_outputs_a = deb_mod(**deb_inputs_a) |
| prob_a_deb = F.softmax(deb_outputs_a.logits, dim=-1).flatten() |
|
|
| deb_inputs_b = deb_tok(text_b, return_tensors="pt", truncation=True, max_length=128).to(device) |
| deb_outputs_b = deb_mod(**deb_inputs_b) |
| prob_b_deb = F.softmax(deb_outputs_b.logits, dim=-1).flatten() |
|
|
| deb_sim = F.cosine_similarity(prob_a_deb.unsqueeze(0), prob_b_deb.unsqueeze(0)).item() * 100 |
|
|
| |
| final_similarity = (kc_sim + deb_sim) / 2.0 |
| |
| return kc_sim, deb_sim, final_similarity |
|
|
| |
| st.set_page_config(page_title="์ ์ ์๋ณ ์์คํ
", layout="wide", page_icon="๐ต๏ธโโ๏ธ") |
| st.title("๐ต๏ธโโ๏ธ ์ํ ํ๋ก์ ํธ: ๋ฌธ์ฒดํ ๊ธฐ๋ฐ ์ ์ ๋์ผ์ฑ ๊ฒ์ฆ ์์คํ
") |
| st.subheader("๋ ๊ฐ์ ๊ธ์ ๋น๊ตํ์ฌ ๋์ผ ์ธ๋ฌผ์ด ์์ฑํ๋์ง ์ค์๊ฐ์ผ๋ก ๋ถ์ํฉ๋๋ค.") |
| st.write("---") |
|
|
| |
| col1, col2 = st.columns(2) |
| with col1: |
| st.markdown("### ๐ ๋ถ์ ๋์ ๊ธ A") |
| text_a = st.text_area( |
| "์ฒซ ๋ฒ์งธ ๊ธ์ ์
๋ ฅํ์ธ์:", |
| placeholder="๋น๊ตํ ์ฒซ ๋ฒ์งธ ๋ณธ๋ฌธ์ ์
๋ ฅํ์ธ์.", |
| height=250, |
| key="text_a", |
| ) |
| with col2: |
| st.markdown("### ๐ ๋ถ์ ๋์ ๊ธ B") |
| text_b = st.text_area( |
| "๋ ๋ฒ์งธ ๊ธ์ ์
๋ ฅํ์ธ์:", |
| placeholder="๋น๊ตํ ๋ ๋ฒ์งธ ๋ณธ๋ฌธ์ ์
๋ ฅํ์ธ์.", |
| height=250, |
| key="text_b", |
| ) |
|
|
| st.write("---") |
|
|
| |
| if st.button("๐ ๋์ผ์ธ ์ฌ๋ถ ์ ๋ฐ ๊ฒ์ฆ ์์", use_container_width=True): |
| if not text_a.strip() or not text_b.strip(): |
| st.error("โ ๏ธ ๊ธ A์ ๊ธ B ๋ชจ๋ ํ
์คํธ๋ฅผ ์
๋ ฅํด์ผ ๋ถ์์ด ๊ฐ๋ฅํฉ๋๋ค!") |
| else: |
| try: |
| with st.spinner("3๋์ฅ ์๊ณ ๋ฆฌ์ฆ์ด ๋ ๋ฌธ์ฅ์ ๋ฌธ์ฒด ํจํด์ ๋์กฐํ๋ ์ค..."): |
| |
| kc_sim, deb_sim, final_sim = calculate_similarity(text_a, text_b) |
|
|
| |
| st.success("๐ ๋ฌธ์ฒด ๋์กฐ ๋ถ์ ์๋ฃ!") |
|
|
| |
| THRESHOLD = 85.0 |
| is_same_author = final_sim >= THRESHOLD |
|
|
| |
| rect_col1, rect_col2 = st.columns(2) |
| with rect_col1: |
| if is_same_author: |
| st.metric(label="์ต์ข
ํ์ ๊ฒฐ๊ณผ", value="๐ข ๋์ผ์ธ ๊ฐ๋ฅ์ฑ ๋งค์ฐ ๋์") |
| else: |
| st.metric(label="์ต์ข
ํ์ ๊ฒฐ๊ณผ", value="๐ด ๋ค๋ฅธ ์ธ๋ฌผ์ผ ๊ฐ๋ฅ์ฑ ๋์") |
| with rect_col2: |
| st.metric(label="์ต์ข
๋ฌธ์ฒด ์ ์ฌ๋ ์ ์", value=f"{final_sim:.2f} / 100์ ") |
|
|
| st.write("---") |
| |
| |
| st.progress(min(max(final_sim / 100.0, 0.0), 1.0)) |
|
|
| |
| result_col1, result_col2 = st.columns([4, 3]) |
| |
| with result_col1: |
| st.markdown("### ๐ ๋ชจ๋ธ๋ณ ๋ฌธ์ฒด ๋์กฐ ์ค์ฝ์ด") |
| scores = { |
| "KcBERT ๋์กฐ ์ ์": kc_sim, |
| "DeBERTa ๋์กฐ ์ ์": deb_sim, |
| "์ต์ข
์์๋ธ ๊ฒฐ๋ก ": final_sim, |
| } |
| st.bar_chart(scores) |
|
|
| with result_col2: |
| st.markdown("### ๐ ๋ชจ๋ธ๋ณ ๊ฒฐ๊ณผ ์์น") |
| model_table = { |
| "ํ๊ฐ ํญ๋ชฉ": ["KcBERT", "DeBERTa", "์ต์ข
์์๋ธ ๊ฒฐ๊ณผ"], |
| "์ ์ฌ๋ ์ค์ฝ์ด": [f"{kc_sim:.2f}์ ", f"{deb_sim:.2f}์ ", f"{final_sim:.2f}์ "] |
| } |
| st.table(model_table) |
|
|
| |
| st.info( |
| f"๐ก **๋ถ์ ๊ฒฐ๊ณผ ์์ฝ**: ๋ ๋ฌธ์ฅ์ ๋ฌธ์ฅ ๊ตฌ์กฐ, ๋จ์ด ์ ํ ํจํด, ์ด์กฐ๋ฅผ ์ข
ํฉํ ๊ฒฐ๊ณผ " |
| f"์ต์ข
**{final_sim:.1f}%**์ ์ผ์น์จ์ ๋ณด์์ต๋๋ค. (ํ์ ๊ธฐ์ค์ : {THRESHOLD}%)" |
| ) |
|
|
| |
| with st.expander("๐ ๏ธ ์์คํ
๊ธฐ์ ์ ๋ณด"): |
| st.write("์คํ ์ฅ์น (Device):", device) |
| st.write("Python ์คํ ๊ฒฝ๋ก:") |
| st.code(sys.executable) |
| st.write("๋ชจ๋ธ ๊ธฐ๋ณธ ๋๋ ํ ๋ฆฌ:") |
| st.code(BASE_DIR) |
|
|
| st.caption("์ฃผ์: ๋ณธ ๊ฒฐ๊ณผ๋ AI ๋ชจ๋ธ ๊ธฐ๋ฐ ์ถ์ ๊ฐ์ด๋ฉฐ, ์ค์ ๋์ผ์ธ์ ๋จ์ ํ๋ ๋ฒ์ ํ๋จ์ด ์๋๋๋ค.") |
|
|
| except Exception as e: |
| st.error("๊ณ ๊ธ ๋ชจ๋ธ ์คํ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.") |
| st.code(str(e)) |
| with st.expander("์์ธ ์ค๋ฅ ๋ณด๊ธฐ (Traceback)"): |
| st.code(traceback.format_exc()) |