AbstractIntegratedModule 0.8.8__tar.gz → 0.9.0__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.
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/AbstractIntegratedModule.egg-info/PKG-INFO +7 -14
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/AbstractIntegratedModule.py +189 -48
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/AbstractOptimizedModules.c +533 -453
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/PKG-INFO +7 -14
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/README.md +6 -13
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/setup.py +1 -1
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/AbstractIntegratedModule.egg-info/SOURCES.txt +0 -0
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/AbstractIntegratedModule.egg-info/dependency_links.txt +0 -0
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/AbstractIntegratedModule.egg-info/requires.txt +0 -0
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/AbstractIntegratedModule.egg-info/top_level.txt +0 -0
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/AbstractOptimizedModules.pyx +0 -0
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/MANIFEST.in +0 -0
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/abstract_model_storage/Cargo.toml +0 -0
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/abstract_model_storage/pyproject.toml +0 -0
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/abstract_model_storage/src/lib.rs +0 -0
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/abstract_model_storage/target/debug/build/libsqlite3-sys-ed07b882cd2aa5e2/out/bindgen.rs +0 -0
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/abstract_model_storage/target/debug/build/serde_core-ebc15f2e9cad7f5f/out/private.rs +0 -0
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/abstract_model_storage/target/debug/build/target-lexicon-08527f45de28143d/out/host.rs +0 -0
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/abstract_model_storage/target/release/build/libsqlite3-sys-bf0400df4523274c/out/bindgen.rs +0 -0
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/abstract_model_storage/target/release/build/serde_core-5cdb76131825e4af/out/private.rs +0 -0
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/abstract_model_storage/target/release/build/target-lexicon-43eb95a0588bf457/out/host.rs +0 -0
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/pyproject.toml +0 -0
- {abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: AbstractIntegratedModule
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
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.
|
|
45
|
+
- Development Stage: 0.9.0 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,12 @@ 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.
|
|
83
|
+
- v0.9.0:
|
|
84
84
|
- [=] New features:
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
- added robustness for
|
|
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.
|
|
88
|
+
- added robustness for input ids handling.
|
|
96
89
|
|
|
97
90
|
____________
|
|
98
91
|
<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" />
|
{abstractintegratedmodule-0.8.8 → abstractintegratedmodule-0.9.0}/AbstractIntegratedModule.py
RENAMED
|
@@ -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 '
|
|
8616
|
-
print(f'[!]
|
|
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
|
-
|
|
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
|
-
|
|
10126
|
-
|
|
10127
|
-
|
|
10128
|
-
|
|
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
|
-
|
|
14952
|
-
|
|
14953
|
-
|
|
14954
|
-
|
|
14955
|
-
|
|
14956
|
-
|
|
14957
|
-
|
|
14958
|
-
|
|
14959
|
-
|
|
14960
|
-
|
|
14961
|
-
|
|
14962
|
-
|
|
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
|
-
|
|
14965
|
-
|
|
14966
|
-
|
|
14967
|
-
|
|
14968
|
-
|
|
14969
|
-
|
|
14970
|
-
|
|
14971
|
-
|
|
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
|
-
|
|
14974
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|