somukandula commited on
Commit
82f3dc0
·
verified ·
1 Parent(s): bba0068

Upload phase2/error_analysis_loop.py

Browse files
Files changed (1) hide show
  1. phase2/error_analysis_loop.py +57 -45
phase2/error_analysis_loop.py CHANGED
@@ -1,21 +1,47 @@
1
  """
2
  Maskara Phase 2 Error Analysis Loop
3
  ====================================
4
- Reads evaluation_results.json, identifies failure patterns, and augments the
5
- dataset generator with targeted synthetic examples. Run after each training
6
  iteration.
 
 
 
7
  """
8
  import json
9
  import os
10
  from collections import Counter
11
 
12
  EVAL_RESULTS = "/app/eval_results/evaluation_results.json"
13
- OUTPUT_TEMPLATES = "/app/targeted_templates.json"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
 
16
  def main():
17
  if not os.path.exists(EVAL_RESULTS):
18
- print(f"No evaluation results found at {EVAL_RESULTS}. Run evaluate_maskara.py first.")
19
  return
20
 
21
  with open(EVAL_RESULTS) as f:
@@ -23,69 +49,55 @@ def main():
23
 
24
  report = {
25
  "iteration": 1,
 
 
 
26
  "action_items": [],
27
  }
28
 
 
 
29
  for split, metrics in results.items():
30
- print(f"\n=== {split} ===")
31
  ov = metrics["overall"]
 
 
32
  print(f"Overall P={ov['precision']:.4f} R={ov['recall']:.4f} F1={ov['f1']:.4f}")
33
 
34
- # Entity-level worst performers
35
  entity_f1 = {k: v["f1"] for k, v in metrics["entity_metrics"].items()}
36
- worst = sorted(entity_f1.items(), key=lambda x: x[1])[:5]
37
  print("Lowest entity F1:")
38
  for ent, f1 in worst:
39
  print(f" {ent}: {f1:.4f}")
40
- report["action_items"].append({
41
  "split": split,
42
  "entity": ent,
43
  "f1": f1,
44
- "action": "add targeted synthetic examples for this entity",
45
  })
46
 
47
- # Error categories
48
- print("Error categories:")
49
  for cat, count in metrics["error_categories"].items():
50
- print(f" {cat}: {count}")
51
- report["action_items"].append({
52
- "split": split,
53
- "category": cat,
54
- "count": count,
55
- "action": f"add synthetic examples addressing {cat}",
56
- })
57
 
58
- # Generate targeted template suggestions
59
- targeted_templates = {
60
- "formatting issue": [
61
- "Aadhaar: {AADHAAR} (dashes)",
62
- "PAN - {PAN_CARD}",
63
- "Mobile: ({PHONE})",
64
- ],
65
- "Hinglish phrasing": [
66
- "{PERSON_NAME} ka phone {PHONE} hai",
67
- "{PERSON_NAME} ko {EMAIL} par mail karo",
68
- "UPI {UPI_ID} par paisa bhejo",
69
- ],
70
- "noisy OCR / malformed": [
71
- "Name : {PERSON_NAME}\nAadhaar : {AADHAAR}",
72
- ],
73
- "multiline formatting": [
74
- "Address:\n{ADDRESS}",
75
- ],
76
- "context ambiguity": [
77
- "My name is {PERSON_NAME} and I am calling for {PERSON_NAME}",
78
- ],
79
- }
80
 
81
- with open(OUTPUT_TEMPLATES, "w") as f:
82
- json.dump(targeted_templates, f, indent=2)
 
 
83
 
84
- with open("/app/error_analysis_report.json", "w") as f:
85
  json.dump(report, f, indent=2, default=float)
86
 
87
- print(f"\nSaved targeted template suggestions to {OUTPUT_TEMPLATES}")
88
- print("Saved error analysis report to /app/error_analysis_report.json")
89
 
90
 
91
  if __name__ == "__main__":
 
1
  """
2
  Maskara Phase 2 Error Analysis Loop
3
  ====================================
4
+ Reads evaluation_results.json, identifies failure patterns, and produces an
5
+ actionable report plus targeted synthetic examples for the next training
6
  iteration.
7
+
8
+ Run on Modal after evaluate_maskara.py:
9
+ python phase2/error_analysis_loop.py
10
  """
11
  import json
12
  import os
13
  from collections import Counter
14
 
15
  EVAL_RESULTS = "/app/eval_results/evaluation_results.json"
16
+ REPORT_PATH = "/app/error_analysis_report.json"
17
+
18
+
19
+ def categorize_error(text: str, span: dict) -> str:
20
+ label = span["label"]
21
+ value = text[span["start"]:span["end"]]
22
+ if label in ["AADHAAR", "PAN_CARD", "PHONE", "CREDIT_CARD"]:
23
+ if any(c in value for c in " -:/"):
24
+ return "formatting issue"
25
+ if label == "ADDRESS" and "\n" in value:
26
+ return "multiline formatting"
27
+ if label == "PERSON_NAME" and any(w in text[:span["start"]].lower().split()[-3:] for w in ["nam", "name"]):
28
+ return "context ambiguity"
29
+ if label == "PHONE" and len("".join(c for c in value if c.isdigit())) < 10:
30
+ return "tokenizer limitation / short number"
31
+ if any(w in text.lower() for w in ["hai", "mera", "bhai", "karo"]):
32
+ return "Hinglish phrasing"
33
+ if label == "AADHAAR" and len(value.replace(" ", "").replace("-", "")) != 12:
34
+ return "noisy OCR / malformed"
35
+ if label in ["PASSWORD", "API_KEY"]:
36
+ return "credential context missing"
37
+ if label == "CREDIT_CARD" and len("".join(c for c in value if c.isdigit())) < 13:
38
+ return "masked or truncated card"
39
+ return "missing template / other"
40
 
41
 
42
  def main():
43
  if not os.path.exists(EVAL_RESULTS):
44
+ print(f"No evaluation results found at {EVAL_RESULTS}. Run phase2/evaluate_maskara.py first.")
45
  return
46
 
47
  with open(EVAL_RESULTS) as f:
 
49
 
50
  report = {
51
  "iteration": 1,
52
+ "overall_summary": {},
53
+ "weak_entities": [],
54
+ "error_categories": {},
55
  "action_items": [],
56
  }
57
 
58
+ all_categories = Counter()
59
+
60
  for split, metrics in results.items():
 
61
  ov = metrics["overall"]
62
+ report["overall_summary"][split] = ov
63
+ print(f"\n=== {split} ===")
64
  print(f"Overall P={ov['precision']:.4f} R={ov['recall']:.4f} F1={ov['f1']:.4f}")
65
 
 
66
  entity_f1 = {k: v["f1"] for k, v in metrics["entity_metrics"].items()}
67
+ worst = sorted(entity_f1.items(), key=lambda x: x[1])[:7]
68
  print("Lowest entity F1:")
69
  for ent, f1 in worst:
70
  print(f" {ent}: {f1:.4f}")
71
+ report["weak_entities"].append({
72
  "split": split,
73
  "entity": ent,
74
  "f1": f1,
75
+ "action": "add targeted synthetic examples for this entity (see targeted_augmentation.py)",
76
  })
77
 
 
 
78
  for cat, count in metrics["error_categories"].items():
79
+ all_categories[cat] += count
 
 
 
 
 
 
80
 
81
+ for ex in metrics.get("sample_errors", [])[:10]:
82
+ for span in ex["false_positives"] + ex["false_negatives"]:
83
+ report["action_items"].append({
84
+ "split": split,
85
+ "entity": span["label"],
86
+ "category": categorize_error(ex["text"], span),
87
+ "text_snippet": ex["text"][:120].replace("\n", " "),
88
+ "span": span,
89
+ })
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
+ report["error_categories"] = dict(all_categories.most_common(20))
92
+ print("\nCombined error categories:")
93
+ for cat, count in all_categories.most_common(20):
94
+ print(f" {cat}: {count}")
95
 
96
+ with open(REPORT_PATH, "w") as f:
97
  json.dump(report, f, indent=2, default=float)
98
 
99
+ print(f"\nSaved error analysis report to {REPORT_PATH}")
100
+ print("Next step: update phase2/targeted_augmentation.py and run phase2/retrain_with_augmentation.py")
101
 
102
 
103
  if __name__ == "__main__":