quantmacro-india / src /utils /sentiment.py
kingbros919's picture
Upload folder using huggingface_hub
292b308 verified
Raw
History Blame Contribute Delete
3.43 kB
import os
import logging
import nltk
from nltk.sentiment.vader import SentimentIntensityAnalyzer
logger = logging.getLogger(__name__)
# Ensure VADER lexicon is downloaded
try:
nltk.data.find('sentiment/vader_lexicon.zip')
except LookupError:
nltk.download('vader_lexicon', quiet=True)
class SentimentAnalyzer:
def __init__(self, use_finbert=True, model_name="ProsusAI/finbert", cache_dir="./models"):
self.use_finbert = use_finbert
self.model_name = model_name
self.cache_dir = cache_dir
self.finbert_pipeline = None
self.vader_analyzer = SentimentIntensityAnalyzer()
if self.use_finbert:
try:
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification, pipeline
device = 0 if torch.cuda.is_available() else -1
device_name = torch.cuda.get_device_name(0) if device == 0 else "CPU"
logger.info(f"Initializing FinBERT ({model_name}) on {device_name} (Cache: {cache_dir})...")
os.makedirs(cache_dir, exist_ok=True)
tokenizer = AutoTokenizer.from_pretrained(model_name, cache_dir=cache_dir)
model = AutoModelForSequenceClassification.from_pretrained(model_name, cache_dir=cache_dir)
self.finbert_pipeline = pipeline(
"sentiment-analysis",
model=model,
tokenizer=tokenizer,
device=device
)
logger.info("FinBERT initialized successfully.")
except Exception as e:
logger.warning(f"Failed to load FinBERT: {e}. Falling back to VADER sentiment.")
self.use_finbert = False
def analyze(self, text: str) -> tuple:
"""
Analyzes the text and returns (sentiment_label, sentiment_score).
sentiment_score ranges from -1.0 (most negative) to 1.0 (most positive).
"""
if not text or not text.strip():
return "neutral", 0.0
if self.use_finbert and self.finbert_pipeline:
try:
# Run batch-friendly inference on truncated text (max 512 tokens)
result = self.finbert_pipeline(text[:2000])[0]
label = result['label'].lower() # 'positive', 'negative', or 'neutral'
score = result['score'] # Confidence probability [0, 1]
# Map probability score to a -1 to +1 range for comparison metrics
if label == 'positive':
mapped_score = score
elif label == 'negative':
mapped_score = -score
else:
mapped_score = 0.0
return label, mapped_score
except Exception as e:
logger.error(f"FinBERT inference error: {e}. Falling back to VADER.")
# VADER Fallback
scores = self.vader_analyzer.polarity_scores(text)
compound = scores['compound']
if compound >= 0.05:
label = "positive"
elif compound <= -0.05:
label = "negative"
else:
label = "neutral"
return label, compound