analisisNews / app /analyzers /sentiment.py
ahmadsayadi's picture
feat: BrainWatches Python Analysis Service - sentiment, topics, summarize, similarity
60e8c0a
Raw
History Blame Contribute Delete
3.07 kB
"""
Sentiment analyzer.
- mode "light": lexicon-based sederhana (cepat, tanpa dependency berat)
- mode "transformer": IndoBERT/RoBERTa (akurat, butuh torch + transformers)
"""
from typing import List, Dict
from app.config import settings
# Lexicon ringan Bahasa Indonesia (subset). Bisa diperluas.
POSITIVE_WORDS = {
"baik", "bagus", "hebat", "sukses", "untung", "naik", "tumbuh", "positif",
"menang", "juara", "prestasi", "maju", "berhasil", "setuju", "dukung",
"apresiasi", "optimis", "damai", "sehat", "aman", "puas", "senang",
"bangga", "harapan", "solusi", "peluang", "inovasi", "manfaat", "efektif",
}
NEGATIVE_WORDS = {
"buruk", "jelek", "gagal", "rugi", "turun", "anjlok", "negatif", "kalah",
"krisis", "masalah", "korupsi", "tewas", "meninggal", "bencana", "konflik",
"protes", "demo", "tolak", "kecam", "marah", "takut", "khawatir", "sakit",
"bahaya", "ancaman", "kritik", "lemah", "lambat", "mahal", "sulit", "rusak",
"kecewa", "tertekan", "korban", "darurat",
}
_pipeline = None
def _load_transformer():
global _pipeline
if _pipeline is None:
from transformers import pipeline
_pipeline = pipeline(
"text-classification",
model=settings.SENTIMENT_MODEL,
truncation=True,
max_length=512,
)
return _pipeline
def _normalize_label(label: str) -> str:
low = label.lower()
if "pos" in low:
return "positive"
if "neg" in low:
return "negative"
return "neutral"
def analyze_light(text: str) -> Dict:
tokens = [t.strip(".,!?;:\"'()[]").lower() for t in text.split()]
pos = sum(1 for t in tokens if t in POSITIVE_WORDS)
neg = sum(1 for t in tokens if t in NEGATIVE_WORDS)
total = pos + neg
if total == 0:
return {"sentiment": "neutral", "score": 0.0, "confidence": 0.5}
score = round((pos - neg) / total, 3)
if score > 0.15:
sentiment = "positive"
elif score < -0.15:
sentiment = "negative"
else:
sentiment = "neutral"
confidence = round(min(1.0, total / 10), 3)
return {"sentiment": sentiment, "score": score, "confidence": confidence}
def analyze_transformer(texts: List[str]) -> List[Dict]:
pipe = _load_transformer()
outputs = pipe(texts)
results = []
for out in outputs:
label = _normalize_label(out["label"])
conf = round(float(out["score"]), 3)
score = conf if label == "positive" else (-conf if label == "negative" else 0.0)
results.append({"sentiment": label, "score": round(score, 3), "confidence": conf})
return results
def analyze_batch(items: List) -> List[Dict]:
if settings.MODEL_MODE == "transformer":
try:
texts = [it.text[:2000] for it in items]
tf = analyze_transformer(texts)
return [{"id": it.id, **r} for it, r in zip(items, tf)]
except Exception:
# Fallback ke light bila transformer gagal load
pass
return [{"id": it.id, **analyze_light(it.text)} for it in items]