AbstractIntegratedModule 1.1.0__tar.gz → 1.1.2__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-1.1.0 → abstractintegratedmodule-1.1.2}/AbstractIntegratedModule.egg-info/PKG-INFO +1 -1
  2. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/AbstractIntegratedModule.py +149 -65
  3. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/AbstractOptimizedModules.c +391 -348
  4. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/PKG-INFO +1 -1
  5. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/README.md +5 -4
  6. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/pyproject.toml +1 -1
  7. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/setup.py +1 -1
  8. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/AbstractIntegratedModule.egg-info/SOURCES.txt +0 -0
  9. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/AbstractIntegratedModule.egg-info/dependency_links.txt +0 -0
  10. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/AbstractIntegratedModule.egg-info/requires.txt +0 -0
  11. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/AbstractIntegratedModule.egg-info/top_level.txt +0 -0
  12. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/AbstractOptimizedModules.pyx +0 -0
  13. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/MANIFEST.in +0 -0
  14. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/abstract_model_storage/Cargo.toml +0 -0
  15. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/abstract_model_storage/pyproject.toml +0 -0
  16. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/abstract_model_storage/src/lib.rs +0 -0
  17. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/abstract_model_storage/target/debug/build/libsqlite3-sys-ed07b882cd2aa5e2/out/bindgen.rs +0 -0
  18. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/abstract_model_storage/target/debug/build/serde_core-ebc15f2e9cad7f5f/out/private.rs +0 -0
  19. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/abstract_model_storage/target/debug/build/target-lexicon-08527f45de28143d/out/host.rs +0 -0
  20. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/abstract_model_storage/target/release/build/libsqlite3-sys-bf0400df4523274c/out/bindgen.rs +0 -0
  21. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/abstract_model_storage/target/release/build/serde_core-5cdb76131825e4af/out/private.rs +0 -0
  22. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/abstract_model_storage/target/release/build/target-lexicon-43eb95a0588bf457/out/host.rs +0 -0
  23. {abstractintegratedmodule-1.1.0 → abstractintegratedmodule-1.1.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AbstractIntegratedModule
3
- Version: 1.1.0
3
+ Version: 1.1.2
4
4
  Summary: Integrated Pipeline with Specialized Non-LLM AI Agent Framework for ARM64 architecture
5
5
  Author: Micro-Novelty
6
6
  Author-email: Micro-Novelty <hernikpuspita5@gmail.com>
@@ -21,6 +21,7 @@ from collections import defaultdict
21
21
  import hashlib
22
22
  import ssl
23
23
  import os
24
+ import glob
24
25
  import asyncio
25
26
  import queue
26
27
  import threading
@@ -1567,6 +1568,7 @@ class Transformer:
1567
1568
  acc = float(np.mean(preds == true))
1568
1569
 
1569
1570
  return loss, acc
1571
+
1570
1572
 
1571
1573
  def _sanitize_string_chars(self, x):
1572
1574
  if isinstance(x, (str, np.str_)):
@@ -2035,8 +2037,10 @@ class MLP:
2035
2037
  # re adapt shape of pred_counts and error_counts if they don't match prob shape
2036
2038
  if self.pred_counts.shape != prob.shape:
2037
2039
  self.pred_counts = np.zeros_like(prob)
2040
+ self.pred_counts *= decay
2038
2041
  if self.error_counts.shape != prob.shape:
2039
2042
  self.error_counts = np.zeros_like(prob)
2043
+ self.error_counts *= decay
2040
2044
 
2041
2045
  except Exception as e:
2042
2046
  print(f'[!] Cant check and calibrate probs based on penalty due to: {e}')
@@ -2155,6 +2159,7 @@ class MLP:
2155
2159
  X = self._sanitize_string_chars(X)
2156
2160
  y = self._sanitize_string_chars(y)
2157
2161
  focused_fit_condition = False
2162
+ parameters = sum(w.size for w in self.layers[0].W) + sum(b.size for b in self.layers[0].b)
2158
2163
 
2159
2164
  AME = self.AME_Encoder(X)
2160
2165
  AMR = 1.0 / (1.0 + np.exp(-float(AME)))
@@ -2167,7 +2172,7 @@ class MLP:
2167
2172
  if training_not_allowed:
2168
2173
  print(f'[!] MLP Training not allowed due to unsuitable data characteristics. Anisotropy: {anisotropy:.4f}, AME: {AME:.4f}')
2169
2174
  else:
2170
- print('[+] MLP Training started...')
2175
+ print(f'[+] MLP Training started with: {parameters} Parameters.')
2171
2176
  for epoch in range(epochs):
2172
2177
  if not focused_fit_condition:
2173
2178
  y_pred = self.forward(X, AME=AME, anisotropy=anisotropy)
@@ -3899,9 +3904,6 @@ class CrossSessionAutomation:
3899
3904
  print(f"✅ Session imported! Total memories: {len(self.pipeline.memory)}")
3900
3905
 
3901
3906
  def sync_with_another_device(self, device_ip, port=5000):
3902
- import socket
3903
- import pickle
3904
-
3905
3907
  # Export current session
3906
3908
  temp_file = self.export_session(f"sync_{self.session_id}")
3907
3909
 
@@ -3919,8 +3921,6 @@ class CrossSessionAutomation:
3919
3921
 
3920
3922
 
3921
3923
  def list_sessions(self, name):
3922
- import glob
3923
-
3924
3924
  sessions = glob.glob(f"{name}*.json")
3925
3925
 
3926
3926
  print(f"\n📚 Available Sessions: {sessions}")
@@ -4267,7 +4267,7 @@ class ExplainabilityModule:
4267
4267
  if not self.learned_from_feedback:
4268
4268
  return
4269
4269
 
4270
- print(f"\n🔄 Consolidating {len(self.learned_from_feedback)} supervised memories...")
4270
+ print(f"\n[🔄] Consolidating {len(self.learned_from_feedback)} supervised memories...")
4271
4271
 
4272
4272
  # Extract all supervised examples
4273
4273
  texts = [m['input'] for m in self.learned_from_feedback]
@@ -4523,7 +4523,7 @@ class ExplainabilityModule:
4523
4523
 
4524
4524
  def _compute_anisotropy(self, attn_weights):
4525
4525
  if attn_weights is None or len(attn_weights) == 0:
4526
- return 0.5
4526
+ return self.pipeline.confidence_threshold
4527
4527
 
4528
4528
  try:
4529
4529
 
@@ -4537,7 +4537,7 @@ class ExplainabilityModule:
4537
4537
  return np.std(val) / (np.mean(val) + 1e-8)
4538
4538
 
4539
4539
  except:
4540
- return 0.5
4540
+ return self.pipeline.confidence_threshold
4541
4541
 
4542
4542
  def _compute_attention_quality(self, attn_weights):
4543
4543
  eps = 1e-5
@@ -4568,9 +4568,9 @@ class ExplainabilityModule:
4568
4568
 
4569
4569
  quality = norm_entropy * (1.0 - AMR) + avg_max * AMR + norm_var * AMR
4570
4570
  return np.clip(quality, 0, 1)
4571
- except:
4572
- print("[-] Error occurred while computing attention quality.")
4573
- AMR = 0.1
4571
+ except Exception as e:
4572
+ print(f"[-] Error occurred while computing attention quality: {e}")
4573
+ AMR = self.pipeline.confidence_threshold
4574
4574
  if attn_weights is not None:
4575
4575
  print(f"[-] Attention weights shape: {attn_weights.shape}")
4576
4576
  AME = self.AME_Encoder(attn_weights)
@@ -4680,19 +4680,19 @@ class ExplainabilityModule:
4680
4680
  d1 = self.decision_history[idx1]
4681
4681
  d2 = self.decision_history[idx2]
4682
4682
 
4683
- comparison.append(f"🔄 Decision Comparison")
4683
+ comparison.append(f"🔄 Ensemble Decision Comparison")
4684
4684
  comparison.append("====================================")
4685
4685
 
4686
- comparison.append("[<] Earlier Decision:")
4686
+ comparison.append("[<] Ensemble Earlier Decision:")
4687
4687
  comparison.append(f"[+] Input: {d1['input']}")
4688
4688
  comparison.append(f"[+] Detail Focus: {d1['prediction']} ({d1['confidence']:.1%})")
4689
4689
 
4690
- comparison.append("🧠 Later Decision:")
4690
+ comparison.append("🧠 Ensemble Later Decision:")
4691
4691
  comparison.append(f"[=] Input: {d2['input']}")
4692
4692
  comparison.append(f"[=] Detail Focus: {d2['prediction']} ({d2['confidence']:.1%})")
4693
4693
 
4694
- comparison.append("🔬 Learning Progress: ")
4695
- comparison.append(f"• Confidence {'increased' if d2['confidence'] > d1['confidence'] else 'decreased'} from {d1['confidence']} to {d2['confidence']}")
4694
+ comparison.append("🔬 Ensemble Learning Progress: ")
4695
+ comparison.append(f"• Confidence {'increased' if d2['confidence'] > d1['confidence'] else 'decreased'} from {d1['confidence']:.1%} to {d2['confidence']:.1%}")
4696
4696
  comparison.append(f"• The model is becoming {'more' if d2['confidence'] > d1['confidence'] else 'less'} certain")
4697
4697
 
4698
4698
  return '\n'.join(comparison)
@@ -4709,9 +4709,9 @@ class ExplainabilityModule:
4709
4709
 
4710
4710
  # Check transformer confidence
4711
4711
  if details['transformer']['confidence'] > 0.8:
4712
- factors.append(f"✅ Transformer is confident ({details['transformer']['confidence']}) with focused attention")
4712
+ factors.append(f"✅ Transformer is confident ({details['transformer']['confidence']:.1%}) with focused attention")
4713
4713
  elif details['transformer']['confidence'] < 0.5:
4714
- factors.append(f"🤔 Transformer is uncertain ({details['transformer']['confidence']}) due to scattered attention")
4714
+ factors.append(f"🤔 Transformer is uncertain with uncertainty up to: ({details['transformer']['confidence']:.1%}), due to scattered attention")
4715
4715
 
4716
4716
  # Check agreement
4717
4717
  if details['agreement']:
@@ -4721,9 +4721,9 @@ class ExplainabilityModule:
4721
4721
 
4722
4722
  # Attention quality
4723
4723
  if details.get('attention_quality', 0) > 0.7:
4724
- factors.append(f"[✅] High attention quality ({details['attention_quality']}) indicates clear consistent patterns!")
4724
+ factors.append(f"[✅] High attention quality: ({details['attention_quality']:.1%}) indicates clear consistent patterns!")
4725
4725
  elif details.get('attention_quality', 0) < 0.3:
4726
- factors.append(f"[-] Low Attention Quality! : ({details['attention_quality']}) Indicates noisy unnecessary patterns on seen data!")
4726
+ factors.append(f"[-] Low Attention Quality: ({details['attention_quality']:.1%}) Indicates inconsistent and ambiguous patterns on seen data!")
4727
4727
 
4728
4728
  return '\n'.join(factors)
4729
4729
 
@@ -9177,6 +9177,7 @@ class QueryNode:
9177
9177
 
9178
9178
  return self.permission
9179
9179
 
9180
+
9180
9181
  def _connect_with_peer(self, node):
9181
9182
  node_id = id(node)
9182
9183
 
@@ -9206,6 +9207,7 @@ class QueryNode:
9206
9207
 
9207
9208
  return self.permission
9208
9209
 
9210
+
9209
9211
  def _adjust_trust(self, node_id, delta):
9210
9212
  """
9211
9213
  trust that actually evolves. EMA-style bounded adjustment.
@@ -9214,6 +9216,7 @@ class QueryNode:
9214
9216
  updated = float(np.clip(current + delta, 0.0, 1.0))
9215
9217
  self._trust_scores[node_id] = updated
9216
9218
 
9219
+
9217
9220
  def _identify_node(self, node):
9218
9221
  eps = 1e-5
9219
9222
  node_id = id(node)
@@ -9237,6 +9240,7 @@ class QueryNode:
9237
9240
  ) + eps
9238
9241
  return False
9239
9242
 
9243
+
9240
9244
  def _node_safety_check(self, node):
9241
9245
  node_id = id(node)
9242
9246
  trust = self._trust_scores.get(node_id, self._default_trust)
@@ -12092,9 +12096,15 @@ class IntegratedPipeline:
12092
12096
  if 0 <= mlp_target < n_classes and i < calibrated.shape[0]:
12093
12097
  if mlp_target_int is None:
12094
12098
  mlp_target_int = int(mlp_target.flat[0])
12095
- calibrated[i, mlp_target_int] = min(
12096
- calibrated[i, mlp_target_int] * (1.5 * (1.0 - abstract_score)), 0.95
12097
- )
12099
+
12100
+ if len(calibrated.shape) >= 2:
12101
+ calibrated[i, mlp_target_int] = min(
12102
+ calibrated[i, mlp_target_int] * (1.5 * (1.0 - abstract_score)), 0.95
12103
+ )
12104
+ else:
12105
+ calibrated[mlp_target_int] = min(
12106
+ calibrated[mlp_target_int] * (1.5 * (1.0 - abstract_score)), 0.95
12107
+ )
12098
12108
 
12099
12109
  if i <= len(calibrated):
12100
12110
  try:
@@ -13110,41 +13120,65 @@ class IntegratedPipeline:
13110
13120
  return unsuitable_training
13111
13121
 
13112
13122
  def sequence_encoding(self, datasets=None, label_map=None, max_len=32):
13123
+ if not datasets:
13124
+ raise Warning('[!] Dataset is None or empty! Make sure you provide a dataset or create it automatically.')
13125
+
13126
+ if not self.model2:
13127
+ intents = [d[1] for d in datasets]
13128
+ intent_to_id = {intent: i for i, intent in enumerate(sorted(set(intents)))}
13129
+ num_classes = self._get_num_classes(label_map=label_map)
13130
+ self.model2 = Transformer(
13131
+ vocab_size=len(self.vocab),
13132
+ d_model=self.transformer_d_model,
13133
+ n_heads=self.transformer_heads,
13134
+ num_classes=num_classes
13135
+ )
13136
+
13137
+ self._sync_vocab_to_embedding() # once, not per-item
13138
+
13139
+ vocab_size = self.model2.token_embedding.shape[0] # for bounds guard
13140
+
13113
13141
  input_sequences = []
13142
+ for item in datasets:
13143
+ text = item[0] if isinstance(item, tuple) else item
13114
13144
 
13115
- if datasets:
13116
- for item in datasets:
13117
- if not self.model2:
13118
- intents = [d[1] for d in datasets]
13119
- intent_to_id = {intent:i for i, intent in enumerate(sorted(set(intents)))}
13120
- num_classes = self._get_num_classes(label_map=label_map)
13121
- self.model2 = Transformer(
13122
- vocab_size=len(self.vocab),
13123
- d_model=self.transformer_d_model,
13124
- n_heads=self.transformer_heads,
13125
- num_classes=num_classes
13126
- )
13127
-
13128
- self._sync_vocab_to_embedding()
13129
- text = item[0] if isinstance(item, tuple) else item
13145
+ token_ids = self.encode(text, self.vocab, max_len=max_len)
13146
+ token_ids = np.asarray(token_ids)
13130
13147
 
13131
- token_ids = self.encode(text, self.vocab, max_len=max_len)
13132
- token_embs = self.model2.token_embedding[token_ids] # (max_len, d_model)
13133
- pos_embs = self.model2.pos_embedding[:max_len] # (max_len, d_model)
13148
+ # bounds guard before indexing, with informative context
13149
+ out_of_bounds = (token_ids < 0) | (token_ids >= vocab_size)
13150
+ if out_of_bounds.any():
13151
+ n_bad = out_of_bounds.sum()
13152
+ print(f'[⚠️] sequence_encoding: {n_bad} token id(s) out of '
13153
+ f'range [0, {vocab_size}) for text="{str(text)[:50]}" '
13154
+ f'— clamping to valid range. This usually means vocab '
13155
+ f'grew after model2 was constructed; consider rebuilding '
13156
+ f'model2 or resizing token_embedding.')
13157
+ token_ids = np.clip(token_ids, 0, vocab_size - 1)
13158
+
13159
+ token_embs = self.model2.token_embedding[token_ids]
13160
+ pos_embs = self.model2.pos_embedding[:max_len]
13161
+
13162
+ sequence_input = token_embs + pos_embs
13163
+ input_sequences.append(sequence_input)
13164
+
13165
+ return np.stack(input_sequences)
13134
13166
 
13135
- sequence_input = token_embs + pos_embs
13136
- input_sequences.append(sequence_input)
13137
- return np.stack(input_sequences) # shape: (batch, max_len, d_model)
13138
- else:
13139
- raise Warning('[!] Dataset is None! make sure you provide a dataset or create it Automatically!')
13140
-
13141
- return None
13142
13167
 
13143
13168
  def transformer_pooled_features(self, sequence_inputs):
13144
- # mean/max/std pooling over sequence dimension
13169
+ """Mean/max/std pooling over the sequence dimension."""
13170
+ sequence_inputs = np.asarray(sequence_inputs)
13171
+
13172
+ # guard degenerate single-timestep input, where std=0
13173
+ # everywhere is mathematically correct.
13174
+ if sequence_inputs.shape[1] == 1:
13175
+ print('[=] transformer_pooled_features: single-timestep input, '
13176
+ 'std_pool will be all zeros (no variance across T=1)')
13177
+
13145
13178
  mean_pool = np.mean(sequence_inputs, axis=1)
13146
- max_pool = np.max(sequence_inputs, axis=1)
13147
- std_pool = np.std(sequence_inputs, axis=1)
13179
+ max_pool = np.max(sequence_inputs, axis=1)
13180
+ std_pool = np.std(sequence_inputs, axis=1)
13181
+
13148
13182
  return np.concatenate([mean_pool, max_pool, std_pool], axis=-1)
13149
13183
 
13150
13184
  def _features_to_sequence(self, X_provided, d_model=None, min_seq_len=2):
@@ -13462,6 +13496,16 @@ class IntegratedPipeline:
13462
13496
  else:
13463
13497
  self.vocab_size = 1
13464
13498
 
13499
+ onehot_validation = self._validate_onehot(y)
13500
+ if onehot_validation:
13501
+ if n_classes != len(label_map):
13502
+ n_classes = len(label_map)
13503
+ if n_classes > np.max(y):
13504
+ y = np.eye(n_classes)[np.asarray(y)]
13505
+ else:
13506
+ print('[⚠️] Warning: Y onehot encoding fails, Returning Y samples as is, This may cause Exploding Gradient in MLP Training!')
13507
+ y = y.copy()
13508
+
13465
13509
  if self.model2 is None:
13466
13510
  self.model2 = Transformer(
13467
13511
  vocab_size=self.vocab_size,
@@ -17127,6 +17171,42 @@ class PipelinePredictionManager:
17127
17171
 
17128
17172
  return X_train, X_val, y_train, y_val
17129
17173
 
17174
+ def _compute_need_ensemble_method(self, anisotropy, AME, error_counts,
17175
+ anisotropy_threshold=0.3,
17176
+ ame_threshold=0.3,
17177
+ error_threshold=0.3,
17178
+ min_signals_required=1):
17179
+ """
17180
+ Ensemble fallback is a safety net — it should trigger if ANY
17181
+ single strong risk signal fires, not only when all signals
17182
+ happen to align simultaneously. Uses max() over error_counts
17183
+ so a single persistently-wrong class triggers assistance even
17184
+ when most other classes are healthy.
17185
+ """
17186
+ signals = []
17187
+
17188
+ if anisotropy is not None and anisotropy > anisotropy_threshold:
17189
+ signals.append(f'anisotropy={anisotropy:.3f}')
17190
+
17191
+ if AME is not None and AME > ame_threshold:
17192
+ signals.append(f'AME={AME:.3f}')
17193
+
17194
+ if error_counts is not None and len(error_counts) > 0:
17195
+ # FIX — max, not mean: catches localized single-class failure
17196
+ # that a flat average would dilute away
17197
+ worst_class_idx = int(np.argmax(error_counts))
17198
+ max_error = float(error_counts[worst_class_idx])
17199
+ if max_error > error_threshold:
17200
+ signals.append(f'error_rate(class={worst_class_idx}, '
17201
+ f'value={max_error:.2f})')
17202
+
17203
+ need_ensemble = len(signals) >= min_signals_required
17204
+
17205
+ if need_ensemble:
17206
+ print(f'[=] Ensemble method triggered by: {", ".join(signals)}')
17207
+
17208
+ return need_ensemble
17209
+
17130
17210
  def advanced_prediction_method(self, titles=None, label_map=None, rules=None,
17131
17211
  X=None, y=None,
17132
17212
  show_proba=False, top_k=3,
@@ -17155,6 +17235,7 @@ class PipelinePredictionManager:
17155
17235
  X_gen = None
17156
17236
  sec_chosen_label = None
17157
17237
  sec_confidence = 0.0
17238
+ final_confidence = 0.0
17158
17239
 
17159
17240
  correct = 0
17160
17241
  sec_correct= 0
@@ -17199,7 +17280,7 @@ class PipelinePredictionManager:
17199
17280
 
17200
17281
  if 0 in X.shape:
17201
17282
  print(f"[⚠️] Warning: X has zero samples in the total shapes, X shapes: {X.shape}. Creating an empty array with shape (1, n_features) for processing..")
17202
- X = np.empty((1, X.shape[1])) # Create an empty array with shape (1, n_features)
17283
+ X = np.empty((1, X.shape[1])) # Created an empty array with shape (1, n_features)
17203
17284
  X = X.reshape(1, -1) # Reshape to (1, n_features) if empty but has features
17204
17285
 
17205
17286
  X_train, X_val, y_train, y_val = self._prepare_train_val_split(
@@ -17336,7 +17417,7 @@ class PipelinePredictionManager:
17336
17417
  if hasattr(self.pipeline.mlp, 'predict_proba'):
17337
17418
  mlp_probs = self.pipeline.model3.predict_proba(X)
17338
17419
  else:
17339
- logits = self.pipeline.mlp.forward(X)
17420
+ logits = self.pipeline.model3.forward(X)
17340
17421
  mlp_probs = self.pipeline._softmax(logits)
17341
17422
 
17342
17423
  # Validate all MLP predictions at once
@@ -17358,7 +17439,6 @@ class PipelinePredictionManager:
17358
17439
  if sequence_ids is not None:
17359
17440
  print("\n[🔍] Using sequence encoding for transformer input due to low anisotropy.")
17360
17441
  input_ids = sequence_ids.copy()
17361
-
17362
17442
  # verify samples for accurate answer from cache
17363
17443
  print('[🔍] Verifying Samples for possible predicted output in cache for accurate answer...')
17364
17444
  cached = self.pipeline.accurate_cache_lookup.lookup(
@@ -17404,12 +17484,13 @@ class PipelinePredictionManager:
17404
17484
  else:
17405
17485
  lstm_probs = None
17406
17486
 
17407
- need_ensemble_method = (
17408
- anisotropy > 0.3 and
17409
- AME is not None and
17410
- AME > 0.3 and
17411
- np.mean(self.error_counts) > 0.3
17412
- )
17487
+
17488
+ threshold = 0.5 + (self.pipeline.confidence_threshold + np.mean(self.error_counts)) / 2
17489
+ need_ensemble_method = self._compute_need_ensemble_method(anisotropy, AME, self.error_counts,
17490
+ anisotropy_threshold=threshold,
17491
+ ame_threshold=threshold,
17492
+ error_threshold=threshold,
17493
+ min_signals_required=1)
17413
17494
 
17414
17495
  results = []
17415
17496
  attention_data = [] if return_attention else None
@@ -17722,7 +17803,7 @@ class PipelinePredictionManager:
17722
17803
  if not results[0].get('models_agree', True) and self.pipeline.use_transformer:
17723
17804
  trans_confidence = results[0].get('trans_confidence', 1e-8)
17724
17805
  confidence = (confidence + trans_confidence / 2) + 1e-5
17725
- print(f'[=] Calibrating confidence around: {confidence:.1%} Due to disagreement between Transformer and MLP.')
17806
+ print(f'[=] Calibrating confidence around: {confidence:.1%}')
17726
17807
 
17727
17808
  if isinstance(chosen_label, str) and chosen_label.startswith("unknown") or float(confidence) < self.pipeline.confidence_threshold:
17728
17809
  if chosen_label is None or chosen_label.startswith('unknown'):
@@ -17999,7 +18080,7 @@ class PipelinePredictionManager:
17999
18080
  print(f"[!] Error in advanced prediction method: {e}, Initiating regular prediction method...")
18000
18081
  traceback.print_exc()
18001
18082
  try:
18002
- results = self.regular_prediction_method(titles=titles, label_map=label_map, rules=rules, X=X, y=y, show_proba=False, top_k=3, batch_size=2, use_transformer=True)
18083
+ results = self.regular_prediction_method(titles=titles, label_map=label_map, rules=rules, X=X, y=y, show_proba=False, top_k=3, batch_size=2, use_transformer=self.pipeline.use_transformer)
18003
18084
  chosen_label = results[0]['predicted']
18004
18085
  confidence = results[0]['confidence']
18005
18086
  except Exception as error:
@@ -18040,7 +18121,7 @@ class PipelinePredictionManager:
18040
18121
  self.pred_counts *= decay
18041
18122
 
18042
18123
  if final_probs is None:
18043
- print('[!] Warning final probabilities is None! returning the probabilities...')
18124
+ print('[!] Warning final probabilities is None! returning the None probabilities...')
18044
18125
  return final_probs
18045
18126
 
18046
18127
  try:
@@ -18074,12 +18155,15 @@ class PipelinePredictionManager:
18074
18155
  prob_sum = final_probs.sum()
18075
18156
  if prob_sum > 1e-8:
18076
18157
  final_probs /= prob_sum
18077
-
18158
+
18078
18159
  # re adapt shape of pred_counts and error_counts if they don't match prob shape
18079
18160
  if self.pred_counts.shape != final_probs.shape:
18080
18161
  self.pred_counts = np.zeros_like(final_probs)
18162
+ self.pred_counts *= decay
18081
18163
  if self.error_counts.shape != final_probs.shape:
18082
18164
  self.error_counts = np.zeros_like(final_probs)
18165
+ self.error_counts *= decay
18166
+
18083
18167
 
18084
18168
  except Exception as e:
18085
18169
  print(f'[!] Cant check and calibrate probs based on penalty due to: {e}')