AbstractIntegratedModule 1.1.4__tar.gz → 1.1.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.
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/AbstractIntegratedModule.egg-info/PKG-INFO +1 -1
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/AbstractIntegratedModule.py +228 -77
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/AbstractOptimizedModules.c +1173 -1164
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/AbstractOptimizedModules.pyx +6 -4
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/PKG-INFO +1 -1
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/README.md +79 -11
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/pyproject.toml +1 -1
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/setup.py +1 -1
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/AbstractIntegratedModule.egg-info/SOURCES.txt +0 -0
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/AbstractIntegratedModule.egg-info/dependency_links.txt +0 -0
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/AbstractIntegratedModule.egg-info/requires.txt +0 -0
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/AbstractIntegratedModule.egg-info/top_level.txt +0 -0
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/MANIFEST.in +0 -0
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/abstract_model_storage/Cargo.toml +0 -0
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/abstract_model_storage/pyproject.toml +0 -0
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/abstract_model_storage/src/lib.rs +0 -0
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/abstract_model_storage/target/debug/build/libsqlite3-sys-ed07b882cd2aa5e2/out/bindgen.rs +0 -0
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/abstract_model_storage/target/debug/build/serde_core-ebc15f2e9cad7f5f/out/private.rs +0 -0
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/abstract_model_storage/target/debug/build/target-lexicon-08527f45de28143d/out/host.rs +0 -0
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/abstract_model_storage/target/release/build/libsqlite3-sys-bf0400df4523274c/out/bindgen.rs +0 -0
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/abstract_model_storage/target/release/build/serde_core-5cdb76131825e4af/out/private.rs +0 -0
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/abstract_model_storage/target/release/build/target-lexicon-43eb95a0588bf457/out/host.rs +0 -0
- {abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/setup.cfg +0 -0
{abstractintegratedmodule-1.1.4 → abstractintegratedmodule-1.1.6}/AbstractIntegratedModule.py
RENAMED
|
@@ -2981,7 +2981,7 @@ class LSTMEngine:
|
|
|
2981
2981
|
boundaries = np.percentile(non_dominant, percentiles)
|
|
2982
2982
|
|
|
2983
2983
|
if labels is None:
|
|
2984
|
-
labels = ["Base"] + [f"Level_{i+1}" for i in range(n_bins-1)]
|
|
2984
|
+
labels = ["Base"] + [f"Level_{i+1}" for i in range(n_bins-1)]
|
|
2985
2985
|
|
|
2986
2986
|
base_lo = float(value_range[0]) if value_range else -1e-6
|
|
2987
2987
|
bins = {"Base": (base_lo, float(boundaries[0]))}
|
|
@@ -3584,6 +3584,7 @@ class WeightedEnsemblePredictor:
|
|
|
3584
3584
|
def _dynamic_weighted_ensemble(self, trans_probs, mlp_probs, attn_weights,
|
|
3585
3585
|
input_ids, lstm_probs=None, lstm_weight_hint=0.0):
|
|
3586
3586
|
# normalize all inputs to guaranteed 2D float64
|
|
3587
|
+
mean_pred_counts = np.mean(self.pipeline.model3.pred_counts)
|
|
3587
3588
|
try:
|
|
3588
3589
|
trans_probs = np.asarray(trans_probs, dtype=np.float64)
|
|
3589
3590
|
mlp_probs = np.asarray(mlp_probs, dtype=np.float64)
|
|
@@ -3635,7 +3636,8 @@ class WeightedEnsemblePredictor:
|
|
|
3635
3636
|
np.ascontiguousarray(lstm_probs),
|
|
3636
3637
|
np.ascontiguousarray(lstm_weight_hints),
|
|
3637
3638
|
float(self.pipeline.confidence_threshold),
|
|
3638
|
-
has_lstm
|
|
3639
|
+
has_lstm,
|
|
3640
|
+
mean_pred_counts
|
|
3639
3641
|
)
|
|
3640
3642
|
except Exception as e:
|
|
3641
3643
|
print(f'[=] Error in optimized dynamic weighted ensemble: {e}, using regular dynamic ensemble method.')
|
|
@@ -3666,7 +3668,7 @@ class WeightedEnsemblePredictor:
|
|
|
3666
3668
|
mlp_cf = 1.0 / (1.0 + mlp_entropy)
|
|
3667
3669
|
|
|
3668
3670
|
tw = trans_cf * (1.0 + agreement) / 2.0
|
|
3669
|
-
mw = mlp_cf
|
|
3671
|
+
mw = mlp_cf * mean_pred_counts * (1.0 + agreement) / 2.0
|
|
3670
3672
|
|
|
3671
3673
|
if has_lstm:
|
|
3672
3674
|
lstm_row = np.zeros(n_classes)
|
|
@@ -3693,6 +3695,7 @@ class WeightedEnsemblePredictor:
|
|
|
3693
3695
|
|
|
3694
3696
|
|
|
3695
3697
|
def _attention_weighted_ensemble(self, trans_probs, mlp_probs, attn_weights):
|
|
3698
|
+
mean_pred_counts = np.mean(self.pipeline.model3.pred_counts)
|
|
3696
3699
|
if attn_weights is None:
|
|
3697
3700
|
return (trans_probs + mlp_probs) / 2
|
|
3698
3701
|
|
|
@@ -3730,12 +3733,12 @@ class WeightedEnsemblePredictor:
|
|
|
3730
3733
|
trans_trust = attn_limit * (1.0 - anisotropy)
|
|
3731
3734
|
|
|
3732
3735
|
# MLP gets the rest
|
|
3733
|
-
mlp_trust = 1.0 - trans_trust
|
|
3736
|
+
mlp_trust = (1.0 - trans_trust) * mean_pred_counts
|
|
3734
3737
|
|
|
3735
3738
|
try:
|
|
3736
|
-
ensemble[i] = trans_trust * trans_row + mlp_trust * mlp_row
|
|
3739
|
+
ensemble[i] = trans_trust * trans_row + mlp_trust * mlp_row
|
|
3737
3740
|
except:
|
|
3738
|
-
ensemble = trans_trust * trans_row + mlp_trust * mlp_row
|
|
3741
|
+
ensemble = trans_trust * trans_row + mlp_trust * mlp_row
|
|
3739
3742
|
|
|
3740
3743
|
return ensemble
|
|
3741
3744
|
|
|
@@ -4003,6 +4006,7 @@ class ExplainabilityModule:
|
|
|
4003
4006
|
def data_preparation(self, titles, labels):
|
|
4004
4007
|
datasets = []
|
|
4005
4008
|
raw = []
|
|
4009
|
+
|
|
4006
4010
|
for title in titles:
|
|
4007
4011
|
tupled_title = (str(title))
|
|
4008
4012
|
datasets.append(tupled_title)
|
|
@@ -4025,6 +4029,7 @@ class ExplainabilityModule:
|
|
|
4025
4029
|
filled = int(value * max_width)
|
|
4026
4030
|
return '█' * filled + '░' * (max_width - filled)
|
|
4027
4031
|
|
|
4032
|
+
|
|
4028
4033
|
def _learn_from_feedback(self, text, correct_label, wrong_result, batch_size=2):
|
|
4029
4034
|
eps = 1e-5
|
|
4030
4035
|
print(f"\n[📚] Learning: '{text}' → {correct_label}...")
|
|
@@ -4205,7 +4210,7 @@ class ExplainabilityModule:
|
|
|
4205
4210
|
if choice.lower() == 'skip':
|
|
4206
4211
|
return None
|
|
4207
4212
|
elif choice.lower() == 'explain':
|
|
4208
|
-
print(explanation)
|
|
4213
|
+
print(f"Explanation: {explanation}")
|
|
4209
4214
|
return self._ask_for_feedback(text, result, explanation)
|
|
4210
4215
|
elif choice == '4':
|
|
4211
4216
|
history = self.get_decision_history(limit=10)
|
|
@@ -4253,11 +4258,11 @@ class ExplainabilityModule:
|
|
|
4253
4258
|
if zero_ratio > threshold:
|
|
4254
4259
|
print(f'[!] {len(zero_rows)} zero rows ({zero_ratio:.0%}), refitting on current batch')
|
|
4255
4260
|
if isinstance(texts, str):
|
|
4256
|
-
self.tfidf.fit(texts)
|
|
4257
|
-
X_features = self.tfidf.transform(texts).toarray()
|
|
4261
|
+
self.tfidf.fit([texts])
|
|
4262
|
+
X_features = self.tfidf.transform([texts]).toarray()
|
|
4258
4263
|
elif isinstance(texts[0], str):
|
|
4259
|
-
self.tfidf.fit(texts[0])
|
|
4260
|
-
X_features = self.tfidf.transform(texts[0]).toarray()
|
|
4264
|
+
self.tfidf.fit([texts[0]])
|
|
4265
|
+
X_features = self.tfidf.transform([texts[0]]).toarray()
|
|
4261
4266
|
else:
|
|
4262
4267
|
X_features = X_features
|
|
4263
4268
|
|
|
@@ -4480,10 +4485,10 @@ class ExplainabilityModule:
|
|
|
4480
4485
|
|
|
4481
4486
|
if mlp_conf > trans_conf:
|
|
4482
4487
|
final_pred = mlp_pred
|
|
4483
|
-
|
|
4488
|
+
consensus_conf = mlp_conf * (1.0 - trans_conf) * (1.0 - np.mean(AAT)) + eps
|
|
4484
4489
|
else:
|
|
4485
4490
|
final_pred = trans_pred
|
|
4486
|
-
|
|
4491
|
+
consensus_conf = trans_conf * (1.0 - mlp_conf) * np.mean(AAT) + eps
|
|
4487
4492
|
|
|
4488
4493
|
print('='*50)
|
|
4489
4494
|
print('===== ABSTRACTION LAYER ======')
|
|
@@ -4493,10 +4498,12 @@ class ExplainabilityModule:
|
|
|
4493
4498
|
print(f'[= ABSTRACTION =] Sigmoid growth of Attention weight consistency: {np.std(sigmoid_growth)}')
|
|
4494
4499
|
print('[=] Note: Very little Consistency meaning Transformer attention quality is Healthy and focused')
|
|
4495
4500
|
|
|
4496
|
-
if isinstance(
|
|
4497
|
-
|
|
4501
|
+
if isinstance(consensus_conf, np.ndarray):
|
|
4502
|
+
consensus_conf = 1.0 / (1.0 + np.exp(-final_conf))
|
|
4498
4503
|
# Apply a sigmoid transformation to ensure the confidence is between 0 and 1
|
|
4499
4504
|
|
|
4505
|
+
# averaged all confidences to get the final confidence.
|
|
4506
|
+
final_conf = mlp_conf + trans_conf + final_conf / 3
|
|
4500
4507
|
if np.isnan(final_conf).any() or np.isinf(final_conf).any():
|
|
4501
4508
|
final_conf = self.pipeline.confidence_threshold
|
|
4502
4509
|
|
|
@@ -9582,6 +9589,7 @@ class IntegratedPipeline:
|
|
|
9582
9589
|
self.max_size = 500
|
|
9583
9590
|
self.error_decay = 0.85
|
|
9584
9591
|
self.performance_result = 1.0
|
|
9592
|
+
self.max_ram_allowed = 150
|
|
9585
9593
|
|
|
9586
9594
|
# === TRAINING SETUP ===
|
|
9587
9595
|
self.mlp_training_epochs = 1000
|
|
@@ -9872,6 +9880,7 @@ class IntegratedPipeline:
|
|
|
9872
9880
|
f"Unexpected memory type: {type(memory).__name__}",
|
|
9873
9881
|
"Clear and reinitialize memory")
|
|
9874
9882
|
|
|
9883
|
+
|
|
9875
9884
|
def k_fold_cross_validate(self, X, y, input_dim, n_classes, k=5, seed=42,
|
|
9876
9885
|
epochs=None, lr=None, log_to_diagnostics=True):
|
|
9877
9886
|
"""
|
|
@@ -12165,11 +12174,11 @@ class IntegratedPipeline:
|
|
|
12165
12174
|
if zero_ratio > threshold:
|
|
12166
12175
|
print(f'[!] {len(zero_rows)} zero rows ({zero_ratio:.0%}), refitting on current batch')
|
|
12167
12176
|
if isinstance(texts, str):
|
|
12168
|
-
self.tfidf.fit(texts)
|
|
12169
|
-
X_features = self.tfidf.transform(texts).toarray()
|
|
12177
|
+
self.tfidf.fit([texts])
|
|
12178
|
+
X_features = self.tfidf.transform([texts]).toarray()
|
|
12170
12179
|
elif isinstance(texts[0], str):
|
|
12171
|
-
self.tfidf.fit(texts[0])
|
|
12172
|
-
X_features = self.tfidf.transform(texts[0]).toarray()
|
|
12180
|
+
self.tfidf.fit([texts[0]])
|
|
12181
|
+
X_features = self.tfidf.transform([texts[0]]).toarray()
|
|
12173
12182
|
else:
|
|
12174
12183
|
X_features = X_features
|
|
12175
12184
|
|
|
@@ -12415,33 +12424,6 @@ class IntegratedPipeline:
|
|
|
12415
12424
|
pass
|
|
12416
12425
|
|
|
12417
12426
|
|
|
12418
|
-
def utility_MLP_set(self, X, y):
|
|
12419
|
-
if not self.autonomous:
|
|
12420
|
-
try:
|
|
12421
|
-
joblib.dump(self.mlp, 'analyzer_model.pkl')
|
|
12422
|
-
joblib.dump(self.model2, 'analyzer_agent.pkl')
|
|
12423
|
-
print("🎉 Model trained and saved!")
|
|
12424
|
-
except Exception as e:
|
|
12425
|
-
print(f'|| Failed to joblib dump file! : {e}, User Manual filepath suggestion needed...')
|
|
12426
|
-
|
|
12427
|
-
permission = input('|| Insert Filepath? [Y/N]: ')
|
|
12428
|
-
if permission == 'Y':
|
|
12429
|
-
suggested_path = input('|| Filepath suggestion: ')
|
|
12430
|
-
if suggested_path:
|
|
12431
|
-
self.safe_pickle_save_with_feedback(self.mlp, suggested_path)
|
|
12432
|
-
self.safe_pickle_save_with_feedback(self.model2, suggested_path)
|
|
12433
|
-
print(" || Model saved!")
|
|
12434
|
-
else:
|
|
12435
|
-
print('|| Failed to dump Your model! ')
|
|
12436
|
-
pass
|
|
12437
|
-
else:
|
|
12438
|
-
print('|| Failed to dump Your model! ')
|
|
12439
|
-
pass
|
|
12440
|
-
|
|
12441
|
-
else:
|
|
12442
|
-
pass
|
|
12443
|
-
|
|
12444
|
-
|
|
12445
12427
|
def auto_generate_labels_from_texts(self, rules, texts):
|
|
12446
12428
|
y_raw = []
|
|
12447
12429
|
self.rules = rules
|
|
@@ -13062,7 +13044,7 @@ class IntegratedPipeline:
|
|
|
13062
13044
|
is in that sample's OWN true class — used as a genuine continuous
|
|
13063
13045
|
LSTM training target instead of the raw class index.
|
|
13064
13046
|
|
|
13065
|
-
X
|
|
13047
|
+
X : hybrid_X after shape_adaptation — same format self.mlp
|
|
13066
13048
|
(self.model3) and self.model3.train() already consume.
|
|
13067
13049
|
Y : raw class indices, shape (n_samples,) or (n_samples, 1)
|
|
13068
13050
|
input_ids : sequence_inputs — same format self.model2 (Transformer)
|
|
@@ -13070,7 +13052,7 @@ class IntegratedPipeline:
|
|
|
13070
13052
|
confidence is used if not provided or Transformer
|
|
13071
13053
|
is unavailable.
|
|
13072
13054
|
|
|
13073
|
-
Returns
|
|
13055
|
+
Returns : (n_samples,) float64 array, each value in (eps, 1.0]
|
|
13074
13056
|
— never exactly 0, to avoid degenerate regression
|
|
13075
13057
|
targets downstream (consistent with the eps-floor
|
|
13076
13058
|
pattern used elsewhere in this codebase).
|
|
@@ -13147,8 +13129,9 @@ class IntegratedPipeline:
|
|
|
13147
13129
|
sample_confs.append(float(mlp_probs[i, true_class]))
|
|
13148
13130
|
else:
|
|
13149
13131
|
print(f'[⚠️] Sample {i}: true_class={true_class} out of '
|
|
13150
|
-
f'range for MLP output width {n_cls} — skipping '
|
|
13132
|
+
f'range for MLP output width {n_cls} — skipping Most '
|
|
13151
13133
|
f'MLP contribution for this sample')
|
|
13134
|
+
sample_confs.append(float(mlp_probs[i, n_cls - 1]))
|
|
13152
13135
|
|
|
13153
13136
|
if trans_probs is not None:
|
|
13154
13137
|
n_cls = trans_probs.shape[1]
|
|
@@ -13157,7 +13140,8 @@ class IntegratedPipeline:
|
|
|
13157
13140
|
else:
|
|
13158
13141
|
print(f'[⚠️] Sample {i}: true_class={true_class} out of '
|
|
13159
13142
|
f'range for Transformer output width {n_cls} — '
|
|
13160
|
-
f'skipping Transformer contribution for this sample')
|
|
13143
|
+
f'skipping Most Transformer contribution for this sample')
|
|
13144
|
+
sample_confs.append(float(trans_probs[i, n_cls - 1]))
|
|
13161
13145
|
|
|
13162
13146
|
if sample_confs:
|
|
13163
13147
|
confidences[i] = max(np.mean(sample_confs), eps)
|
|
@@ -13318,6 +13302,7 @@ class IntegratedPipeline:
|
|
|
13318
13302
|
raise Warning('[!] Dataset is None or empty! Make sure you provide a dataset or create it automatically.')
|
|
13319
13303
|
|
|
13320
13304
|
if not self.model2:
|
|
13305
|
+
print(datasets)
|
|
13321
13306
|
intents = [d[1] for d in datasets]
|
|
13322
13307
|
intent_to_id = {intent: i for i, intent in enumerate(sorted(set(intents)))}
|
|
13323
13308
|
num_classes = self._get_num_classes(label_map=label_map)
|
|
@@ -13457,7 +13442,7 @@ class IntegratedPipeline:
|
|
|
13457
13442
|
sequence_inputs = np.where(flat_mask, sequence_inputs + noise, sequence_inputs)
|
|
13458
13443
|
|
|
13459
13444
|
# ---- 6. Hard safety net: guarantee finite output no matter what happened above ----
|
|
13460
|
-
if not np.isfinite(sequence_inputs).all():
|
|
13445
|
+
if not np.isfinite(sequence_inputs).all() or not np.isfinite(sequence_inputs.std(axis=-1)).all():
|
|
13461
13446
|
n_bad = (~np.isfinite(sequence_inputs)).sum()
|
|
13462
13447
|
print(f"[!] _features_to_sequence: FINAL sanitize caught {n_bad} non-finite values "
|
|
13463
13448
|
f"(this should be rare — investigate if it triggers often)")
|
|
@@ -13469,6 +13454,7 @@ class IntegratedPipeline:
|
|
|
13469
13454
|
|
|
13470
13455
|
return sequence_inputs
|
|
13471
13456
|
|
|
13457
|
+
|
|
13472
13458
|
def _sanitize_string_chars(self, x):
|
|
13473
13459
|
if isinstance(x, (str, np.str_)):
|
|
13474
13460
|
clean_str = str(x).replace('[', '').replace(']', '').replace('...', '').strip()
|
|
@@ -13534,11 +13520,63 @@ class IntegratedPipeline:
|
|
|
13534
13520
|
|
|
13535
13521
|
return False
|
|
13536
13522
|
|
|
13523
|
+
def _should_train_transformer(self, sequence_inputs, X_raw,
|
|
13524
|
+
min_seq_len=3, min_anisotropy=0.35,
|
|
13525
|
+
min_samples=10, ram_headroom_mb=80):
|
|
13526
|
+
"""
|
|
13527
|
+
Decides whether Transformer training is worth its RAM/compute cost
|
|
13528
|
+
for THIS data, reusing existing AME/anisotropy signals rather than
|
|
13529
|
+
adding new expensive computation. Built specifically for
|
|
13530
|
+
memory-constrained edge deployment (e.g. Raspberry Pi Zero).
|
|
13531
|
+
|
|
13532
|
+
Returns (should_train: bool, reason: str) so callers can log why should_train.
|
|
13533
|
+
|
|
13534
|
+
"""
|
|
13535
|
+
sequence_inputs = np.asarray(sequence_inputs)
|
|
13536
|
+
n_samples = sequence_inputs.shape[0] if sequence_inputs.ndim > 0 else 0
|
|
13537
|
+
T = sequence_inputs.shape[1] if sequence_inputs.ndim > 1 else 1
|
|
13538
|
+
|
|
13539
|
+
# ── Check 1: sample count ────────────────────────────────
|
|
13540
|
+
if n_samples < min_samples:
|
|
13541
|
+
return False, (f'only {n_samples} samples (need >= {min_samples}) — '
|
|
13542
|
+
f'too little data to justify Transformer parameter count')
|
|
13543
|
+
|
|
13544
|
+
# ── Check 2: sequence length ─────────────────────────────
|
|
13545
|
+
if T < min_seq_len:
|
|
13546
|
+
return False, (f'sequence length T={T} (need >= {min_seq_len}) — '
|
|
13547
|
+
f'attention has too few positions to model '
|
|
13548
|
+
f'meaningful relationships')
|
|
13549
|
+
|
|
13550
|
+
# ── Check 3: anisotropy ──
|
|
13551
|
+
# ── zero new computation cost ──
|
|
13552
|
+
anisotropy = self.anisotropy_measurement(sequence_inputs)
|
|
13553
|
+
if anisotropy < min_anisotropy:
|
|
13554
|
+
return False, (f'anisotropy={anisotropy:.4f} (need >= {min_anisotropy}) — '
|
|
13555
|
+
f'data lacks directional/sequential structure; '
|
|
13556
|
+
f'MLP alone is the appropriate model for this '
|
|
13557
|
+
f'geometry (same finding as make_moons earlier)')
|
|
13558
|
+
|
|
13559
|
+
# ── Check 4: RAM headrooms, optional, graceful if unavailable ──
|
|
13560
|
+
if ram_headroom_mb is not None:
|
|
13561
|
+
try:
|
|
13562
|
+
available_mb = psutil.virtual_memory().available / (1024 * 1024)
|
|
13563
|
+
if available_mb < ram_headroom_mb:
|
|
13564
|
+
return False, (f'only {available_mb:.0f}MB RAM available '
|
|
13565
|
+
f'(need >= {ram_headroom_mb}MB headroom) — '
|
|
13566
|
+
f'skipping Transformer to protect device stability')
|
|
13567
|
+
except ImportError:
|
|
13568
|
+
pass # psutil not installed — skip memory check silently,
|
|
13569
|
+
|
|
13570
|
+
|
|
13571
|
+
return True, (f'n={n_samples}, T={T}, anisotropy={anisotropy:.4f} — '
|
|
13572
|
+
f'sufficient structure and data to justify Transformer training')
|
|
13537
13573
|
|
|
13538
13574
|
def transformer_utilities(self, X_provided= None, X_raw=None, y_true=None, rules=None,
|
|
13539
13575
|
datasets=None, label_map=None, batch_size=2, min_signal=1e-3,
|
|
13540
|
-
max_samples_for_focused_fit=500):
|
|
13576
|
+
max_samples_for_focused_fit=500, max_ram_used=80):
|
|
13541
13577
|
unsuitable = False
|
|
13578
|
+
max_ram_used = self.max_ram_allowed if max_ram_used is None else max_ram_used
|
|
13579
|
+
|
|
13542
13580
|
if X_provided is not None:
|
|
13543
13581
|
X_raw = X_provided
|
|
13544
13582
|
|
|
@@ -13717,13 +13755,14 @@ class IntegratedPipeline:
|
|
|
13717
13755
|
n_heads=self.transformer_heads,
|
|
13718
13756
|
num_classes=n_classes
|
|
13719
13757
|
)
|
|
13720
|
-
|
|
13721
|
-
|
|
13758
|
+
|
|
13759
|
+
should_train_transformer, reason = self._should_train_transformer(sequence_inputs, X_raw, min_seq_len=3, min_anisotropy=0.35, min_samples=10, ram_headroom_mb=max_ram_used)
|
|
13760
|
+
if self.use_transformer and should_train_transformer:
|
|
13761
|
+
print(f'[=] Transformer Training allowed, Reason: {reason}')
|
|
13722
13762
|
self.model2.train(sequence_inputs, y_true, epochs=self.transformer_training_epochs, mode=mode, lr=lr, embedded=True, batch_size=batch_size)
|
|
13723
13763
|
|
|
13724
13764
|
X = self.shape_adaptation(hybrid_X, input_dim)
|
|
13725
13765
|
|
|
13726
|
-
|
|
13727
13766
|
self.initialize_model_(X, input_dim, n_classes)
|
|
13728
13767
|
if not unsuitable:
|
|
13729
13768
|
self.model3.train(X, y, epochs=self.mlp_training_epochs, lr=self.mlp_lr, max_samples_for_focused_fit=max_samples_for_focused_fit)
|
|
@@ -13794,8 +13833,7 @@ class IntegratedPipeline:
|
|
|
13794
13833
|
print(f"\n🚀 Separate Modular MLP Pipeline:")
|
|
13795
13834
|
print(f" Samples: {len(self.X)}")
|
|
13796
13835
|
|
|
13797
|
-
y_true = self.initialize_model_encoding(self.X, y_raw)
|
|
13798
|
-
self.utility_MLP_set(self.X, y_true)
|
|
13836
|
+
y_true = self.initialize_model_encoding(self.X, y_raw)
|
|
13799
13837
|
print('✅ Done Training MLP Model! ')
|
|
13800
13838
|
|
|
13801
13839
|
|
|
@@ -14505,6 +14543,7 @@ class AsyncResultQueue:
|
|
|
14505
14543
|
logger.debug(f"[=] Submitted request {request_id}: {texts}")
|
|
14506
14544
|
return request_id
|
|
14507
14545
|
|
|
14546
|
+
|
|
14508
14547
|
async def wait_for_result(self, request_id: str, timeout: int = 30) -> Dict:
|
|
14509
14548
|
"""
|
|
14510
14549
|
Wait for a specific request to complete.
|
|
@@ -16369,7 +16408,6 @@ class PipelineAsyncManager:
|
|
|
16369
16408
|
class PipelinePredictionManager:
|
|
16370
16409
|
def __init__(self, pipeline, label_csv='labels.csv', target_title='title', label='label'):
|
|
16371
16410
|
self.pipeline = pipeline
|
|
16372
|
-
|
|
16373
16411
|
try:
|
|
16374
16412
|
print("📖 Loading labels from text file...")
|
|
16375
16413
|
self.titles, self.y_raw, self.label_map = self.load_labels_from_csv(label_csv, target_title, label)
|
|
@@ -16490,8 +16528,10 @@ class PipelinePredictionManager:
|
|
|
16490
16528
|
X_gen = None
|
|
16491
16529
|
use_embedded = False
|
|
16492
16530
|
attn_weights = None
|
|
16531
|
+
|
|
16493
16532
|
trans_probs = None
|
|
16494
16533
|
mlp_probs = None
|
|
16534
|
+
target_probs = None
|
|
16495
16535
|
|
|
16496
16536
|
print(f"\n[🚀] Regular Prediction Initiated...")
|
|
16497
16537
|
self.pipeline.titles = titles
|
|
@@ -16508,6 +16548,30 @@ class PipelinePredictionManager:
|
|
|
16508
16548
|
_, y, _, _ = self.pipeline.mlp_training_features(rules, dataset)
|
|
16509
16549
|
else:
|
|
16510
16550
|
dataset, _ = self.pipeline.data_preparation(titles, label_map)
|
|
16551
|
+
|
|
16552
|
+
if X is not None or y is not None and isinstance(X, (np.ndarray, list)) and isinstance(y, (np.ndarray, list)) and len(X) > 0 and len(y) > 0:
|
|
16553
|
+
X_train, X_val, y_train, y_val = self._prepare_train_val_split(
|
|
16554
|
+
X, y, min_val_per_class=5, min_frac=0.1, max_frac=0.3
|
|
16555
|
+
)
|
|
16556
|
+
|
|
16557
|
+
onehot_validation = self.pipeline._validate_onehot(y_train)
|
|
16558
|
+
if onehot_validation:
|
|
16559
|
+
if num_classes != len(label_map):
|
|
16560
|
+
num_classes = len(label_map)
|
|
16561
|
+
if num_classes > np.max(y_train):
|
|
16562
|
+
y_train = np.eye(num_classes)[np.asarray(y_train)]
|
|
16563
|
+
y_val = np.eye(num_classes)[np.asarray(y_val)]
|
|
16564
|
+
else:
|
|
16565
|
+
print('[⚠️] Warning: Y onehot encoding fails, Returning y samples as is, This may cause Exploding Gradient in MLP Training!')
|
|
16566
|
+
y_train = y_train
|
|
16567
|
+
y_val = y_val
|
|
16568
|
+
|
|
16569
|
+
X_mean = X_train.mean(axis=0)
|
|
16570
|
+
X_std = X_train.std(axis=0) + 1e-8
|
|
16571
|
+
|
|
16572
|
+
X_train = (X_train - X_mean) / X_std
|
|
16573
|
+
X = (X_val - X_mean) / X_std
|
|
16574
|
+
y = y_val.copy()
|
|
16511
16575
|
|
|
16512
16576
|
if X_gen is not None:
|
|
16513
16577
|
self.pipeline.transformer_utilities(X_provided=X, X_raw=X_gen, y_true=y, rules=rules, datasets=dataset, label_map=label_map, batch_size=batch_size)
|
|
@@ -16860,6 +16924,7 @@ class PipelinePredictionManager:
|
|
|
16860
16924
|
self.pipeline.titles = titles
|
|
16861
16925
|
self.pipeline.labels = label_map
|
|
16862
16926
|
|
|
16927
|
+
num_classes = self.pipeline._get_num_classes(label_map=label_map)
|
|
16863
16928
|
try:
|
|
16864
16929
|
|
|
16865
16930
|
if titles is not None and rules is not None:
|
|
@@ -16871,10 +16936,34 @@ class PipelinePredictionManager:
|
|
|
16871
16936
|
else:
|
|
16872
16937
|
datasets, _ = self.pipeline.data_preparation(titles, label_map)
|
|
16873
16938
|
|
|
16939
|
+
if X is not None or y is not None and len(X) > 0 and len(y) > 0:
|
|
16940
|
+
X_train, X_val, y_train, y_val = self._prepare_train_val_split(
|
|
16941
|
+
X, y, min_val_per_class=5, min_frac=0.1, max_frac=0.3
|
|
16942
|
+
)
|
|
16943
|
+
|
|
16944
|
+
onehot_validation = self.pipeline._validate_onehot(y_train)
|
|
16945
|
+
if onehot_validation:
|
|
16946
|
+
if num_classes != len(label_map):
|
|
16947
|
+
num_classes = len(label_map)
|
|
16948
|
+
if num_classes > np.max(y_train):
|
|
16949
|
+
y_train = np.eye(num_classes)[np.asarray(y_train)]
|
|
16950
|
+
y_val = np.eye(num_classes)[np.asarray(y_val)]
|
|
16951
|
+
else:
|
|
16952
|
+
print('[⚠️] Warning: Y onehot encoding fails, Returning y samples as is, This may cause Exploding Gradient in MLP Training!')
|
|
16953
|
+
y_train = y_train
|
|
16954
|
+
y_val = y_val
|
|
16955
|
+
|
|
16956
|
+
X_mean = X_train.mean(axis=0)
|
|
16957
|
+
X_std = X_train.std(axis=0) + 1e-8
|
|
16958
|
+
|
|
16959
|
+
X_train = (X_train - X_mean) / X_std
|
|
16960
|
+
X = (X_val - X_mean) / X_std
|
|
16961
|
+
y = y_val.copy()
|
|
16962
|
+
|
|
16874
16963
|
if X_gen is not None:
|
|
16875
|
-
self.pipeline.transformer_utilities(X_provided=
|
|
16964
|
+
self.pipeline.transformer_utilities(X_provided=X_train, X_raw=X_gen, y_true=y_train, rules=rules, datasets=dataset, label_map=label_map, batch_size=batch_size)
|
|
16876
16965
|
else:
|
|
16877
|
-
self.pipeline.transformer_utilities(X_provided=
|
|
16966
|
+
self.pipeline.transformer_utilities(X_provided=X_train, X_raw=X, y_true=y_train, rules=rules, datasets=dataset, label_map=label_map, batch_size=batch_size)
|
|
16878
16967
|
|
|
16879
16968
|
reverse_map = {v: k for k, v in label_map.items()}
|
|
16880
16969
|
|
|
@@ -17395,7 +17484,7 @@ class PipelinePredictionManager:
|
|
|
17395
17484
|
signals.append(f'AME={AME:.3f}')
|
|
17396
17485
|
|
|
17397
17486
|
if error_counts is not None and len(error_counts) > 0:
|
|
17398
|
-
#
|
|
17487
|
+
# max: catches localized single-class failure
|
|
17399
17488
|
# that a flat average would dilute away
|
|
17400
17489
|
worst_class_idx = int(np.argmax(error_counts))
|
|
17401
17490
|
max_error = float(error_counts[worst_class_idx])
|
|
@@ -17410,6 +17499,51 @@ class PipelinePredictionManager:
|
|
|
17410
17499
|
|
|
17411
17500
|
return need_ensemble
|
|
17412
17501
|
|
|
17502
|
+
def _check_for_transformer_sequences(self, sequence_inputs, min_seq_len=3,
|
|
17503
|
+
min_samples=10, min_AME=0.5, min_anisotropy=0.5):
|
|
17504
|
+
score = 0
|
|
17505
|
+
sequence_inputs = np.asarray(sequence_inputs)
|
|
17506
|
+
n_samples = sequence_inputs.shape[0] if sequence_inputs.ndim > 0 else 0
|
|
17507
|
+
T = sequence_inputs.shape[1] if sequence_inputs.ndim > 1 else 1
|
|
17508
|
+
|
|
17509
|
+
# ── Check 1: sample count ────────────────────────────────
|
|
17510
|
+
if n_samples < min_samples:
|
|
17511
|
+
score += 1
|
|
17512
|
+
print(f'[=] only {n_samples} samples (need >= {min_samples}) — '
|
|
17513
|
+
f'too little data to justify Transformer parameter count')
|
|
17514
|
+
|
|
17515
|
+
# ── Check 2: sequence length ─────────────────────────────
|
|
17516
|
+
if T < min_seq_len:
|
|
17517
|
+
score += 1
|
|
17518
|
+
print(f'[=] sequence length T={T} (need >= {min_seq_len}) — '
|
|
17519
|
+
f'attention has too few positions to model '
|
|
17520
|
+
f'meaningful relationships')
|
|
17521
|
+
|
|
17522
|
+
# ── Check 3: anisotropy ──
|
|
17523
|
+
# ── zero new computation cost ──
|
|
17524
|
+
anisotropy = self.pipeline.anisotropy_measurement(sequence_inputs)
|
|
17525
|
+
if anisotropy < min_anisotropy:
|
|
17526
|
+
score += 1
|
|
17527
|
+
print(f'[=] anisotropy={anisotropy:.4f} (need >= {min_anisotropy}) — '
|
|
17528
|
+
f'data lacks directional/sequential structure; '
|
|
17529
|
+
f'MLP alone is the appropriate model for this '
|
|
17530
|
+
f'geometry.')
|
|
17531
|
+
|
|
17532
|
+
|
|
17533
|
+
AME = self.pipeline.AME_Encoder(sequence_inputs)
|
|
17534
|
+
if AME > min_AME:
|
|
17535
|
+
score += 1
|
|
17536
|
+
print(f'[=] Result of Abstract Modelling Error is High, Complexity in samples is High.')
|
|
17537
|
+
|
|
17538
|
+
error_counts = np.mean(self.pipeline.model3.error_counts)
|
|
17539
|
+
if error_counts > 0.5:
|
|
17540
|
+
score += 1
|
|
17541
|
+
print('[=] MLP Experienced lots of Errors, Considering to use Transformer Prediction.')
|
|
17542
|
+
|
|
17543
|
+
propriate_single_transformer = score == 5
|
|
17544
|
+
print(f'[+] Transformer Prediction allowed: {propriate_single_transformer}')
|
|
17545
|
+
return propriate_single_transformer
|
|
17546
|
+
|
|
17413
17547
|
def advanced_prediction_method(self, titles=None, label_map=None, rules=None,
|
|
17414
17548
|
X=None, y=None,
|
|
17415
17549
|
show_proba=False, top_k=3,
|
|
@@ -17434,11 +17568,14 @@ class PipelinePredictionManager:
|
|
|
17434
17568
|
AME = None
|
|
17435
17569
|
use_embedded = False
|
|
17436
17570
|
dataset = None
|
|
17571
|
+
target_probs = None
|
|
17437
17572
|
|
|
17438
17573
|
X_gen = None
|
|
17439
17574
|
sec_chosen_label = None
|
|
17575
|
+
|
|
17440
17576
|
sec_confidence = 0.0
|
|
17441
17577
|
final_confidence = 0.0
|
|
17578
|
+
min_signal = 1e-3
|
|
17442
17579
|
|
|
17443
17580
|
correct = 0
|
|
17444
17581
|
sec_correct= 0
|
|
@@ -17530,9 +17667,18 @@ class PipelinePredictionManager:
|
|
|
17530
17667
|
|
|
17531
17668
|
if dataset is not None:
|
|
17532
17669
|
input_ids, _ = self.pipeline.input_encoding(dataset)
|
|
17670
|
+
elif dataset is not None and X_train is not None:
|
|
17671
|
+
input_ids, _ = self.pipeline.input_encoding(dataset)
|
|
17672
|
+
row_sums = input_ids.sum(axis=1)
|
|
17673
|
+
weak_rows = np.where(row_sums < min_signal)[0]
|
|
17674
|
+
weak_ratio = len(weak_rows) / len(input_ids)
|
|
17675
|
+
if weak_ratio > 0.3:
|
|
17676
|
+
print(f'[=] Zero rows abundant in Input indices: {weak_ratio:.1%}, using input indices from X samples...')
|
|
17677
|
+
input_ids = self.pipeline._features_to_sequence(X_train)
|
|
17533
17678
|
else:
|
|
17534
17679
|
input_ids = self.pipeline._features_to_sequence(X_train)
|
|
17535
17680
|
|
|
17681
|
+
|
|
17536
17682
|
if len(input_ids) == 0:
|
|
17537
17683
|
print('[⚠️] input_encoding produced no samples — skipping '
|
|
17538
17684
|
'transformer training/prediction for this call and transformer results will be Replaced by MLP or LSTM.')
|
|
@@ -17661,6 +17807,7 @@ class PipelinePredictionManager:
|
|
|
17661
17807
|
return result, cached['prediction'], cached['confidence']
|
|
17662
17808
|
else:
|
|
17663
17809
|
print(f'[!] Similarity: {cached['similarity']} is low, Cannot pick label due to low certainty, Initiating advanced prediction...')
|
|
17810
|
+
target_probs = mlp_probs.copy()
|
|
17664
17811
|
else:
|
|
17665
17812
|
print('[=] No verified output from cache available that matched samples, starting advanced prediction...')
|
|
17666
17813
|
if self.pipeline.use_transformer:
|
|
@@ -17669,7 +17816,7 @@ class PipelinePredictionManager:
|
|
|
17669
17816
|
|
|
17670
17817
|
target_probs = self.pipeline.predict_proba(input_ids, X, type='Hybrid', embedded=use_embedded)
|
|
17671
17818
|
else:
|
|
17672
|
-
target_probs = mlp_probs
|
|
17819
|
+
target_probs = mlp_probs.copy()
|
|
17673
17820
|
|
|
17674
17821
|
target_probs = target_probs[:mlp_probs.shape[0], :mlp_probs.shape[1]]
|
|
17675
17822
|
target_probs = self.pipeline.model3.continuous_predictive_correction(self, target_probs, mlp_pred_indices)
|
|
@@ -17923,12 +18070,20 @@ class PipelinePredictionManager:
|
|
|
17923
18070
|
|
|
17924
18071
|
else:
|
|
17925
18072
|
print("[=] Initiating Continuous sample prediction without Titles.")
|
|
17926
|
-
|
|
18073
|
+
|
|
18074
|
+
transformer_takes = self._check_for_transformer_sequences(input_ids, min_seq_len=3,
|
|
18075
|
+
min_samples=10, min_AME=0.5, min_anisotropy=0.5)
|
|
18076
|
+
if not transformer_takes:
|
|
18077
|
+
n_samples = mlp_probs.shape[0]
|
|
18078
|
+
chosen_probs = mlp_probs.copy()
|
|
18079
|
+
else:
|
|
18080
|
+
n_samples = trans_probs.shape[0]
|
|
18081
|
+
chosen_probs = trans_probs.copy()
|
|
17927
18082
|
|
|
17928
18083
|
lstm_pred_indices = np.argmax(lstm_probs, axis=1) if lstm_probs is not None else None
|
|
17929
18084
|
for i in range(n_samples):
|
|
17930
18085
|
outcome = self._compute_sample_prediction(
|
|
17931
|
-
i,
|
|
18086
|
+
i, chosen_probs, target_probs, target_pred_indices,
|
|
17932
18087
|
trans_probs=trans_probs, lstm_probs=lstm_probs,
|
|
17933
18088
|
lstm_pred_indices=lstm_pred_indices,
|
|
17934
18089
|
attn_weights=attn_weights, input_ids=input_ids,
|
|
@@ -18009,14 +18164,6 @@ class PipelinePredictionManager:
|
|
|
18009
18164
|
confidence = (confidence + trans_confidence / 2) + 1e-5
|
|
18010
18165
|
print(f'[=] Calibrating confidence around: {confidence:.1%}')
|
|
18011
18166
|
|
|
18012
|
-
if isinstance(chosen_label, str) and chosen_label.startswith("unknown") or float(confidence) < self.pipeline.confidence_threshold:
|
|
18013
|
-
if chosen_label is None or chosen_label.startswith('unknown'):
|
|
18014
|
-
chosen_label = 'Unknown'
|
|
18015
|
-
confidence = 1.0 - confidence #Invert confidence for unknown class
|
|
18016
|
-
print(f"\n[⚠️] Final prediction is {chosen_label} with uncertain confidence: {confidence:.1%}. Consider more consistent data for the model to learn from.")
|
|
18017
|
-
else:
|
|
18018
|
-
print(f"\n[🎯] Predicted label: {chosen_label} || With Certain Confidence: {confidence:.1%}")
|
|
18019
|
-
|
|
18020
18167
|
payload = {
|
|
18021
18168
|
'X_samples': X,
|
|
18022
18169
|
'input_ids': input_ids
|
|
@@ -18025,7 +18172,14 @@ class PipelinePredictionManager:
|
|
|
18025
18172
|
if titles is not None and len(titles) > 0:
|
|
18026
18173
|
correct, sec_correct = self.display_hybrid_results(payload, final_class_idx, results, top_k, verbose=True)
|
|
18027
18174
|
|
|
18028
|
-
|
|
18175
|
+
if isinstance(chosen_label, str) and chosen_label.startswith("unknown") or float(confidence) < self.pipeline.confidence_threshold:
|
|
18176
|
+
if chosen_label is None or chosen_label.startswith('unknown'):
|
|
18177
|
+
chosen_label = 'Unknown'
|
|
18178
|
+
confidence = 1.0 - confidence #Invert confidence for unknown class
|
|
18179
|
+
print(f"\n[⚠️] Final prediction is {chosen_label} with uncertain confidence: {confidence:.1%}. Consider more consistent data for the model to learn from.")
|
|
18180
|
+
else:
|
|
18181
|
+
print(f"\n[🎯] Predicted label: {chosen_label} || With Certain Confidence: {confidence:.1%}")
|
|
18182
|
+
|
|
18029
18183
|
return results, chosen_label, confidence
|
|
18030
18184
|
|
|
18031
18185
|
else:
|
|
@@ -18312,7 +18466,7 @@ class PipelinePredictionManager:
|
|
|
18312
18466
|
print(f'[⚡] Final Prediction: {chosen_label} with confidence: {confidence:.1%}')
|
|
18313
18467
|
chosen_label = chosen_label
|
|
18314
18468
|
|
|
18315
|
-
|
|
18469
|
+
self.pipeline.evaluate_mlp_performance(X, y, self.label_map)
|
|
18316
18470
|
# delete pipelines cache
|
|
18317
18471
|
print('[🔍] Pipelines Cache Cleaned!')
|
|
18318
18472
|
self.pipeline.cache.clear()
|
|
@@ -18374,10 +18528,7 @@ class PipelinePredictionManager:
|
|
|
18374
18528
|
|
|
18375
18529
|
except Exception as e:
|
|
18376
18530
|
print(f'[!] Cant check and calibrate probs based on penalty due to: {e}')
|
|
18377
|
-
traceback.print_exc()
|
|
18378
18531
|
|
|
18379
|
-
|
|
18380
|
-
|
|
18381
18532
|
return final_probs
|
|
18382
18533
|
|
|
18383
18534
|
|