Spaces:
Sleeping
Sleeping
Download ml_service/app/services/explainer.py from Hardik-25/Sentiment-Analysis: direct link, hf CLI and curl.
- Browser
- Download file 2.83 kB
-
https://huggingface.co/spaces/Hardik-25/Sentiment-Analysis/resolve/main/ml_service/app/services/explainer.py
- Command line
-
hf download hf://spaces/Hardik-25/Sentiment-Analysis/ml_service/app/services/explainer.py
-
curl -L -o explainer.py https://huggingface.co/spaces/Hardik-25/Sentiment-Analysis/resolve/main/ml_service/app/services/explainer.py
2.83 kB
| import numpy as np | |
| from typing import List, Tuple | |
| def get_lr_important_words(text: str, vectorizer, model, top_n: int = 5) -> Tuple[List[str], List[str]]: | |
| """Extract top positive and negative words for LR prediction based on TF-IDF weights.""" | |
| if not vectorizer or not model: | |
| return [], [] | |
| try: | |
| # Transform the single document | |
| tfidf_vec = vectorizer.transform([text]) | |
| # Get feature names (vocabulary) | |
| feature_names = vectorizer.get_feature_names_out() | |
| # Get the non-zero feature indices and their tf-idf values in this document | |
| doc_indices = tfidf_vec.nonzero()[1] | |
| # Multiply by model coefficients to get importance | |
| # model.coef_[0] because it's a binary classifier | |
| importances = [(feature_names[idx], tfidf_vec[0, idx] * model.coef_[0][idx]) for idx in doc_indices] | |
| # Sort by importance | |
| importances.sort(key=lambda x: x[1]) | |
| # Deduplicate overlapping tokens (e.g. "movie" vs "movie ended") globally | |
| def deduplicate(word_score_pairs, n, exclude_words=None): | |
| if exclude_words is None: | |
| exclude_words = [] | |
| kept = [] | |
| for word, _ in word_score_pairs: | |
| is_dup = any((word in k or k in word) for k in kept + exclude_words) | |
| if not is_dup: | |
| kept.append(word) | |
| if len(kept) == n: | |
| break | |
| return kept | |
| # Top positive (highest positive values) | |
| pos_pairs = [(word, score) for word, score in reversed(importances) if score > 0] | |
| top_pos = deduplicate(pos_pairs, top_n) | |
| # Top negative (lowest negative values), excluding any already in positive | |
| neg_pairs = [(word, score) for word, score in importances if score < 0] | |
| top_neg = deduplicate(neg_pairs, top_n, exclude_words=top_pos) | |
| return top_pos, top_neg | |
| except Exception: | |
| return [], [] | |
| def generate_reasoning(model_name: str, confidence: float, latency: float) -> str: | |
| """Generate honest explanation for model behavior.""" | |
| conf_label = "high" if confidence >= 0.80 else "moderate" if confidence >= 0.60 else "low" | |
| if model_name == "lr": | |
| return f"TF-IDF representation evaluated independently. Captured {conf_label} confidence based on aggregated term weights." | |
| elif model_name == "lstm": | |
| return f"Bidirectional recurrent layers processed the sequence chronologically, retaining context window to yield {conf_label} confidence." | |
| elif model_name == "bert": | |
| return f"Transformer self-attention evaluated full bidirectional context across 12 layers, resulting in {conf_label} confidence." | |
| return "" | |