Lavender825 commited on
Commit
36d9d96
·
1 Parent(s): 20c0231

Add inference fallback for embedding errors

Browse files
Files changed (2) hide show
  1. app.py +2 -1
  2. 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
- 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)}</div>'
 
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
- with torch.no_grad():
228
- out = self.model(input_ids, attn_mask, meta_vec)
 
 
 
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
- with torch.no_grad():
275
- out = self.model(input_ids, attn_mask, meta_vec)
 
 
 
 
 
 
 
 
 
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)):