Lavender825 commited on
Commit
0ace5b0
·
1 Parent(s): 422d4ca

Fix HF model loading for meta tensor

Browse files
Files changed (2) hide show
  1. app.py +7 -6
  2. src/evaluator.py +46 -23
app.py CHANGED
@@ -303,11 +303,11 @@ def merchant_product_scores(metric: str) -> List[List[Any]]:
303
  for product in PRODUCTS:
304
  try:
305
  result = _predict_product(product["name"])
306
- except Exception:
307
- continue
308
  d = result.get("overall", {}) if metric == "Overall" else result.get("aspect_details", {}).get(metric, {})
309
  rows.append([product["name"], product["category"], metric, d.get("label", "Unknown"), round(_safe_float(d.get("confidence")), 4), product["price"], product["average_rating"]])
310
- return rows
311
 
312
 
313
  def _metadata_risks(features: str, categories: str, price: Any, rating: Any, count: Any) -> Dict[str, str]:
@@ -453,7 +453,7 @@ def build_app() -> gr.Blocks:
453
  with gr.Column(scale=6):
454
  gr.Markdown("### Product Score Monitor")
455
  merchant_metric = gr.Dropdown(["Overall"] + ASPECTS, value="Overall", label="Choose overall or aspect")
456
- merchant_scores = gr.Dataframe(headers=["Product", "Category", "Metric", "Prediction", "Confidence", "Price", "Rating"], datatype=["str", "str", "str", "str", "number", "number", "number"], interactive=False)
457
  refresh_scores = gr.Button("Refresh Product Scores", variant="primary")
458
  gr.Markdown("### New Product Metadata Risk Screening")
459
  with gr.Row():
@@ -466,7 +466,7 @@ def build_app() -> gr.Blocks:
466
  new_focus = gr.Dropdown(["All"] + ASPECTS, value="All", label="Focus aspect")
467
  screen_btn = gr.Button("Predict Metadata Risk", variant="primary")
468
  risk_summary = gr.HTML()
469
- risk_table = gr.Dataframe(headers=["Aspect", "Risk Level", "Model Signal", "Confidence", "Reason"], datatype=["str", "str", "str", "number", "str"], interactive=False)
470
  gr.Markdown("### External Review Prediction")
471
  external_review = gr.Textbox("The fabric is soft and the color looks good, but it runs small and the zipper feels weak.", label="External customer review", lines=4)
472
  with gr.Row():
@@ -480,7 +480,7 @@ def build_app() -> gr.Blocks:
480
  external_btn = gr.Button("Analyze External Review", variant="primary")
481
  ext_overall = gr.HTML()
482
  ext_aspects = gr.HTML()
483
- ext_table = gr.Dataframe(headers=["Aspect", "Prediction", "Confidence", "Key review evidence"], datatype=["str", "str", "number", "str"], interactive=False)
484
  refresh_scores.click(merchant_product_scores, merchant_metric, merchant_scores)
485
  merchant_metric.change(merchant_product_scores, merchant_metric, merchant_scores)
486
  screen_btn.click(screen_new_product, [new_features, new_categories, new_price, new_rating, new_count, new_focus], [risk_summary, risk_table])
@@ -507,3 +507,4 @@ demo = build_app()
507
 
508
  if __name__ == "__main__":
509
  demo.launch()
 
 
303
  for product in PRODUCTS:
304
  try:
305
  result = _predict_product(product["name"])
306
+ except Exception as exc:
307
+ return [["ERROR", "Model loading failed", metric, type(exc).__name__, 0.0, 0.0, 0.0]]
308
  d = result.get("overall", {}) if metric == "Overall" else result.get("aspect_details", {}).get(metric, {})
309
  rows.append([product["name"], product["category"], metric, d.get("label", "Unknown"), round(_safe_float(d.get("confidence")), 4), product["price"], product["average_rating"]])
310
+ return rows or [["No rows", "Try Refresh", metric, "-", 0.0, 0.0, 0.0]]
311
 
312
 
313
  def _metadata_risks(features: str, categories: str, price: Any, rating: Any, count: Any) -> Dict[str, str]:
 
453
  with gr.Column(scale=6):
454
  gr.Markdown("### Product Score Monitor")
455
  merchant_metric = gr.Dropdown(["Overall"] + ASPECTS, value="Overall", label="Choose overall or aspect")
456
+ merchant_scores = gr.Dataframe(headers=["Product", "Category", "Metric", "Prediction", "Confidence", "Price", "Rating"], value=[["Click Refresh Product Scores", "", "", "", 0.0, 0.0, 0.0]], datatype=["str", "str", "str", "str", "number", "number", "number"], interactive=False)
457
  refresh_scores = gr.Button("Refresh Product Scores", variant="primary")
458
  gr.Markdown("### New Product Metadata Risk Screening")
459
  with gr.Row():
 
466
  new_focus = gr.Dropdown(["All"] + ASPECTS, value="All", label="Focus aspect")
467
  screen_btn = gr.Button("Predict Metadata Risk", variant="primary")
468
  risk_summary = gr.HTML()
469
+ risk_table = gr.Dataframe(headers=["Aspect", "Risk Level", "Model Signal", "Confidence", "Reason"], value=[["Click Predict Metadata Risk", "", "", 0.0, ""]], datatype=["str", "str", "str", "number", "str"], interactive=False)
470
  gr.Markdown("### External Review Prediction")
471
  external_review = gr.Textbox("The fabric is soft and the color looks good, but it runs small and the zipper feels weak.", label="External customer review", lines=4)
472
  with gr.Row():
 
480
  external_btn = gr.Button("Analyze External Review", variant="primary")
481
  ext_overall = gr.HTML()
482
  ext_aspects = gr.HTML()
483
+ ext_table = gr.Dataframe(headers=["Aspect", "Prediction", "Confidence", "Key review evidence"], value=[["Click Analyze External Review", "", 0.0, ""]], datatype=["str", "str", "number", "str"], interactive=False)
484
  refresh_scores.click(merchant_product_scores, merchant_metric, merchant_scores)
485
  merchant_metric.change(merchant_product_scores, merchant_metric, merchant_scores)
486
  screen_btn.click(screen_new_product, [new_features, new_categories, new_price, new_rating, new_count, new_focus], [risk_summary, risk_table])
 
507
 
508
  if __name__ == "__main__":
509
  demo.launch()
510
+
src/evaluator.py CHANGED
@@ -1,4 +1,4 @@
1
- """Evaluation: per-aspect metrics, aggregated overall comparison.
2
 
3
  Two label encodings live in this project (keep them straight!):
4
  Per-aspect (ACSA): 0=Not_Mentioned, 1=Positive, 2=Negative
@@ -41,13 +41,31 @@ OVERALL_POSITIVE = 2
41
 
42
 
43
  # ---------------------------------------------------------------------------
44
- # Loaders (all use weights_only=False ?these are our own checkpoints)
45
  # ---------------------------------------------------------------------------
46
 
47
  def _load_ckpt(path: Path, device):
48
  return torch.load(path, map_location=device, weights_only=False)
49
 
50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  def load_meta_acsa(checkpoint_dir: Path = None, meta_encoder: Optional[MetaEncoder] = None,
52
  device=None):
53
  if checkpoint_dir is None:
@@ -71,10 +89,11 @@ def load_meta_acsa(checkpoint_dir: Path = None, meta_encoder: Optional[MetaEncod
71
  model = GatedAspectSemanticMetaFusionACSAModel(
72
  bert_name=bert_name,
73
  meta_in_dim=meta_in_dim,
74
- ).to(device)
75
  else:
76
- model = BertMetaFusionACSAModel(bert_name=bert_name, meta_in_dim=meta_in_dim).to(device)
77
- model.load_state_dict(ckpt["model_state_dict"], strict=False)
 
78
  model.eval()
79
  tokenizer = AutoTokenizer.from_pretrained(checkpoint_dir / "tokenizer")
80
  return model, tokenizer, meta_encoder, device
@@ -88,8 +107,9 @@ def load_acsa(checkpoint_dir: Path = None, device=None):
88
  device = get_device()
89
  ckpt = _load_ckpt(checkpoint_dir / "best.pt", device)
90
  bert_name = ckpt.get("config", {}).get("bert_name", cfg.BERT_MODEL_NAME)
91
- model = BertACSAModel(bert_name=bert_name).to(device)
92
- model.load_state_dict(ckpt["model_state_dict"])
 
93
  model.eval()
94
  tokenizer = AutoTokenizer.from_pretrained(checkpoint_dir / "tokenizer")
95
  return model, tokenizer, device
@@ -103,8 +123,9 @@ def load_bert_overall(checkpoint_dir: Path = None, device=None):
103
  device = get_device()
104
  ckpt = _load_ckpt(checkpoint_dir / "best.pt", device)
105
  bert_name = ckpt.get("config", {}).get("bert_name", cfg.BERT_MODEL_NAME)
106
- model = BertOverallModel(bert_name=bert_name).to(device)
107
- model.load_state_dict(ckpt["model_state_dict"])
 
108
  model.eval()
109
  tokenizer = AutoTokenizer.from_pretrained(checkpoint_dir / "tokenizer")
110
  return model, tokenizer, device
@@ -228,17 +249,17 @@ def overall_metrics(y_true, y_pred):
228
  # ---------------------------------------------------------------------------
229
 
230
  def aspect_to_overall_sentiment(aspect_preds) -> np.ndarray:
231
- """Improved voting rule for aggregating per-aspect ?overall.
232
 
233
  Rules (applied per review):
234
  1. Count n_pos and n_neg among MENTIONED aspects (skip Not_Mentioned).
235
- 2. If no aspect is mentioned at all ?NEUTRAL (conservative default).
236
- 3. If n_neg ?2 ?NEGATIVE (multiple negative aspects = clearly unhappy).
237
- 4. If n_neg == 1 and n_pos == 0 ?NEGATIVE.
238
- 5. If n_pos ?2 and n_neg == 0 ?POSITIVE.
239
- 6. If n_pos > n_neg (but not all positive) ?POSITIVE.
240
- 7. If n_neg > n_pos ?NEGATIVE.
241
- 8. Otherwise (tied, or only 1 pos and 0 neg) ?NEUTRAL.
242
 
243
  Input (per-aspect): 0=NM, 1=Pos, 2=Neg
244
  Output (overall): 0=Neg, 1=Neu, 2=Pos
@@ -253,16 +274,16 @@ def aspect_to_overall_sentiment(aspect_preds) -> np.ndarray:
253
 
254
  out = np.full(preds.shape[0], OVERALL_NEUTRAL, dtype=np.int64)
255
 
256
- # No aspects mentioned ?Neutral
257
- # n_neg ?2 ?Negative (strong signal)
258
  out[n_neg >= 2] = OVERALL_NEGATIVE
259
- # n_neg == 1, n_pos == 0 ?Negative
260
  out[(n_neg == 1) & (n_pos == 0)] = OVERALL_NEGATIVE
261
- # n_pos ?2, n_neg == 0 ?Positive (strong signal)
262
  out[(n_pos >= 2) & (n_neg == 0)] = OVERALL_POSITIVE
263
- # n_pos > n_neg and n_pos ?2 ?Positive
264
  out[(n_pos > n_neg) & (n_pos >= 2)] = OVERALL_POSITIVE
265
- # n_neg > n_pos ?Negative (override the ? positive if more negatives)
266
  out[n_neg > n_pos] = OVERALL_NEGATIVE
267
 
268
  return out
@@ -324,3 +345,5 @@ def format_aspect_summary(per_aspect_pred: np.ndarray) -> Dict[str, str]:
324
  for i, aspect in enumerate(cfg.ASPECTS)}
325
 
326
 
 
 
 
1
+ """Evaluation: per-aspect metrics, aggregated overall comparison.
2
 
3
  Two label encodings live in this project (keep them straight!):
4
  Per-aspect (ACSA): 0=Not_Mentioned, 1=Positive, 2=Negative
 
41
 
42
 
43
  # ---------------------------------------------------------------------------
44
+ # Loaders (all use weights_only=False ?these are our own checkpoints)
45
  # ---------------------------------------------------------------------------
46
 
47
  def _load_ckpt(path: Path, device):
48
  return torch.load(path, map_location=device, weights_only=False)
49
 
50
 
51
+ def _move_model_to_device(model, device):
52
+ """Move safely when Transformers creates meta tensors in Spaces."""
53
+ try:
54
+ return model.to(device)
55
+ except NotImplementedError as exc:
56
+ if "meta tensor" not in str(exc):
57
+ raise
58
+ logger.warning("Model contains meta tensors; using to_empty() before loading checkpoint weights.")
59
+ return model.to_empty(device=device)
60
+
61
+
62
+ def _load_model_state(model, state_dict, strict=True):
63
+ try:
64
+ return model.load_state_dict(state_dict, strict=strict, assign=True)
65
+ except TypeError:
66
+ return model.load_state_dict(state_dict, strict=strict)
67
+
68
+
69
  def load_meta_acsa(checkpoint_dir: Path = None, meta_encoder: Optional[MetaEncoder] = None,
70
  device=None):
71
  if checkpoint_dir is None:
 
89
  model = GatedAspectSemanticMetaFusionACSAModel(
90
  bert_name=bert_name,
91
  meta_in_dim=meta_in_dim,
92
+ )
93
  else:
94
+ model = BertMetaFusionACSAModel(bert_name=bert_name, meta_in_dim=meta_in_dim)
95
+ model = _move_model_to_device(model, device)
96
+ _load_model_state(model, ckpt["model_state_dict"], strict=False)
97
  model.eval()
98
  tokenizer = AutoTokenizer.from_pretrained(checkpoint_dir / "tokenizer")
99
  return model, tokenizer, meta_encoder, device
 
107
  device = get_device()
108
  ckpt = _load_ckpt(checkpoint_dir / "best.pt", device)
109
  bert_name = ckpt.get("config", {}).get("bert_name", cfg.BERT_MODEL_NAME)
110
+ model = BertACSAModel(bert_name=bert_name)
111
+ model = _move_model_to_device(model, device)
112
+ _load_model_state(model, ckpt["model_state_dict"])
113
  model.eval()
114
  tokenizer = AutoTokenizer.from_pretrained(checkpoint_dir / "tokenizer")
115
  return model, tokenizer, device
 
123
  device = get_device()
124
  ckpt = _load_ckpt(checkpoint_dir / "best.pt", device)
125
  bert_name = ckpt.get("config", {}).get("bert_name", cfg.BERT_MODEL_NAME)
126
+ model = BertOverallModel(bert_name=bert_name)
127
+ model = _move_model_to_device(model, device)
128
+ _load_model_state(model, ckpt["model_state_dict"])
129
  model.eval()
130
  tokenizer = AutoTokenizer.from_pretrained(checkpoint_dir / "tokenizer")
131
  return model, tokenizer, device
 
249
  # ---------------------------------------------------------------------------
250
 
251
  def aspect_to_overall_sentiment(aspect_preds) -> np.ndarray:
252
+ """Improved voting rule for aggregating per-aspect ?overall.
253
 
254
  Rules (applied per review):
255
  1. Count n_pos and n_neg among MENTIONED aspects (skip Not_Mentioned).
256
+ 2. If no aspect is mentioned at all ?NEUTRAL (conservative default).
257
+ 3. If n_neg ?2 ?NEGATIVE (multiple negative aspects = clearly unhappy).
258
+ 4. If n_neg == 1 and n_pos == 0 ?NEGATIVE.
259
+ 5. If n_pos ?2 and n_neg == 0 ?POSITIVE.
260
+ 6. If n_pos > n_neg (but not all positive) ?POSITIVE.
261
+ 7. If n_neg > n_pos ?NEGATIVE.
262
+ 8. Otherwise (tied, or only 1 pos and 0 neg) ?NEUTRAL.
263
 
264
  Input (per-aspect): 0=NM, 1=Pos, 2=Neg
265
  Output (overall): 0=Neg, 1=Neu, 2=Pos
 
274
 
275
  out = np.full(preds.shape[0], OVERALL_NEUTRAL, dtype=np.int64)
276
 
277
+ # No aspects mentioned ?Neutral
278
+ # n_neg ?2 ?Negative (strong signal)
279
  out[n_neg >= 2] = OVERALL_NEGATIVE
280
+ # n_neg == 1, n_pos == 0 ?Negative
281
  out[(n_neg == 1) & (n_pos == 0)] = OVERALL_NEGATIVE
282
+ # n_pos ?2, n_neg == 0 ?Positive (strong signal)
283
  out[(n_pos >= 2) & (n_neg == 0)] = OVERALL_POSITIVE
284
+ # n_pos > n_neg and n_pos ?2 ?Positive
285
  out[(n_pos > n_neg) & (n_pos >= 2)] = OVERALL_POSITIVE
286
+ # n_neg > n_pos ?Negative (override the ? positive if more negatives)
287
  out[n_neg > n_pos] = OVERALL_NEGATIVE
288
 
289
  return out
 
345
  for i, aspect in enumerate(cfg.ASPECTS)}
346
 
347
 
348
+
349
+