AbstractIntegratedModule 0.8.5__tar.gz → 0.8.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/AbstractIntegratedModule.egg-info/PKG-INFO +4 -3
  2. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/AbstractIntegratedModule.py +183 -68
  3. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/AbstractOptimizedModules.c +200 -200
  4. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/PKG-INFO +4 -3
  5. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/README.md +3 -2
  6. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/setup.py +1 -1
  7. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/AbstractIntegratedModule.egg-info/SOURCES.txt +0 -0
  8. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/AbstractIntegratedModule.egg-info/dependency_links.txt +0 -0
  9. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/AbstractIntegratedModule.egg-info/requires.txt +0 -0
  10. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/AbstractIntegratedModule.egg-info/top_level.txt +0 -0
  11. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/AbstractOptimizedModules.pyx +0 -0
  12. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/MANIFEST.in +0 -0
  13. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/abstract_model_storage/Cargo.toml +0 -0
  14. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/abstract_model_storage/pyproject.toml +0 -0
  15. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/abstract_model_storage/src/lib.rs +0 -0
  16. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/abstract_model_storage/target/debug/build/libsqlite3-sys-ed07b882cd2aa5e2/out/bindgen.rs +0 -0
  17. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/abstract_model_storage/target/debug/build/serde_core-ebc15f2e9cad7f5f/out/private.rs +0 -0
  18. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/abstract_model_storage/target/debug/build/target-lexicon-08527f45de28143d/out/host.rs +0 -0
  19. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/abstract_model_storage/target/release/build/libsqlite3-sys-bf0400df4523274c/out/bindgen.rs +0 -0
  20. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/abstract_model_storage/target/release/build/serde_core-5cdb76131825e4af/out/private.rs +0 -0
  21. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/abstract_model_storage/target/release/build/target-lexicon-43eb95a0588bf457/out/host.rs +0 -0
  22. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/pyproject.toml +0 -0
  23. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AbstractIntegratedModule
3
- Version: 0.8.5
3
+ Version: 0.8.6
4
4
  Summary: Library for Advanced Integrated Non-LLM AI Models - Optimized Backend Framework For Non-LLM AI Agent
5
5
  Author: Micro-Novelty
6
6
  Author-email: hernikpuspita5@gmail.com
@@ -42,7 +42,7 @@ https://github.com/Micro-Novelty/IntegratedPipeline-Specialized-Non-LLM-AI-Agent
42
42
  #### Note: The README here you are reading is a direct copy from my README Repository, to download the necessary files, you can visit my Repository with the provided link above.
43
43
 
44
44
  ### Library Short Description:
45
- - Development Stage: 0.8.5 Official Release.
45
+ - Development Stage: 0.8.6 Official Release.
46
46
  - Maintainer: Micro-Novelty.
47
47
  - library Source-Code is Open-sourced on github.
48
48
  - Purpose: Specifically Designed for providing Non-LLM AI Agent Framework for edge Devices, Optimized for ARM64 architecture.
@@ -80,9 +80,10 @@ https://github.com/Micro-Novelty/IntegratedPipeline-Specialized-Non-LLM-AI-Agent
80
80
  - Transformer Optimized using Cython, to reduce Memory overhead and Reduce CPU Usage, With Reduced Training Time.
81
81
  ______________
82
82
  - Changelog:
83
- - v0.8.5:
83
+ - v0.8.6:
84
84
  - [=] New features:
85
85
  - Added new Optimization module using Cython maths for dynamic weighted ensemble function.
86
+ - Added Penalty for wrong prediction in both single advanced prediction and advanced batch prediction to reduce repetitive errors.
86
87
  - Fixed P2P Vulnerabilities:
87
88
  - Empty allowed_ips in PRODUCTION/HARDENED mode now DENIES external IPs instead of allowing all
88
89
  - Startup validation warns about dangerous config combinations
@@ -8544,6 +8544,7 @@ class IntegratedPipeline:
8544
8544
  self.temperature = 1.0
8545
8545
  self.transformer_lr = 0.1
8546
8546
  self.max_seq_len = 16
8547
+ self.error_decay = 0.85
8547
8548
 
8548
8549
  self.memory_name = memory_name
8549
8550
 
@@ -13517,41 +13518,51 @@ class PipelineAsyncManager:
13517
13518
  self._save_state()
13518
13519
 
13519
13520
 
13520
- def advanced_batch_prediction(self, test_titles, label_map, rules, X=None, y=None, api_key=None, client_ip=None):
13521
+ def advanced_batch_prediction(self, test_titles, label_map, rules,
13522
+ X=None, y=None, api_key=None, client_ip=None):
13521
13523
  try:
13522
- attn_weights = None
13523
- final_idx = None
13524
+ attn_weights = None
13525
+ final_idx = None
13524
13526
  attn_weight_rate = None
13525
- final_idx = None
13527
+ confidence = None
13528
+ chosen_label = None
13526
13529
 
13527
13530
  reverse_label_map = {v: k for k, v in label_map.items()}
13531
+ n_classes = len(label_map)
13528
13532
 
13529
13533
  if 'attn_weights' in self.pipeline.model2.cache:
13530
13534
  attn_weights = self.pipeline.model2.cache['attn_weights']
13531
13535
  if attn_weights is not None:
13532
13536
  attn_weight_growth = 1.0 / (1.0 + np.exp(-attn_weights[0]))
13533
- attn_weight_rate = np.std(attn_weight_growth)
13537
+ attn_weight_rate = float(np.std(attn_weight_growth))
13534
13538
 
13535
- # Batch predictions
13536
- texts = [text[0] for text in test_titles]
13537
- expected_labels = [text[1] for text in test_titles] # Your ground truth
13539
+ texts = [text[0] for text in test_titles]
13540
+ expected_labels = [text[1] for text in test_titles]
13538
13541
  predicted_output = []
13539
13542
 
13540
- text = {"test_titles": test_titles, "label_map": label_map, "rules": rules, "X":X, "y":y, "use_transformer": True}
13543
+ text_payload = {
13544
+ "test_titles" : test_titles,
13545
+ "label_map" : label_map,
13546
+ "rules" : rules,
13547
+ "X" : X,
13548
+ "y" : y,
13549
+ "use_transformer": True
13550
+ }
13541
13551
 
13542
13552
  if not self.pipeline.intents:
13543
- result = self.predict(
13544
- text,
13545
- timeout=self.pipeline.timeout,
13546
- retries=None,
13547
- api_key=api_key,
13548
- client_ip=client_ip,
13549
- ) # regular predict to populate pipelines data
13553
+ result = self.predict(
13554
+ text_payload,
13555
+ timeout=self.pipeline.timeout,
13556
+ retries=None,
13557
+ api_key=api_key,
13558
+ client_ip=client_ip,
13559
+ )
13560
+ confidence = None # no confidence from this path
13550
13561
  else:
13551
13562
  result, chosen_label, confidence = self.advanced_prediction_method(
13552
- self.prediction_manager, test_titles, label_map, rules,
13553
- X=X, y=y, method='Transformer_included')
13554
-
13563
+ self.prediction_manager, test_titles, label_map, rules,
13564
+ X=X, y=y, method='Transformer_included'
13565
+ )
13555
13566
  try:
13556
13567
  if isinstance(result, list) and len(result) > 0:
13557
13568
  final_idx = result[0].get('index')
@@ -13560,62 +13571,107 @@ class PipelineAsyncManager:
13560
13571
  except (KeyError, IndexError):
13561
13572
  final_idx = None
13562
13573
 
13563
- print(f'[=+=] Local single advanced Prediction result: {result}')
13574
+ print(f'[=+=] Local single advanced prediction: {result}')
13575
+ print('[=] Initiating batch prediction...')
13564
13576
 
13565
- print('[=] Initiating Batch prediction for multiple texts...')
13566
13577
  results = self.predict_batch(
13567
13578
  texts=texts,
13568
13579
  timeout=self.pipeline.timeout,
13569
- api_key=api_key)
13580
+ api_key=api_key
13581
+ )
13582
+
13583
+ # class reputation track: how many times each class
13584
+ # was predicted wrong vs total predictions so far
13585
+ # error_rate[c] ∈ [0,1] — higher = this class is unreliable recently
13586
+ error_counts = np.zeros(n_classes, dtype=np.float64)
13587
+ pred_counts = np.zeros(n_classes, dtype=np.float64)
13588
+ decay = self.pipeline.error_decay # how fast old errors fade — tunable
13570
13589
 
13571
13590
  print("========📊 PREDICTION RESULTS============")
13572
-
13573
- for text, expected, probs in zip(texts, expected_labels, results):
13574
- calibrated_probs = probs.copy()
13575
- if confidence is not None and attn_weight_rate is not None and final_idx:
13576
- if final_idx <= len(probs):
13577
- try:
13578
- calibrated_index = probs[final_idx] + (1.0 - attn_weight_rate) * confidence
13579
- calibrated_probs[final_idx] = calibrated_index
13580
- except IndexError:
13581
- calibrated_probs = probs + (1.0 - attn_weight_rate) * confidence
13591
+
13592
+ for idx, (text, expected, probs) in enumerate(
13593
+ zip(texts, expected_labels, results)
13594
+ ):
13595
+ probs = np.asarray(probs, dtype=np.float64).copy()
13596
+
13597
+ # ── calibrate with single-prediction context ──────────────
13598
+ if (confidence is not None and
13599
+ attn_weight_rate is not None and
13600
+ final_idx is not None):
13601
+
13602
+ if 0 <= final_idx < len(probs):
13603
+ boost = (1.0 - attn_weight_rate) * confidence
13604
+ probs[final_idx] = min(probs[final_idx] + boost, 0.95)
13582
13605
  else:
13583
- try:
13584
- calibrated_probs = probs + (1.0 - attn_weight_rate) * confidence
13585
- except:
13586
- calibrated_probs = calibrated_probs.copy()
13606
+ boost = (1.0 - attn_weight_rate) * confidence
13607
+ probs = np.clip(probs + boost, 0.0, 0.95)
13608
+
13609
+ # ── apply class reputation penalty ────────────────────────
13610
+ # ounded reputation-based penalty
13611
+ # classes with high recent error rate get their probs dampened
13612
+ for c in range(n_classes):
13613
+ if pred_counts[c] > 0:
13614
+ error_rate = error_counts[c] / (pred_counts[c] + 1e-8)
13615
+ # sigmoid-shaped dampening — never goes negative
13616
+ # error_rate=0.0 → multiplier=1.0 (no change)
13617
+ # error_rate=0.5 → multiplier≈0.67
13618
+ # error_rate=1.0 → multiplier≈0.5
13619
+ reputation = 1.0 / (1.0 + error_rate)
13620
+ probs[c] *= reputation
13621
+
13622
+ # renormalize after reputation dampening
13623
+ prob_sum = probs.sum()
13624
+ if prob_sum > 1e-8:
13625
+ probs /= prob_sum
13626
+
13627
+ predicted_index = int(np.argmax(probs))
13628
+ predicted_label = reverse_label_map.get(
13629
+ predicted_index, f"class_{predicted_index}"
13630
+ )
13631
+ pred_conf = float(probs[predicted_index])
13632
+
13633
+ top_3_indices = np.argsort(probs)[-3:][::-1]
13634
+ top_3 = [
13635
+ (reverse_label_map.get(int(i), f"class_{i}"), float(probs[i]))
13636
+ for i in top_3_indices
13637
+ ]
13587
13638
 
13588
- predicted_index = np.argmax(calibrated_probs) # Get index with highest probability
13589
- predicted_label = reverse_label_map.get(predicted_index, f"class_{predicted_index}")
13590
- confidence = calibrated_probs[predicted_index]
13591
-
13592
- # Get top 3 predictions for more insight
13593
- top_3_indices = np.argsort(calibrated_probs)[-3:][::-1]
13594
- top_3 = [(reverse_label_map.get(idx, f"class_{idx}"), calibrated_probs[idx]) for idx in top_3_indices]
13595
-
13596
13639
  print(f"\n📌 Input: '{text}'")
13597
13640
  print(f" [=] Expected: {expected}")
13598
- print(f" 🎯 Predicted: {predicted_label} ({confidence:.1%})")
13599
-
13600
- # Show top 3 possibilities
13641
+ print(f" 🎯 Predicted: {predicted_label} ({pred_conf:.1%})")
13601
13642
  print(f" 🔍 Top possibilities:")
13602
13643
  for label, conf in top_3:
13603
13644
  bar = '█' * int(conf * 20)
13604
13645
  print(f"[•] {label:<25} {bar} {conf:.1%}")
13605
-
13606
- # Match expected vs predicted
13646
+
13607
13647
  print('===== COMPARISON MATCHING =====')
13608
- if predicted_label == expected:
13609
- print(f"[✅] CORRECT LABEL!")
13648
+ is_correct = predicted_label == expected
13649
+
13650
+ # update class reputation — decay old counts first..
13651
+ error_counts *= decay
13652
+ pred_counts *= decay
13653
+
13654
+ pred_counts[predicted_index] += 1.0
13655
+ if not is_correct:
13656
+ print(f"[❌] INCORRECT (expected: {expected})")
13657
+ error_counts[predicted_index] += 1.0
13610
13658
  else:
13611
- print(f"[] INCORRECT LABEL (expected: {expected})")
13659
+ print(f"[] CORRECT!")
13612
13660
 
13613
- predicted_output.append(f'{text} -> {predicted_label} With {confidence:.1%} Confidence')
13661
+ predicted_output.append(
13662
+ f'{text} -> {predicted_label} With {pred_conf:.1%} Confidence'
13663
+ )
13614
13664
 
13615
- # Get stats
13665
+ # batch accuracy summary
13666
+ correct = sum(
13667
+ 1 for text, exp in zip(texts, expected_labels)
13668
+ for po in [predicted_output]
13669
+ if f'-> {exp}' in po
13670
+ )
13671
+ print(f"\n[=] Batch complete")
13616
13672
  print(f"[=] Stats: {self.get_stats()}")
13617
- print('[=] Returning predicted output as list.')
13618
- return predicted_output
13673
+ print('[=] Returning predicted output.')
13674
+ return predicted_output
13619
13675
 
13620
13676
  except Exception as e:
13621
13677
  print(f'[=] Error in advanced batch prediction: {e}')
@@ -13891,7 +13947,14 @@ class PipelinePredictionManager:
13891
13947
  self.titles, self.y_raw, self.label_map = self.load_labels_from_csv(label_csv, target_title, label)
13892
13948
  except Exception as e:
13893
13949
  print(f"Error loading labels: {e}")
13894
- self.titles, self.y_raw, self.label_map = [], [], {}
13950
+ self.titles, self.y_raw, self.label_map = None, None, None
13951
+
13952
+ if self.label_map is not None:
13953
+ self.error_counts = np.zeros(len(self.label_map), dtype=np.float64)
13954
+ self.pred_counts = np.zeros(len(self.label_map), dtype=np.float64)
13955
+ self.decay = self.pipeline.error_decay # how fast old errors fade — tunable
13956
+ else:
13957
+ raise Warning('[!] Label map is None, consider adding label map!')
13895
13958
 
13896
13959
  print(f"✅ Loaded {len(self.titles)} labeled examples")
13897
13960
 
@@ -14726,7 +14789,8 @@ class PipelinePredictionManager:
14726
14789
  final_probs = mlp_weight * target_probs[i][:len(calibration)] + trans_weight * calibration[i][:len(calibration)] + lstm_weight * calibration[i][:len(calibration)]
14727
14790
  else:
14728
14791
  final_probs = mlp_weight * target_probs[i][:len(calibration)] + trans_weight * calibration[i][:len(calibration)]
14729
-
14792
+
14793
+ final_probs = self.calibration_penalized_check(final_probs, target_class_idx)
14730
14794
  final_class_idx = target_class_idx
14731
14795
  try:
14732
14796
  final_confidence = final_probs[final_class_idx]
@@ -14740,6 +14804,8 @@ class PipelinePredictionManager:
14740
14804
  agreement = mlp_class_idx == trans_class_idx
14741
14805
  else:
14742
14806
  final_probs = mlp_probs[i]
14807
+ final_probs = self.calibration_penalized_check(final_probs, mlp_class_idx)
14808
+
14743
14809
  final_class_idx = mlp_class_idx
14744
14810
  final_confidence = mlp_confidence[0] if isinstance(mlp_confidence, np.ndarray) else mlp_confidence
14745
14811
  if isinstance(final_confidence, np.ndarray) or isinstance(final_confidence, list):
@@ -14843,7 +14909,9 @@ class PipelinePredictionManager:
14843
14909
  print("\n[⚠️] Disagreement detected between MLP and Transformer predictions. Using calibrated probabilities for final decision.")
14844
14910
  if not self.pipeline.autonomous and need_peer_condition:
14845
14911
  print('|| Uncertain advanced prediction, requesting peer assistance if allowed...')
14846
- final_probs = self.pipeline._handle_distributed_connections(final_probs, attn_weights, input_ids, agreement)
14912
+ final_probs = self.pipeline._handle_distributed_connections(final_probs, attn_weights, input_ids, agreement)
14913
+
14914
+ final_probs = self.calibration_penalized_check(final_probs, final_class_idx)
14847
14915
  final_idx = final_probs[0].argmax()
14848
14916
  original_idx = final_idx
14849
14917
 
@@ -14865,8 +14933,10 @@ class PipelinePredictionManager:
14865
14933
 
14866
14934
  print('[||] Iniating local peer output search in database for best output...')
14867
14935
  final_probs = self.pipeline.distribution._handle_peer_agent_request(final_probs, attn_weights, input_ids, type='DevicePeer', agreement=agreement)
14936
+
14937
+ final_probs = self.calibration_penalized_check(final_probs, final_class_idx)
14868
14938
  final_idx = final_probs[0].argmax()
14869
- original_idx = final_idx
14939
+ original_idx = final_idx
14870
14940
 
14871
14941
  if final_idx > len(reverse_map):
14872
14942
  final_idx = int(np.argmax(final_probs[:len(reverse_map)-1]))
@@ -14884,9 +14954,11 @@ class PipelinePredictionManager:
14884
14954
  if final_confidence is not None and confidence < self.pipeline.confidence_threshold:
14885
14955
  print("\n[⚠️] Low confidence detected, but both models don't agree. Using calibrated probabilities for final decision to ensure robustness.")
14886
14956
  final_probs = self.pipeline.hybrid_prediction(rules, input_ids, dataset)
14957
+
14958
+ final_probs = self.calibration_penalized_check(final_probs, final_class_idx)
14887
14959
  final_idx = final_probs[0].argmax()
14888
14960
  original_idx = final_idx
14889
-
14961
+
14890
14962
  if final_idx > len(reverse_map):
14891
14963
  final_idx = int(np.argmax(final_probs[:len(reverse_map)-1]))
14892
14964
  print(f"[⚠️] Clamping {final_idx} → {final_idx}")
@@ -14894,9 +14966,10 @@ class PipelinePredictionManager:
14894
14966
  else:
14895
14967
  print('[🎯] Stable confidence established, But both Models doesnt Agree, Re-evaluating...')
14896
14968
  final_probs = self.pipeline.hybrid_prediction(rules, input_ids, dataset)
14969
+
14970
+ final_probs = self.calibration_penalized_check(final_probs, final_class_idx)
14897
14971
  final_idx = final_probs[0].argmax()
14898
14972
  original_idx = final_idx
14899
-
14900
14973
  if final_idx > len(reverse_map):
14901
14974
  final_idx = int(np.argmax(final_probs[:len(reverse_map)-1]))
14902
14975
  print(f"[⚠️] Clamping {final_idx} → {final_idx}")
@@ -14912,6 +14985,8 @@ class PipelinePredictionManager:
14912
14985
  print("\n[⚠️] Uncertain confidence and disagreement detected. Using ensemble method for final decision.")
14913
14986
  input_forward = sequence_ids if sequence_ids is not None else input_ids
14914
14987
  final_probs, details = self.pipeline.ensemble.predict_ensemble(input_forward, X, y, method='dynamic', embedded=True)
14988
+
14989
+ final_probs = self.calibration_penalized_check(final_probs, final_class_idx)
14915
14990
  final_idx = final_probs[0].argmax()
14916
14991
  original_idx = final_idx
14917
14992
 
@@ -14927,8 +15002,9 @@ class PipelinePredictionManager:
14927
15002
  confidence = float(final_probs[0][len(reverse_map)-1]) if isinstance(final_probs[0], (float, int)) else 0.0
14928
15003
  else:
14929
15004
  input_forward = sequence_ids if sequence_ids is not None else input_ids
15005
+ final_probs = self.calibration_penalized_check(final_probs, final_class_idx)
14930
15006
  final_idx = final_probs[0].argmax() if final_probs is not None else target_probs[0].argmax()
14931
-
15007
+
14932
15008
  original_idx = final_idx
14933
15009
 
14934
15010
  if final_idx > len(reverse_map):
@@ -14952,6 +15028,7 @@ class PipelinePredictionManager:
14952
15028
  confidence = self.pipeline.confidence_threshold
14953
15029
 
14954
15030
  elif confidence < self.pipeline.confidence_threshold and not self.pipeline.agreement and not results[0].get('models_agree', True):
15031
+ final_probs = self.calibration_penalized_check(final_probs, final_class_idx)
14955
15032
  if trans_probs is not None:
14956
15033
  prob_entropy = self.calculate_entropy(final_probs)
14957
15034
  normalized_entropy = prob_entropy / np.log(prob_entropy.shape[-1]) if prob_entropy.shape[-1] > 1 else 0
@@ -14998,6 +15075,7 @@ class PipelinePredictionManager:
14998
15075
  final_probs = target_probs.copy()
14999
15076
  else:
15000
15077
  print("\n[🎯] Using initial Regular final prediction as final decision.")
15078
+ final_probs = self.calibration_penalized_check(final_probs, final_class_idx)
15001
15079
  final_idx = final_probs[0].argmax()
15002
15080
 
15003
15081
  if final_idx > len(reverse_map):
@@ -15010,7 +15088,8 @@ class PipelinePredictionManager:
15010
15088
  confidence = float(final_probs[0][final_idx])
15011
15089
  except:
15012
15090
  confidence = float(final_probs[0][len(reverse_map)-1]) if isinstance(final_probs[0], (float, int)) else 0.0
15013
-
15091
+
15092
+
15014
15093
  if isinstance(chosen_label, str) and chosen_label.startswith("unknown") or float(confidence) < self.pipeline.confidence_threshold:
15015
15094
  if chosen_label.startswith("unknown"):
15016
15095
  chosen_label = 'Unknown'
@@ -15022,6 +15101,7 @@ class PipelinePredictionManager:
15022
15101
 
15023
15102
  try:
15024
15103
  consecutive_probs = self.pipeline.distribution._handle_peer_agent_request(target_probs, attn_weights, input_ids, type='DevicePeer', agreement=agreement)
15104
+ consecutive_probs = self.calibration_penalized_check(consecutive_probs, final_class_idx)
15025
15105
  sec_final_idx = consecutive_probs[0].argmax()
15026
15106
 
15027
15107
  if sec_final_idx > len(reverse_map):
@@ -15084,7 +15164,7 @@ class PipelinePredictionManager:
15084
15164
  'X_samples': X,
15085
15165
  'input_ids': input_ids
15086
15166
  }
15087
- correct, sec_correct = self.display_hybrid_results(payload, results, top_k, verbose=True)
15167
+ correct, sec_correct = self.display_hybrid_results(payload, final_class_idx, results, top_k, verbose=True)
15088
15168
  if sec_chosen_label and sec_correct > correct:
15089
15169
  print(f'[⚡] Second Prediction: {sec_chosen_label} has higher accuracies, relying on: {sec_chosen_label} as final label.')
15090
15170
  chosen_label = sec_chosen_label # overrides previous chosen label if accuracy is higher
@@ -15097,18 +15177,48 @@ class PipelinePredictionManager:
15097
15177
  self.pipeline.cache.clear()
15098
15178
 
15099
15179
  return results, chosen_label, confidence
15180
+
15181
+
15182
+ def calibration_penalized_check(self, final_probs, predicted_index):
15183
+ # update class reputation.
15184
+ try:
15185
+ decay = self.pipeline.error_decay
15186
+ self.error_counts *= decay
15187
+ self.pred_counts *= decay
15188
+
15189
+ self.pred_counts[predicted_index] += 1.0
15190
+ n_classes = len(self.label_map)
15191
+
15192
+ for c in range(n_classes):
15193
+ if self.pred_counts[c] > 0:
15194
+ error_rate = self.error_counts[c] / (self.pred_counts[c] + 1e-8)
15195
+ # sigmoid-shaped dampening — never goes negative
15196
+ # error_rate=0.0 → multiplier=1.0 (no change)
15197
+ # error_rate=0.5 → multiplier≈0.67
15198
+ # error_rate=1.0 → multiplier≈0.5
15199
+ reputation = 1.0 / (1.0 + error_rate)
15200
+ final_probs[c] *= reputation
15201
+
15202
+ prob_sum = final_probs.sum()
15203
+ if prob_sum > 1e-8:
15204
+ final_probs /= prob_sum
15205
+
15206
+ except Exception as e:
15207
+ print(f'[!] Cant check and calibrate probs based on penalty due to: {e}')
15100
15208
 
15209
+ return final_probs
15210
+
15101
15211
 
15102
- def display_hybrid_results(self, payload, results, top_k=3, verbose=False):
15212
+ def display_hybrid_results(self, payload, predicted_index, results, top_k=3, verbose=False):
15103
15213
  print("\n" + "="*80)
15104
15214
  print("[🎯] == PREDICTION RESULTS == ")
15105
15215
  print("="*80)
15106
-
15216
+
15107
15217
  correct = 0
15108
15218
  sec_correct = 0
15109
15219
  total_with_expected = 0
15110
15220
  X_samples, input_ids = payload['X_samples'], payload['input_ids']
15111
-
15221
+
15112
15222
  for idx, result in enumerate(results):
15113
15223
  print(f"\n{idx+1}. 📌 '{result['title']}'")
15114
15224
 
@@ -15116,6 +15226,7 @@ class PipelinePredictionManager:
15116
15226
  total_with_expected += 1
15117
15227
  status = ": ✅" if result['predicted'] == result['expected'] else ": ❌"
15118
15228
  print(f"[=] First Expectation: {result['expected']} || Model Answer: {status}")
15229
+
15119
15230
  if 'sec_predicted' in result:
15120
15231
  sec_status = ": ✅" if result['sec_predicted'] == result['expected'] else ": ❌"
15121
15232
  print(f"[=] Second Expectation: {result['expected']} || Model Answer: {sec_status}")
@@ -15126,6 +15237,8 @@ class PipelinePredictionManager:
15126
15237
  source='automatic_verified')
15127
15238
 
15128
15239
  sec_correct += 1
15240
+ else:
15241
+ self.error_counts[predicted_index] += 1.0
15129
15242
 
15130
15243
  if result['predicted'] == result['expected']:
15131
15244
  if result['sec_index'] is None:
@@ -15137,6 +15250,8 @@ class PipelinePredictionManager:
15137
15250
  source='automatic_verified')
15138
15251
 
15139
15252
  correct += 1
15253
+ else:
15254
+ self.error_counts[predicted_index] += 1.0
15140
15255
 
15141
15256
  # Agreement indicator
15142
15257
  agree_symbol = "✓" if result.get('models_agree', True) else "⚠️"