Spaces:
Sleeping
Sleeping
File size: 6,063 Bytes
1ebb586 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | import torch
import torch.nn.functional as F
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import gc
class SemanticAnalyzer:
def __init__(self):
print("๐ง [Step 2] ์๋ฏธ๋ก ์ ๋ฅ๋ฌ๋ ๋ถ์๊ธฐ(KoELECTRA)๋ฅผ ๋ก๋ํฉ๋๋ค...")
# M1 Max GPU(MPS) ์ธํ
self.device = torch.device("mps" if torch.backends.mps.is_available() else "cpu")
# ๊ฐ๋ณ๊ณ ์ฑ๋ฅ์ด ๋ฐ์ด๋ KoELECTRA ๋ชจ๋ธ ๋ก๋
self.model_name = "monologg/koelectra-base-v3-discriminator"
self.tokenizer = AutoTokenizer.from_pretrained(self.model_name)
# ๐ ํต์ฌ: output_hidden_states=True ์ต์
์ ์ผ์ผ
# ๋ถ๋ฅ Logit๊ณผ ์ฝ์ฌ์ธ ์ ์ฌ๋์ฉ Vector๋ฅผ ํ ๋ฒ์ ์ฐ์ฐ์ผ๋ก ๋ ๋ค ๋ฝ์๋ผ ์ ์์ต๋๋ค!
self.model = AutoModelForSequenceClassification.from_pretrained(
self.model_name,
num_labels=2, # 0: ์ ์, 1: ๊ณผ๋๊ด๊ณ
output_hidden_states=True
).to(self.device)
self.model.eval() # ์ถ๋ก ๋ชจ๋ ์ ํ
# [๋ฒกํฐ A ๊ตฌ์ถ] ๊ธฐ์กด์ ์ ๋ฐ๋ ํ์๊ด๊ณ ๋ ํผ๋ฐ์ค ๋ฌธ์ฅ๋ค (์์)
self.reference_bad_texts = [
"๋จ ์ผ์ฃผ์ผ ๋ง์ ์ง๋ฐฉ์ด 100% ๋ถํด๋๋ ๊ธฐ์ ์ ํฌ๋ฆผ",
"์์ฝ์ฒ์์ ์ธ์ฆํ ๋ง๋ณํต์น์ฝ, ์์ธํฌ ์๋ฒฝ ์ ๊ฑฐ",
"์ด๊ฒ๋ง ๋จน์ผ๋ฉด ๋
์๊ฐ ๋ฐฐ์ถ๋๊ณ ์ธํฌ๊ฐ ์ฆ๊ฐ ์ฌ์๋ฉ๋๋ค",
"์์ฌ๋ค์ด ๋ฌด์กฐ๊ฑด ์ถ์ฒํ๋ ๋ถ์์ฉ ์๋ ์์น์ "
]
print(" -> ๋ ํผ๋ฐ์ค ํ์๊ด๊ณ ๋ฌธ์ฅ๋ค์ ๋ฒกํฐ(Vector) ๊ณต๊ฐ์ ๋ฐฐ์น ์ค...")
self.reference_embeddings = self._get_embeddings(self.reference_bad_texts)
def clear_memory(self):
"""๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง์ฉ ๊ฐ๋น์ง ์ปฌ๋ ์
"""
gc.collect()
if torch.backends.mps.is_available():
torch.mps.empty_cache()
def _get_embeddings(self, texts):
"""ํ
์คํธ ๋ฆฌ์คํธ๋ฅผ ์
๋ ฅ๋ฐ์ ๋ฌธ์ฅ ์๋ฒ ๋ฉ ๋ฒกํฐ๋ฅผ ๋ฐํํฉ๋๋ค."""
inputs = self.tokenizer(texts, padding=True, truncation=True, max_length=512, return_tensors="pt").to(self.device)
with torch.no_grad():
outputs = self.model(**inputs)
# ๋ง์ง๋ง ๋ ์ด์ด์ hidden states์์ [CLS] ํ ํฐ(์ธ๋ฑ์ค 0)์ ๋ฒกํฐ๋ฅผ ๋ฌธ์ฅ ๋ํ ๋ฒกํฐ๋ก ์ฌ์ฉ
sentence_embeddings = outputs.hidden_states[-1][:, 0, :]
return sentence_embeddings
def calculate_x2_score(self, text):
if not text or len(text.strip()) < 5:
return 0.0
print("\n๐ง [Step 2] ๋ฅ๋ฌ๋ ๋ฌธ๋งฅ ๋ฐ ์ ์ฌ๋(Semantic) ๋ถ์ ์์...")
# ๋๋ฌด ๊ธด ํ
์คํธ๋ PLM ํ๊ณ(512ํ ํฐ)์ ๊ฑธ๋ฆฌ๋ฏ๋ก ์๋ผ์ ์
๋ ฅ
inputs = self.tokenizer(text, return_tensors="pt", truncation=True, max_length=512).to(self.device)
with torch.no_grad(): # ์ญ์ ํ(ํ์ต) ์ฐ์ฐ์ ๊บผ์ M1 ๋ฉ๋ชจ๋ฆฌ ์ ์ฝ
outputs = self.model(**inputs)
# =========================================================
# [์์ 1] ๋ถ๋ฅ ํ๋ฅ (Softmax) : X2
# =========================================================
logits = outputs.logits # ๋ชจ๋ธ์ ๋ ๊ฒ ์ถ๋ ฅ๊ฐ (z)
probs = F.softmax(logits, dim=-1) # Softmax ์ ์ฉ: e^z / sum(e^z)
# ๋ผ๋ฒจ 1(๊ณผ๋๊ด๊ณ )์ ํด๋นํ๋ ํ๋ฅ ๊ฐ (0.0 ~ 1.0)
prob_fake = probs[0][1].item()
# =========================================================
# [์์ 2] ์๋ฏธ๋ก ์ ์ ์ฌ๋ (Cosine Similarity)
# =========================================================
# ํ์ฌ ๊ฒ์ฌ ์ค์ธ ํ
์คํธ์ ๋ฒกํฐ B ์ถ์ถ
current_embedding = outputs.hidden_states[-1][:, 0, :]
# ๋ ํผ๋ฐ์ค ๋ฒกํฐ A๋ค๊ณผ ๋ฒกํฐ B์ ์ ์ฌ๋ ๊ณ์ฐ: (A * B) / (|A| * |B|)
similarities = F.cosine_similarity(current_embedding, self.reference_embeddings)
# ๊ฐ์ฅ ๋ฌธ๋งฅ์ด ๋น์ทํ๋ค๊ณ ํ์ ๋ ๋ ํผ๋ฐ์ค์์ ์ต๊ณ ์ ์ฌ๋ ์ ์
max_sim = torch.max(similarities).item()
# ๋ถ์์ด ๋๋๋ฉด ์ฆ์ VRAM ๋ฐํ
del inputs, outputs, logits, probs, current_embedding, similarities
self.clear_memory()
# =========================================================
# ์ต์ข
X2 ์ค์ฝ์ด ์ฐ์ถ (๋ถ๋ฅ ํ๋ฅ + ์ ์ฌ๋ ์์๋ธ)
# =========================================================
# ๋ถ๋ฅ๊ธฐ ํ๋ฅ ์ 100์ ๋ง์ ์ผ๋ก ๋ณํ
classification_score = prob_fake * 100
# ์ฝ์ฌ์ธ ์ ์ฌ๋๋ฅผ 100์ ๋ง์ ์ผ๋ก ๋ณํ (์ ์ฌ๋๊ฐ 0 ์ดํ๋ฉด 0์ ์ฒ๋ฆฌ)
similarity_score = max(max_sim, 0) * 100
# ๐ก [์ค๋ฌด ํ] ํผํฉ ๊ฐ์ค์น ์ ์ฉ
# ํ์ฌ KoELECTRA ๋ชจ๋ธ์ '๊ณผ๋๊ด๊ณ ์ ์ฉ'์ผ๋ก ํ์ธํ๋ ๋์ง ์์ ์ฉ์ผ ์ํ์
๋๋ค.
# ๋ฐ๋ผ์ Softmax ๋ถ๋ฅ ํ๋ฅ ์ ๋๋ค์ ๊ฐ๊น๊ณ , ์ฝ์ฌ์ธ ์ ์ฌ๋๊ฐ ํจ์ฌ ์ ํํฉ๋๋ค.
# ์ ์ฌ๋ ์ ์์ 80%, ๋ถ๋ฅ ์ ์์ 20% ๊ฐ์ค์น๋ฅผ ์ฃผ์ด ์ต์ข
์ ์๋ฅผ ๋ง๋ญ๋๋ค.
x2_score = (similarity_score * 0.8) + (classification_score * 0.2)
print(f" -> ๐ ๋ชจ๋ธ ๋ถ๋ฅ ํ๋ฅ (Softmax): {prob_fake*100:.1f}%")
print(f" -> ๐ ์ต๋ ๋ฌธ๋งฅ ์ ์ฌ๋ (Cosine Sim): {max_sim*100:.1f}%")
print(f"๐ ์ต์ข
X2 ์ ์ (0~100): {x2_score:.2f}์ ")
return x2_score
# ==========================================
# ๋จ๋
ํ
์คํธ ์ฝ๋
# ==========================================
if __name__ == "__main__":
analyzer = SemanticAnalyzer()
# ๋ ํผ๋ฐ์ค("๋จ ์ผ์ฃผ์ผ ๋ง์...")์ ์๋ฏธ๊ฐ ์ ์ฌํ ๋ฌธ์ฅ ํ
์คํธ
test_text = "๋จ 7์ผ๋ง ํฌ์ํ์ธ์! ์ง๋ฐฉ์ด ์์ ํ ํ๊ดด๋๋ ๋๋ผ์ด ๋ง๋ฒ์ ๊ฒช๊ฒ ๋ฉ๋๋ค."
analyzer.calculate_x2_score(test_text) |