AbstractIntegratedModule 0.8.8__tar.gz → 0.8.9__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.8 → abstractintegratedmodule-0.8.9}/AbstractIntegratedModule.egg-info/PKG-INFO +6 -14
  2. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/AbstractIntegratedModule.py +189 -48
  3. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/AbstractOptimizedModules.c +533 -453
  4. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/PKG-INFO +6 -14
  5. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/README.md +5 -13
  6. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/setup.py +1 -1
  7. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/AbstractIntegratedModule.egg-info/SOURCES.txt +0 -0
  8. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/AbstractIntegratedModule.egg-info/dependency_links.txt +0 -0
  9. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/AbstractIntegratedModule.egg-info/requires.txt +0 -0
  10. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/AbstractIntegratedModule.egg-info/top_level.txt +0 -0
  11. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/AbstractOptimizedModules.pyx +0 -0
  12. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/MANIFEST.in +0 -0
  13. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/abstract_model_storage/Cargo.toml +0 -0
  14. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/abstract_model_storage/pyproject.toml +0 -0
  15. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/abstract_model_storage/src/lib.rs +0 -0
  16. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/abstract_model_storage/target/debug/build/libsqlite3-sys-ed07b882cd2aa5e2/out/bindgen.rs +0 -0
  17. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/abstract_model_storage/target/debug/build/serde_core-ebc15f2e9cad7f5f/out/private.rs +0 -0
  18. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/abstract_model_storage/target/debug/build/target-lexicon-08527f45de28143d/out/host.rs +0 -0
  19. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/abstract_model_storage/target/release/build/libsqlite3-sys-bf0400df4523274c/out/bindgen.rs +0 -0
  20. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/abstract_model_storage/target/release/build/serde_core-5cdb76131825e4af/out/private.rs +0 -0
  21. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/abstract_model_storage/target/release/build/target-lexicon-43eb95a0588bf457/out/host.rs +0 -0
  22. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/pyproject.toml +0 -0
  23. {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.8.9}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AbstractIntegratedModule
3
- Version: 0.8.8
3
+ Version: 0.8.9
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.8 Official Release.
45
+ - Development Stage: 0.8.9 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,19 +80,11 @@ 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.8:
83
+ - v0.8.9:
84
84
  - [=] New features:
85
- - Added new continuos predictive correction for MLP class to reduce inflated confidence despite outputting wrong answer.
86
- - Reduced the frequency of penalty based calibration in advanced prediction method to prevent the models from being too conservative.
87
- - Fixed Aggressive Memory deletion capability bug to prevent Careless memory deletion because of tuple type variables wrapping the probability after its saved to the memory.
88
- - added robustness for Memory corruption check and sanity.
89
- - Fixed P2P Vulnerabilities:
90
- - Empty allowed_ips in PRODUCTION/HARDENED mode now DENIES external IPs instead of allowing all
91
- - Startup validation warns about dangerous config combinations
92
- - Loopback (127.0.0.1) always permitted for local agent comms
93
- - DEVELOPMENT/STAGING retains allow-all behavior with explicit warning since local P2P testing requires it
94
- - pickle.loads() replaced by json.loads with utf-8 encoding order.
95
- - generate self signed cert fixes and added client.crt for fallback when users dont provide any SSL cert and key for both server and client.
85
+ - Ensemble method prediction is now secondary after conditions are met, and no longers acts as the Main prediction flow, Single pass (One Model) prediction now governs the Main prediction for long term stability and determinism in its learning capability.
86
+ - Modifed advanced prediction to use single pass prediction as its main prediction method, ensemble prediction will be triggered when conditions are met.
87
+ - Modifed advanced batch prediction to use single pass prediction for faster and more reliable prediction method.
96
88
 
97
89
  ____________
98
90
  <img width="1280" height="600" alt="WhatsApp Image 2026-05-27 at 07 16 32" src="https://github.com/user-attachments/assets/4b58a556-45a3-419b-96fd-9c1b76cac574" />
@@ -2696,6 +2696,10 @@ class WeightedEnsemblePredictor:
2696
2696
  self.memory_name = memory_name
2697
2697
  self.db_path = 'activity_log.db'
2698
2698
 
2699
+ self.error_counts = None
2700
+ self.pred_counts = None
2701
+ self.error_decay = 0.85
2702
+
2699
2703
  self.self_attn_weights = None
2700
2704
 
2701
2705
  if not self.storage.memory_exists(self.memory_name, type='Transformer'):
@@ -2908,6 +2912,9 @@ class WeightedEnsemblePredictor:
2908
2912
  'explanation': explanation,
2909
2913
  'details': result
2910
2914
  }
2915
+
2916
+
2917
+
2911
2918
 
2912
2919
 
2913
2920
  def explainability_prediction_batch(self, texts, mlp_probs, trans_probs, attn_weights, show_explanation=False):
@@ -3059,6 +3066,8 @@ class WeightedEnsemblePredictor:
3059
3066
  anisotropy = self.pipeline.confidence_threshold
3060
3067
 
3061
3068
  return anisotropy
3069
+
3070
+
3062
3071
 
3063
3072
  def _dynamic_weighted_ensemble(self, trans_probs, mlp_probs, attn_weights,
3064
3073
  input_ids, lstm_probs=None, lstm_weight_hint=0.0):
@@ -8517,6 +8526,7 @@ class IntegratedPipeline:
8517
8526
  self.dropout_rate = 0.1
8518
8527
  self.transformer_training_epochs = 100
8519
8528
  self.max_size = 500
8529
+ self.error_decay = 0.85
8520
8530
 
8521
8531
  # Main component setup
8522
8532
  self.standard_scaler = StandardScaler()
@@ -8583,7 +8593,6 @@ class IntegratedPipeline:
8583
8593
  self.temperature = 1.0
8584
8594
  self.transformer_lr = 0.1
8585
8595
  self.max_seq_len = 16
8586
- self.error_decay = 0.85
8587
8596
 
8588
8597
  self.memory_name = memory_name
8589
8598
 
@@ -8612,16 +8621,17 @@ class IntegratedPipeline:
8612
8621
  print(f' Suggestion: {suggested_fix}')
8613
8622
 
8614
8623
  # Auto-fix based on severity
8615
- if '133' in reason:
8616
- print(f'[!] Hybrid feature detected - clearing all memory entries')
8624
+ if 'deserialization' in reason.lower():
8625
+ print(f'[!] Deserialization error - resetting memory')
8626
+ self.storage.fix_corrupted_memory(memory_name)
8617
8627
  self.memory = {}
8618
- # Also clear from storage
8628
+ elif 'unexpected' in reason.lower():
8629
+ print(f'[!] Unexpected shape in memory - resetting memory')
8619
8630
  self.storage.fix_corrupted_memory(memory_name)
8620
- elif 'deserialization' in reason.lower():
8621
- print(f'[!] Deserialization error - resetting memory')
8622
8631
  self.memory = {}
8623
8632
  else:
8624
8633
  print(f'[!] Keeping memory but will validate on access')
8634
+
8625
8635
  else:
8626
8636
  print(f'[✅] Memory validation passed: {reason}')
8627
8637
 
@@ -8637,6 +8647,31 @@ class IntegratedPipeline:
8637
8647
  'failures': 0
8638
8648
  }
8639
8649
 
8650
+ def _validate_properties(self, memory):
8651
+ if isinstance(memory, (float, int)):
8652
+ print('[!] Memory is single scalar! returning 0.0 similarity')
8653
+ return None
8654
+
8655
+ if isinstance(memory, (str, np.str_)):
8656
+ clean_str = str(memory).replace('[', '').replace(']', '')
8657
+ memory = np.fromstring(clean_str, sep=' ')
8658
+ if isinstance(memory, np.ndarray) and np.issubdtype(memory.dtype, np.character):
8659
+ # catches arrays filled with string text
8660
+ clean_str = ' '.join(memory.astype(str).flatten()).replace('[', '').replace(']', '')
8661
+ memory = np.fromiter(
8662
+ (v for v in clean_str.split() if memory != "..."), dtype=float
8663
+ )
8664
+
8665
+ if isinstance(memory, list) and self.model3 is not None:
8666
+ if len(memory) == self._get_num_classes():
8667
+ return memory
8668
+ else:
8669
+ print('[!] Memory length is not tied with current number of classes! skipping Memory.')
8670
+ return None
8671
+
8672
+ return memory
8673
+
8674
+
8640
8675
  def is_memory_corrupted(self, memory, num_classes: int = None) -> tuple:
8641
8676
  """
8642
8677
  Robust memory corruption detection here.
@@ -8647,6 +8682,11 @@ class IntegratedPipeline:
8647
8682
  if num_classes is None:
8648
8683
  num_classes = self._get_num_classes() or 0
8649
8684
 
8685
+ # ___ Case 0: Checks for string based _______
8686
+ memory = self._validate_properties(memory)
8687
+ if memory is None:
8688
+ return None
8689
+
8650
8690
  # ── Case 1: None ──────────────────────────────────────────────
8651
8691
  if memory is None:
8652
8692
  return (True, "Memory is None", "Initialize new memory dict")
@@ -8673,6 +8713,7 @@ class IntegratedPipeline:
8673
8713
  if isinstance(memory, list):
8674
8714
  return self._validate_list_memory(memory, num_classes)
8675
8715
 
8716
+
8676
8717
  # ── Case 5: dict ──────────────────────────────────────────────
8677
8718
  if isinstance(memory, dict):
8678
8719
  return self._validate_dict_memory(memory, num_classes)
@@ -8731,6 +8772,7 @@ class IntegratedPipeline:
8731
8772
  f"Tuple string at position {element_idx} is "
8732
8773
  f"suspiciously long ({len(element)} chars)",
8733
8774
  "Clear this memory entry")
8775
+
8734
8776
 
8735
8777
  # numpy array elements — check shape sanity first
8736
8778
  elif isinstance(element, np.ndarray):
@@ -9262,6 +9304,8 @@ class IntegratedPipeline:
9262
9304
  traceback.print_exc()
9263
9305
  raise
9264
9306
 
9307
+
9308
+
9265
9309
 
9266
9310
  async def predict_batch_async(self, texts: List[str], callback=None):
9267
9311
  # Batch async predictions.
@@ -10117,16 +10161,38 @@ class IntegratedPipeline:
10117
10161
  return np.zeros((len(chunk_ids), 1)) # minimal safe fallback shape
10118
10162
 
10119
10163
  needs_fresh = [i for i, p in enumerate(chunk_probs) if p is None]
10120
-
10164
+ need_ensemble = (
10165
+ len(chunk_ids) > 100 and
10166
+ len(chunk_X) > 100
10167
+ )
10121
10168
  if needs_fresh:
10122
10169
  fresh_ids = chunk_ids[needs_fresh]
10123
10170
  fresh_X = chunk_X[needs_fresh]
10124
10171
 
10125
- fresh_probs, _ = self.ensemble.predict_ensemble(
10126
- fresh_ids, fresh_X,
10127
- np.zeros((len(fresh_ids), num_classes)),
10128
- method='dynamic', embedded=False
10129
- )
10172
+ if need_ensemble:
10173
+ fresh_probs, _ = self.ensemble.predict_ensemble(
10174
+ fresh_ids, fresh_X,
10175
+ np.zeros((len(fresh_ids), num_classes)),
10176
+ method='dynamic', embedded=False
10177
+ )
10178
+ else:
10179
+ fresh_probs = self.model3.forward(fresh_X)
10180
+ fresh_trans_probs, _ = self.model2.forward(fresh_ids, embedded=False)
10181
+ fresh_mlp_confidence = fresh_probs.max(axis=1, keepdims=True)
10182
+ fresh_trans_confidence = fresh_trans_probs.max(axis=1, keepdims=True)
10183
+
10184
+ if fresh_mlp_confidence >= fresh_trans_confidence:
10185
+ fresh_probs = fresh_probs.copy()
10186
+ else:
10187
+ fresh_probs = fresh_trans_probs.copy()
10188
+
10189
+ if np.std(fresh_probs) < 0.3 or np.mean(fresh_probs) < 0.3:
10190
+ fresh_probs, _ = self.ensemble.predict_ensemble(
10191
+ fresh_ids, fresh_X,
10192
+ np.zeros((len(fresh_ids), num_classes)),
10193
+ method='dynamic', embedded=False
10194
+ )
10195
+
10130
10196
 
10131
10197
  # validate fresh_probs shape before assignment
10132
10198
  fresh_probs = np.asarray(fresh_probs)
@@ -13377,6 +13443,11 @@ class PipelineAsyncManager:
13377
13443
  texts = texts[0]
13378
13444
 
13379
13445
  result = self._predict_sync(texts, timeout)
13446
+
13447
+ elif method == 'singlepass':
13448
+ if isinstance(texts, tuple):
13449
+ texts = texts[0]
13450
+ result = self._predict_single_sync(texts, timeout)
13380
13451
  else:
13381
13452
  if 'test_titles' in texts and 'label_map' in texts and 'rules' in texts and 'X' in texts and 'y' in texts:
13382
13453
  result = self._advanced_predict_sync(texts['test_titles'], texts['label_map'], texts['rules'], texts['X'], texts['y'], texts.get('agent_id', 'default'), texts.get('use_transformer', False), timeout)
@@ -13443,6 +13514,18 @@ class PipelineAsyncManager:
13443
13514
  finally:
13444
13515
  self._remove_task(task_id)
13445
13516
 
13517
+ async def _predict_with_timeout(self, text: Any, timeout: float) -> Any:
13518
+ # Async prediction with timeout.
13519
+ try:
13520
+ return await asyncio.wait_for(
13521
+ self.pipeline.predict_async_await(text),
13522
+ timeout=timeout
13523
+ )
13524
+ except asyncio.TimeoutError:
13525
+ raise FutureTimeoutError(f"[-] Prediction timed out after {timeout}s")
13526
+
13527
+
13528
+
13446
13529
  def _advanced_predict_sync(self, test_titles, label_map, rules, X=None, y=None, agent_id: str=None, use_transformer: bool=False, timeout: float = 30.0) -> Any:
13447
13530
  # Internal synchronous prediction.
13448
13531
 
@@ -13464,15 +13547,6 @@ class PipelineAsyncManager:
13464
13547
  finally:
13465
13548
  self._remove_task(task_id)
13466
13549
 
13467
- async def _predict_with_timeout(self, text: Any, timeout: float) -> Any:
13468
- # Async prediction with timeout.
13469
- try:
13470
- return await asyncio.wait_for(
13471
- self.pipeline.predict_async_await(text),
13472
- timeout=timeout
13473
- )
13474
- except asyncio.TimeoutError:
13475
- raise FutureTimeoutError(f"[-] Prediction timed out after {timeout}s")
13476
13550
 
13477
13551
  async def advanced_predict_async_await(self, test_titles: list[tuple], label_map: dict, rules: list[tuple], X: np.ndarray=None, y: np.ndarray=None, use_transformer: bool=False, agent_id: str=None, timeout: float = 30.0):
13478
13552
  # Async advanced prediction with await support.
@@ -13761,6 +13835,7 @@ class PipelineAsyncManager:
13761
13835
  api_key=api_key
13762
13836
  )
13763
13837
 
13838
+
13764
13839
  # class reputation track: how many times each class
13765
13840
  # was predicted wrong vs total predictions so far
13766
13841
  # error_rate[c] ∈ [0,1] — higher = this class is unreliable recently
@@ -13881,6 +13956,20 @@ class PipelineAsyncManager:
13881
13956
  results.append(result)
13882
13957
  return results
13883
13958
 
13959
+ def single_pass_predict_batch(self, texts, timeout, api_key=None):
13960
+ """
13961
+ Single-pass batch prediction without ensemble or advanced features.
13962
+ """
13963
+ if not self.is_running:
13964
+ if not self.start():
13965
+ raise RuntimeError("[-] Wrapper not running and failed to start")
13966
+
13967
+ results = []
13968
+ for text in texts:
13969
+ result = self.predict(text, timeout, api_key, method='singlepass')
13970
+ results.append(result)
13971
+ return results
13972
+
13884
13973
  def advanced_prediction_method(self, manager, test_titles, label_map, rules, X=None, y=None, method='Transformer_included'):
13885
13974
  # starting PredictionManager for advanced prediction
13886
13975
  try:
@@ -14768,6 +14857,10 @@ class PipelinePredictionManager:
14768
14857
  trans_probs = None
14769
14858
  attn_weights = None
14770
14859
  sequence_ids = None
14860
+ input_ids = None
14861
+ anisotropy = None
14862
+ final_class_idx = None
14863
+ AME = None
14771
14864
 
14772
14865
  print("\n[🚀] Starting Advanced Hybrid Prediction Method")
14773
14866
 
@@ -14815,6 +14908,8 @@ class PipelinePredictionManager:
14815
14908
  else:
14816
14909
  print("\n[⚡] Running MLP-only predictions")
14817
14910
  print("[⚡] Note: Transformer not available, so Transformer results will be replaced with MLP results.")
14911
+ anisotropy = self.pipeline.anisotropy_measurement(X) if X is not None else self.pipeline.confidence_threshold
14912
+ AME = self.pipeline.model2.AME_Encoder(X) if X is not None else self.pipeline.confidence_threshold
14818
14913
 
14819
14914
  if X is None or len(X) == 0 or isinstance(X, int) or (isinstance(X, np.ndarray) and X.size == 0):
14820
14915
  # Get MLP predictions
@@ -14893,6 +14988,13 @@ class PipelinePredictionManager:
14893
14988
  else:
14894
14989
  lstm_probs = None
14895
14990
 
14991
+ need_ensemble_method = (
14992
+ anisotropy > 0.3 and
14993
+ AME is not None and
14994
+ AME > 0.3 and
14995
+ np.mean(self.error_counts) > 0.3
14996
+ )
14997
+
14896
14998
  results = []
14897
14999
  attention_data = [] if return_attention else None
14898
15000
 
@@ -14948,33 +15050,72 @@ class PipelinePredictionManager:
14948
15050
 
14949
15051
  trans_label = reverse_map.get(trans_class_idx, f"unknown_{trans_class_idx}")
14950
15052
 
14951
- calibration = self.pipeline._calibrate_probs(target_probs, target_pred_indices, attn_weights, input_ids)
14952
- # Blend predictions (MLP decides class, transformer calibrates confidence)
14953
- mlp_weight = mlp_confidence / (target_confidence + trans_confidence + eps)
14954
- trans_weight = trans_confidence / (target_confidence + trans_confidence + eps)
14955
- if lstm_confidence is not None:
14956
- lstm_weight = lstm_confidence / (target_confidence + lstm_confidence + eps)
14957
-
14958
- calibration_weighting = calibration[target_class_idx] if target_class_idx < len(calibration) else 0.0
14959
-
14960
- # Weighted blend: calibration_weighting * calibrated + (1-weight) * mlp
14961
- if lstm_confidence is not None and lstm_weight is not None:
14962
- final_probs = mlp_weight * target_probs[i][:len(calibration)] + trans_weight * calibration[i][:len(calibration)] + lstm_weight * calibration[i][:len(calibration)]
15053
+ if need_ensemble_method:
15054
+ print(f"[🔄] Ensemble method activated for sample {i} due to high anisotropy")
15055
+ calibration = self.pipeline._calibrate_probs(target_probs, target_pred_indices, attn_weights, input_ids)
15056
+ # Blend predictions (MLP decides class, transformer calibrates confidence)
15057
+ mlp_weight = mlp_confidence / (target_confidence + trans_confidence + eps)
15058
+ trans_weight = trans_confidence / (target_confidence + trans_confidence + eps)
15059
+ if lstm_confidence is not None:
15060
+ lstm_weight = lstm_confidence / (target_confidence + lstm_confidence + eps)
15061
+
15062
+ calibration_weighting = calibration[target_class_idx] if target_class_idx < len(calibration) else 0.0
15063
+
15064
+ # Weighted blend: calibration_weighting * calibrated + (1-weight) * mlp
15065
+ if lstm_confidence is not None and lstm_weight is not None:
15066
+ final_probs = mlp_weight * target_probs[i][:len(calibration)] + trans_weight * calibration[i][:len(calibration)] + lstm_weight * calibration[i][:len(calibration)]
15067
+ else:
15068
+ final_probs = mlp_weight * target_probs[i][:len(calibration)] + trans_weight * calibration[i][:len(calibration)]
15069
+
15070
+ final_probs = self.calibration_penalized_check(final_probs, target_class_idx)
15071
+ final_class_idx = target_class_idx
15072
+ try:
15073
+ final_confidence = final_probs[final_class_idx]
15074
+ except IndexError:
15075
+ final_confidence = np.max(final_probs) if isinstance(final_probs, np.ndarray) else np.mean(final_probs)
15076
+
15077
+ if isinstance(final_confidence, np.ndarray):
15078
+ final_confidence = np.max(final_confidence)
15079
+
15080
+ # Calculate agreement
15081
+ agreement = mlp_class_idx == trans_class_idx
14963
15082
  else:
14964
- final_probs = mlp_weight * target_probs[i][:len(calibration)] + trans_weight * calibration[i][:len(calibration)]
14965
-
14966
- final_probs = self.calibration_penalized_check(final_probs, target_class_idx)
14967
- final_class_idx = target_class_idx
14968
- try:
14969
- final_confidence = final_probs[final_class_idx]
14970
- except IndexError:
14971
- final_confidence = np.max(final_probs) if isinstance(final_probs, np.ndarray) else final_probs
15083
+ print(f"[🔄] Ensemble method not activated for sample {i} due to unmet conditions")
15084
+ if lstm_confidence is None:
15085
+ lstm_confidence = mlp_confidence
15086
+ if mlp_confidence > trans_confidence and mlp_confidence > lstm_confidence and not mlp_confidence > 0.95:
15087
+ final_probs = mlp_probs[i]
15088
+ final_class_idx = mlp_class_idx
15089
+ final_confidence = mlp_confidence
15090
+ print(f"[🔄] MLP chosen for sample {i} due to highest confidence: {mlp_confidence:.1%}")
15091
+ elif trans_confidence > lstm_confidence:
15092
+ final_probs = trans_probs[i]
15093
+ final_class_idx = trans_class_idx
15094
+ final_confidence = trans_confidence
15095
+ print(f"[🔄] Transformer chosen for sample {i} due to highest confidence: {trans_confidence:.1%}")
15096
+ else:
15097
+ final_probs = lstm_probs[i] if lstm_probs is not None else mlp_probs[i]
15098
+ final_class_idx = lstm_class_idx if lstm_probs is not None else mlp_class_idx
15099
+ final_confidence = lstm_confidence if lstm_probs is not None else mlp_confidence
15100
+ if final_confidence > 0.95:
15101
+ if mlp_probs.shape == lstm_probs.shape:
15102
+ final_probs = mlp_probs[i] * lstm_probs[i]
15103
+ final_class_idx = mlp_class_idx
15104
+ final_confidence = mlp_confidence * lstm_confidence
15105
+ else:
15106
+ if lstm_probs is not None:
15107
+ final_probs = target_probs[i] if len(target_probs) == num_classes else lstm_probs[i]
15108
+ else:
15109
+ final_probs = trans_probs[i]
15110
+
15111
+ final_class_idx = target_class_idx
15112
+ final_confidence = target_confidence * lstm_confidence
14972
15113
 
14973
- if isinstance(final_confidence, np.ndarray):
14974
- final_confidence = np.max(final_confidence)
15114
+ else:
15115
+ print(f"[🔄] No model chosen for sample {i} due to low confidence: MLP={mlp_confidence:.1%}, Transformer={trans_confidence:.1%}, LSTM={lstm_confidence:.1%}")
15116
+
15117
+ agreement = mlp_class_idx == trans_class_idx
14975
15118
 
14976
- # Calculate agreement
14977
- agreement = mlp_class_idx == trans_class_idx
14978
15119
  else:
14979
15120
  final_probs = mlp_probs[i]
14980
15121
  final_probs = self.calibration_penalized_check(final_probs, mlp_class_idx)
@@ -14986,7 +15127,7 @@ class PipelinePredictionManager:
14986
15127
 
14987
15128
  trans_label = None
14988
15129
  trans_confidence = None
14989
- agreement = True
15130
+ agreement = False
14990
15131
 
14991
15132
  final_label = reverse_map.get(final_class_idx, f"unknown_{final_class_idx}")
14992
15133
  # Build result
@@ -15150,7 +15291,7 @@ class PipelinePredictionManager:
15150
15291
  except:
15151
15292
  confidence = float(final_probs[0][len(reverse_map)-1]) if isinstance(final_probs[0], (float, int)) else 0.0
15152
15293
  else:
15153
- if self.pipeline.use_transformer:
15294
+ if self.pipeline.use_transformer and need_ensemble_method:
15154
15295
  print("\n[⚠️] Uncertain confidence and disagreement detected. Using ensemble method for final decision.")
15155
15296
  input_forward = sequence_ids if sequence_ids is not None else input_ids
15156
15297
  final_probs, details = self.pipeline.ensemble.predict_ensemble(input_forward, X, y, method='dynamic', embedded=True)
@@ -15169,7 +15310,7 @@ class PipelinePredictionManager:
15169
15310
  except:
15170
15311
  confidence = float(final_probs[0][len(reverse_map)-1]) if isinstance(final_probs[0], (float, int)) else 0.0
15171
15312
  else:
15172
- input_forward = sequence_ids if sequence_ids is not None else input_ids
15313
+ final_probs = self.calibration_penalized_check(final_probs, target_pred_indices[0])
15173
15314
  final_idx = final_probs[0].argmax() if final_probs is not None else target_probs[0].argmax()
15174
15315
 
15175
15316
  original_idx = final_idx