Spaces:
Sleeping
Sleeping
Commit ·
36d9d96
1
Parent(s): 20c0231
Add inference fallback for embedding errors
Browse files- app.py +2 -1
- src/inference.py +77 -4
app.py
CHANGED
|
@@ -453,7 +453,8 @@ def _overall_html(result: Dict[str, Any]) -> str:
|
|
| 453 |
for name in ["Negative", "Neutral", "Positive"]:
|
| 454 |
val = _safe_float(probs.get(name))
|
| 455 |
bars.append(f'<div class="prob-row"><span>{name}</span><div class="bar"><i style="width:{val * 100:.1f}%"></i></div><b>{val:.2f}</b></div>')
|
| 456 |
-
|
|
|
|
| 457 |
|
| 458 |
|
| 459 |
def _join_aspects(items: List[str]) -> str:
|
|
|
|
| 453 |
for name in ["Negative", "Neutral", "Positive"]:
|
| 454 |
val = _safe_float(probs.get(name))
|
| 455 |
bars.append(f'<div class="prob-row"><span>{name}</span><div class="bar"><i style="width:{val * 100:.1f}%"></i></div><b>{val:.2f}</b></div>')
|
| 456 |
+
fallback = '<div class="small-label">Fallback inference mode used for this sample.</div>' if result.get("fallback") else ""
|
| 457 |
+
return f'<div class="overall-card"><div class="small-label">Overall sentiment</div><div class="overall-main">{_chip(label)} <span class="conf">confidence {conf:.2f}</span></div>{"".join(bars)}{fallback}</div>'
|
| 458 |
|
| 459 |
|
| 460 |
def _join_aspects(items: List[str]) -> str:
|
src/inference.py
CHANGED
|
@@ -71,6 +71,8 @@ _SENTIMENT_EVIDENCE_KEYWORDS = {
|
|
| 71 |
"bad", "poor", "cheap", "terrible", "awful", "small", "large", "tight", "loose", "thin",
|
| 72 |
"worth", "disappointed", "return", "returned", "recommend", "flattering", "beautiful",
|
| 73 |
}
|
|
|
|
|
|
|
| 74 |
_STOPWORDS = {
|
| 75 |
"a", "an", "the", "and", "or", "but", "if", "then", "than", "so", "as", "at", "by", "for", "from",
|
| 76 |
"in", "into", "of", "on", "to", "with", "without", "is", "are", "was", "were", "be", "been", "being",
|
|
@@ -159,6 +161,65 @@ def _format_attention(out: Dict) -> Dict:
|
|
| 159 |
return result
|
| 160 |
|
| 161 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
def _slice_model_out(out: Dict, start: int, end: int) -> Dict:
|
| 163 |
sliced = {}
|
| 164 |
for k, v in out.items():
|
|
@@ -224,8 +285,11 @@ class AspectPredictor:
|
|
| 224 |
meta_df = _meta_dict_to_df(product_meta)
|
| 225 |
meta_vec = torch.from_numpy(self.meta_encoder.transform(meta_df)).float().to(self.device)
|
| 226 |
|
| 227 |
-
|
| 228 |
-
|
|
|
|
|
|
|
|
|
|
| 229 |
|
| 230 |
preds = out["logits"][0].argmax(dim=-1).cpu().numpy()
|
| 231 |
result = {
|
|
@@ -271,8 +335,17 @@ class AspectPredictor:
|
|
| 271 |
)
|
| 272 |
meta_vec = torch.from_numpy(self.meta_encoder.transform(meta_df)).float().to(self.device)
|
| 273 |
|
| 274 |
-
|
| 275 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 276 |
preds = out["logits"].argmax(dim=-1).cpu().numpy()
|
| 277 |
|
| 278 |
for j in range(len(chunk)):
|
|
|
|
| 71 |
"bad", "poor", "cheap", "terrible", "awful", "small", "large", "tight", "loose", "thin",
|
| 72 |
"worth", "disappointed", "return", "returned", "recommend", "flattering", "beautiful",
|
| 73 |
}
|
| 74 |
+
_POSITIVE_HINTS = {"good", "great", "love", "loved", "perfect", "nice", "excellent", "comfortable", "soft", "worth", "recommend", "flattering", "beautiful"}
|
| 75 |
+
_NEGATIVE_HINTS = {"bad", "poor", "cheap", "terrible", "awful", "small", "large", "tight", "loose", "thin", "disappointed", "return", "returned", "broke", "torn"}
|
| 76 |
_STOPWORDS = {
|
| 77 |
"a", "an", "the", "and", "or", "but", "if", "then", "than", "so", "as", "at", "by", "for", "from",
|
| 78 |
"in", "into", "of", "on", "to", "with", "without", "is", "are", "was", "were", "be", "been", "being",
|
|
|
|
| 161 |
return result
|
| 162 |
|
| 163 |
|
| 164 |
+
def _fallback_prediction(review_text: str, product_meta: Dict, reason: str = "") -> Dict:
|
| 165 |
+
evidence = _text_evidence_by_aspect(review_text)
|
| 166 |
+
aspect_details = {}
|
| 167 |
+
aspect_labels = {}
|
| 168 |
+
pos_total = 0
|
| 169 |
+
neg_total = 0
|
| 170 |
+
for aspect in cfg.ASPECTS:
|
| 171 |
+
terms = evidence.get(aspect, [])
|
| 172 |
+
pos_hits = [t for t in terms if t in _POSITIVE_HINTS]
|
| 173 |
+
neg_hits = [t for t in terms if t in _NEGATIVE_HINTS]
|
| 174 |
+
if neg_hits and len(neg_hits) >= len(pos_hits):
|
| 175 |
+
label = "Negative"
|
| 176 |
+
confidence = 0.62
|
| 177 |
+
neg_total += 1
|
| 178 |
+
elif pos_hits:
|
| 179 |
+
label = "Positive"
|
| 180 |
+
confidence = 0.62
|
| 181 |
+
pos_total += 1
|
| 182 |
+
elif terms:
|
| 183 |
+
label = "Not_Mentioned"
|
| 184 |
+
confidence = 0.55
|
| 185 |
+
else:
|
| 186 |
+
label = "Not_Mentioned"
|
| 187 |
+
confidence = 0.60
|
| 188 |
+
aspect_labels[aspect] = label
|
| 189 |
+
aspect_details[aspect] = {
|
| 190 |
+
"label": label,
|
| 191 |
+
"confidence": confidence,
|
| 192 |
+
"class_probs": {
|
| 193 |
+
"Not_Mentioned": 0.70 if label == "Not_Mentioned" else 0.20,
|
| 194 |
+
"Positive": confidence if label == "Positive" else 0.20,
|
| 195 |
+
"Negative": confidence if label == "Negative" else 0.20,
|
| 196 |
+
},
|
| 197 |
+
}
|
| 198 |
+
overall_label = "Negative" if neg_total > pos_total else "Positive" if pos_total > 0 else "Neutral"
|
| 199 |
+
return {
|
| 200 |
+
"aspects": aspect_labels,
|
| 201 |
+
"aspect_details": aspect_details,
|
| 202 |
+
"overall": {
|
| 203 |
+
"label": overall_label,
|
| 204 |
+
"confidence": 0.60,
|
| 205 |
+
"class_probs": {"Negative": 0.60 if overall_label == "Negative" else 0.20,
|
| 206 |
+
"Neutral": 0.60 if overall_label == "Neutral" else 0.20,
|
| 207 |
+
"Positive": 0.60 if overall_label == "Positive" else 0.20},
|
| 208 |
+
},
|
| 209 |
+
"metadata_summary": _meta_summary(product_meta),
|
| 210 |
+
"meta_attention_by_aspect": {
|
| 211 |
+
aspect: {"features": 0.50, "categories": 0.25, "numeric": 0.25}
|
| 212 |
+
for aspect in cfg.ASPECTS
|
| 213 |
+
},
|
| 214 |
+
"top_meta_source_by_aspect": {
|
| 215 |
+
aspect: {"source": "features", "weight": 0.50}
|
| 216 |
+
for aspect in cfg.ASPECTS
|
| 217 |
+
},
|
| 218 |
+
"fallback": True,
|
| 219 |
+
"fallback_reason": reason or "Model embedding index guard activated.",
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
|
| 223 |
def _slice_model_out(out: Dict, start: int, end: int) -> Dict:
|
| 224 |
sliced = {}
|
| 225 |
for k, v in out.items():
|
|
|
|
| 285 |
meta_df = _meta_dict_to_df(product_meta)
|
| 286 |
meta_vec = torch.from_numpy(self.meta_encoder.transform(meta_df)).float().to(self.device)
|
| 287 |
|
| 288 |
+
try:
|
| 289 |
+
with torch.no_grad():
|
| 290 |
+
out = self.model(input_ids, attn_mask, meta_vec)
|
| 291 |
+
except IndexError as exc:
|
| 292 |
+
return _fallback_prediction(review_text, product_meta, str(exc))
|
| 293 |
|
| 294 |
preds = out["logits"][0].argmax(dim=-1).cpu().numpy()
|
| 295 |
result = {
|
|
|
|
| 335 |
)
|
| 336 |
meta_vec = torch.from_numpy(self.meta_encoder.transform(meta_df)).float().to(self.device)
|
| 337 |
|
| 338 |
+
try:
|
| 339 |
+
with torch.no_grad():
|
| 340 |
+
out = self.model(input_ids, attn_mask, meta_vec)
|
| 341 |
+
except IndexError as exc:
|
| 342 |
+
for item in chunk:
|
| 343 |
+
results.append(_fallback_prediction(
|
| 344 |
+
item.get("review_text", ""),
|
| 345 |
+
item.get("product_meta", {}),
|
| 346 |
+
str(exc),
|
| 347 |
+
))
|
| 348 |
+
continue
|
| 349 |
preds = out["logits"].argmax(dim=-1).cpu().numpy()
|
| 350 |
|
| 351 |
for j in range(len(chunk)):
|