AbstractIntegratedModule 1.1.5__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.5 → abstractintegratedmodule-1.1.6}/AbstractIntegratedModule.egg-info/PKG-INFO +1 -1
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/AbstractIntegratedModule.py +165 -67
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/AbstractOptimizedModules.c +1173 -1164
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/AbstractOptimizedModules.pyx +6 -4
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/PKG-INFO +1 -1
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/README.md +79 -9
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/pyproject.toml +1 -1
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/setup.py +1 -1
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/AbstractIntegratedModule.egg-info/SOURCES.txt +0 -0
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/AbstractIntegratedModule.egg-info/dependency_links.txt +0 -0
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/AbstractIntegratedModule.egg-info/requires.txt +0 -0
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/AbstractIntegratedModule.egg-info/top_level.txt +0 -0
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/MANIFEST.in +0 -0
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/abstract_model_storage/Cargo.toml +0 -0
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/abstract_model_storage/pyproject.toml +0 -0
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/abstract_model_storage/src/lib.rs +0 -0
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/abstract_model_storage/target/debug/build/libsqlite3-sys-ed07b882cd2aa5e2/out/bindgen.rs +0 -0
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/abstract_model_storage/target/debug/build/serde_core-ebc15f2e9cad7f5f/out/private.rs +0 -0
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/abstract_model_storage/target/debug/build/target-lexicon-08527f45de28143d/out/host.rs +0 -0
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/abstract_model_storage/target/release/build/libsqlite3-sys-bf0400df4523274c/out/bindgen.rs +0 -0
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/abstract_model_storage/target/release/build/serde_core-5cdb76131825e4af/out/private.rs +0 -0
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/abstract_model_storage/target/release/build/target-lexicon-43eb95a0588bf457/out/host.rs +0 -0
- {abstractintegratedmodule-1.1.5 → abstractintegratedmodule-1.1.6}/setup.cfg +0 -0
{abstractintegratedmodule-1.1.5 → 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
|
|
|
@@ -4207,7 +4210,7 @@ class ExplainabilityModule:
|
|
|
4207
4210
|
if choice.lower() == 'skip':
|
|
4208
4211
|
return None
|
|
4209
4212
|
elif choice.lower() == 'explain':
|
|
4210
|
-
print(explanation)
|
|
4213
|
+
print(f"Explanation: {explanation}")
|
|
4211
4214
|
return self._ask_for_feedback(text, result, explanation)
|
|
4212
4215
|
elif choice == '4':
|
|
4213
4216
|
history = self.get_decision_history(limit=10)
|
|
@@ -4255,11 +4258,11 @@ class ExplainabilityModule:
|
|
|
4255
4258
|
if zero_ratio > threshold:
|
|
4256
4259
|
print(f'[!] {len(zero_rows)} zero rows ({zero_ratio:.0%}), refitting on current batch')
|
|
4257
4260
|
if isinstance(texts, str):
|
|
4258
|
-
self.tfidf.fit(texts)
|
|
4259
|
-
X_features = self.tfidf.transform(texts).toarray()
|
|
4261
|
+
self.tfidf.fit([texts])
|
|
4262
|
+
X_features = self.tfidf.transform([texts]).toarray()
|
|
4260
4263
|
elif isinstance(texts[0], str):
|
|
4261
|
-
self.tfidf.fit(texts[0])
|
|
4262
|
-
X_features = self.tfidf.transform(texts[0]).toarray()
|
|
4264
|
+
self.tfidf.fit([texts[0]])
|
|
4265
|
+
X_features = self.tfidf.transform([texts[0]]).toarray()
|
|
4263
4266
|
else:
|
|
4264
4267
|
X_features = X_features
|
|
4265
4268
|
|
|
@@ -4482,10 +4485,10 @@ class ExplainabilityModule:
|
|
|
4482
4485
|
|
|
4483
4486
|
if mlp_conf > trans_conf:
|
|
4484
4487
|
final_pred = mlp_pred
|
|
4485
|
-
|
|
4488
|
+
consensus_conf = mlp_conf * (1.0 - trans_conf) * (1.0 - np.mean(AAT)) + eps
|
|
4486
4489
|
else:
|
|
4487
4490
|
final_pred = trans_pred
|
|
4488
|
-
|
|
4491
|
+
consensus_conf = trans_conf * (1.0 - mlp_conf) * np.mean(AAT) + eps
|
|
4489
4492
|
|
|
4490
4493
|
print('='*50)
|
|
4491
4494
|
print('===== ABSTRACTION LAYER ======')
|
|
@@ -4495,10 +4498,12 @@ class ExplainabilityModule:
|
|
|
4495
4498
|
print(f'[= ABSTRACTION =] Sigmoid growth of Attention weight consistency: {np.std(sigmoid_growth)}')
|
|
4496
4499
|
print('[=] Note: Very little Consistency meaning Transformer attention quality is Healthy and focused')
|
|
4497
4500
|
|
|
4498
|
-
if isinstance(
|
|
4499
|
-
|
|
4501
|
+
if isinstance(consensus_conf, np.ndarray):
|
|
4502
|
+
consensus_conf = 1.0 / (1.0 + np.exp(-final_conf))
|
|
4500
4503
|
# Apply a sigmoid transformation to ensure the confidence is between 0 and 1
|
|
4501
4504
|
|
|
4505
|
+
# averaged all confidences to get the final confidence.
|
|
4506
|
+
final_conf = mlp_conf + trans_conf + final_conf / 3
|
|
4502
4507
|
if np.isnan(final_conf).any() or np.isinf(final_conf).any():
|
|
4503
4508
|
final_conf = self.pipeline.confidence_threshold
|
|
4504
4509
|
|
|
@@ -9584,6 +9589,7 @@ class IntegratedPipeline:
|
|
|
9584
9589
|
self.max_size = 500
|
|
9585
9590
|
self.error_decay = 0.85
|
|
9586
9591
|
self.performance_result = 1.0
|
|
9592
|
+
self.max_ram_allowed = 150
|
|
9587
9593
|
|
|
9588
9594
|
# === TRAINING SETUP ===
|
|
9589
9595
|
self.mlp_training_epochs = 1000
|
|
@@ -12168,11 +12174,11 @@ class IntegratedPipeline:
|
|
|
12168
12174
|
if zero_ratio > threshold:
|
|
12169
12175
|
print(f'[!] {len(zero_rows)} zero rows ({zero_ratio:.0%}), refitting on current batch')
|
|
12170
12176
|
if isinstance(texts, str):
|
|
12171
|
-
self.tfidf.fit(texts)
|
|
12172
|
-
X_features = self.tfidf.transform(texts).toarray()
|
|
12177
|
+
self.tfidf.fit([texts])
|
|
12178
|
+
X_features = self.tfidf.transform([texts]).toarray()
|
|
12173
12179
|
elif isinstance(texts[0], str):
|
|
12174
|
-
self.tfidf.fit(texts[0])
|
|
12175
|
-
X_features = self.tfidf.transform(texts[0]).toarray()
|
|
12180
|
+
self.tfidf.fit([texts[0]])
|
|
12181
|
+
X_features = self.tfidf.transform([texts[0]]).toarray()
|
|
12176
12182
|
else:
|
|
12177
12183
|
X_features = X_features
|
|
12178
12184
|
|
|
@@ -12418,33 +12424,6 @@ class IntegratedPipeline:
|
|
|
12418
12424
|
pass
|
|
12419
12425
|
|
|
12420
12426
|
|
|
12421
|
-
def utility_MLP_set(self, X, y):
|
|
12422
|
-
if not self.autonomous:
|
|
12423
|
-
try:
|
|
12424
|
-
joblib.dump(self.mlp, 'analyzer_model.pkl')
|
|
12425
|
-
joblib.dump(self.model2, 'analyzer_agent.pkl')
|
|
12426
|
-
print("🎉 Model trained and saved!")
|
|
12427
|
-
except Exception as e:
|
|
12428
|
-
print(f'|| Failed to joblib dump file! : {e}, User Manual filepath suggestion needed...')
|
|
12429
|
-
|
|
12430
|
-
permission = input('|| Insert Filepath? [Y/N]: ')
|
|
12431
|
-
if permission == 'Y':
|
|
12432
|
-
suggested_path = input('|| Filepath suggestion: ')
|
|
12433
|
-
if suggested_path:
|
|
12434
|
-
self.safe_pickle_save_with_feedback(self.mlp, suggested_path)
|
|
12435
|
-
self.safe_pickle_save_with_feedback(self.model2, suggested_path)
|
|
12436
|
-
print(" || Model saved!")
|
|
12437
|
-
else:
|
|
12438
|
-
print('|| Failed to dump Your model! ')
|
|
12439
|
-
pass
|
|
12440
|
-
else:
|
|
12441
|
-
print('|| Failed to dump Your model! ')
|
|
12442
|
-
pass
|
|
12443
|
-
|
|
12444
|
-
else:
|
|
12445
|
-
pass
|
|
12446
|
-
|
|
12447
|
-
|
|
12448
12427
|
def auto_generate_labels_from_texts(self, rules, texts):
|
|
12449
12428
|
y_raw = []
|
|
12450
12429
|
self.rules = rules
|
|
@@ -13065,7 +13044,7 @@ class IntegratedPipeline:
|
|
|
13065
13044
|
is in that sample's OWN true class — used as a genuine continuous
|
|
13066
13045
|
LSTM training target instead of the raw class index.
|
|
13067
13046
|
|
|
13068
|
-
X
|
|
13047
|
+
X : hybrid_X after shape_adaptation — same format self.mlp
|
|
13069
13048
|
(self.model3) and self.model3.train() already consume.
|
|
13070
13049
|
Y : raw class indices, shape (n_samples,) or (n_samples, 1)
|
|
13071
13050
|
input_ids : sequence_inputs — same format self.model2 (Transformer)
|
|
@@ -13073,7 +13052,7 @@ class IntegratedPipeline:
|
|
|
13073
13052
|
confidence is used if not provided or Transformer
|
|
13074
13053
|
is unavailable.
|
|
13075
13054
|
|
|
13076
|
-
Returns
|
|
13055
|
+
Returns : (n_samples,) float64 array, each value in (eps, 1.0]
|
|
13077
13056
|
— never exactly 0, to avoid degenerate regression
|
|
13078
13057
|
targets downstream (consistent with the eps-floor
|
|
13079
13058
|
pattern used elsewhere in this codebase).
|
|
@@ -13150,8 +13129,9 @@ class IntegratedPipeline:
|
|
|
13150
13129
|
sample_confs.append(float(mlp_probs[i, true_class]))
|
|
13151
13130
|
else:
|
|
13152
13131
|
print(f'[⚠️] Sample {i}: true_class={true_class} out of '
|
|
13153
|
-
f'range for MLP output width {n_cls} — skipping '
|
|
13132
|
+
f'range for MLP output width {n_cls} — skipping Most '
|
|
13154
13133
|
f'MLP contribution for this sample')
|
|
13134
|
+
sample_confs.append(float(mlp_probs[i, n_cls - 1]))
|
|
13155
13135
|
|
|
13156
13136
|
if trans_probs is not None:
|
|
13157
13137
|
n_cls = trans_probs.shape[1]
|
|
@@ -13160,7 +13140,8 @@ class IntegratedPipeline:
|
|
|
13160
13140
|
else:
|
|
13161
13141
|
print(f'[⚠️] Sample {i}: true_class={true_class} out of '
|
|
13162
13142
|
f'range for Transformer output width {n_cls} — '
|
|
13163
|
-
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]))
|
|
13164
13145
|
|
|
13165
13146
|
if sample_confs:
|
|
13166
13147
|
confidences[i] = max(np.mean(sample_confs), eps)
|
|
@@ -13461,7 +13442,7 @@ class IntegratedPipeline:
|
|
|
13461
13442
|
sequence_inputs = np.where(flat_mask, sequence_inputs + noise, sequence_inputs)
|
|
13462
13443
|
|
|
13463
13444
|
# ---- 6. Hard safety net: guarantee finite output no matter what happened above ----
|
|
13464
|
-
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():
|
|
13465
13446
|
n_bad = (~np.isfinite(sequence_inputs)).sum()
|
|
13466
13447
|
print(f"[!] _features_to_sequence: FINAL sanitize caught {n_bad} non-finite values "
|
|
13467
13448
|
f"(this should be rare — investigate if it triggers often)")
|
|
@@ -13473,6 +13454,7 @@ class IntegratedPipeline:
|
|
|
13473
13454
|
|
|
13474
13455
|
return sequence_inputs
|
|
13475
13456
|
|
|
13457
|
+
|
|
13476
13458
|
def _sanitize_string_chars(self, x):
|
|
13477
13459
|
if isinstance(x, (str, np.str_)):
|
|
13478
13460
|
clean_str = str(x).replace('[', '').replace(']', '').replace('...', '').strip()
|
|
@@ -13538,11 +13520,63 @@ class IntegratedPipeline:
|
|
|
13538
13520
|
|
|
13539
13521
|
return False
|
|
13540
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')
|
|
13541
13573
|
|
|
13542
13574
|
def transformer_utilities(self, X_provided= None, X_raw=None, y_true=None, rules=None,
|
|
13543
13575
|
datasets=None, label_map=None, batch_size=2, min_signal=1e-3,
|
|
13544
|
-
max_samples_for_focused_fit=500):
|
|
13576
|
+
max_samples_for_focused_fit=500, max_ram_used=80):
|
|
13545
13577
|
unsuitable = False
|
|
13578
|
+
max_ram_used = self.max_ram_allowed if max_ram_used is None else max_ram_used
|
|
13579
|
+
|
|
13546
13580
|
if X_provided is not None:
|
|
13547
13581
|
X_raw = X_provided
|
|
13548
13582
|
|
|
@@ -13721,13 +13755,14 @@ class IntegratedPipeline:
|
|
|
13721
13755
|
n_heads=self.transformer_heads,
|
|
13722
13756
|
num_classes=n_classes
|
|
13723
13757
|
)
|
|
13724
|
-
|
|
13725
|
-
|
|
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}')
|
|
13726
13762
|
self.model2.train(sequence_inputs, y_true, epochs=self.transformer_training_epochs, mode=mode, lr=lr, embedded=True, batch_size=batch_size)
|
|
13727
13763
|
|
|
13728
13764
|
X = self.shape_adaptation(hybrid_X, input_dim)
|
|
13729
13765
|
|
|
13730
|
-
|
|
13731
13766
|
self.initialize_model_(X, input_dim, n_classes)
|
|
13732
13767
|
if not unsuitable:
|
|
13733
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)
|
|
@@ -13798,8 +13833,7 @@ class IntegratedPipeline:
|
|
|
13798
13833
|
print(f"\n🚀 Separate Modular MLP Pipeline:")
|
|
13799
13834
|
print(f" Samples: {len(self.X)}")
|
|
13800
13835
|
|
|
13801
|
-
y_true = self.initialize_model_encoding(self.X, y_raw)
|
|
13802
|
-
self.utility_MLP_set(self.X, y_true)
|
|
13836
|
+
y_true = self.initialize_model_encoding(self.X, y_raw)
|
|
13803
13837
|
print('✅ Done Training MLP Model! ')
|
|
13804
13838
|
|
|
13805
13839
|
|
|
@@ -17465,6 +17499,51 @@ class PipelinePredictionManager:
|
|
|
17465
17499
|
|
|
17466
17500
|
return need_ensemble
|
|
17467
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
|
+
|
|
17468
17547
|
def advanced_prediction_method(self, titles=None, label_map=None, rules=None,
|
|
17469
17548
|
X=None, y=None,
|
|
17470
17549
|
show_proba=False, top_k=3,
|
|
@@ -17493,8 +17572,10 @@ class PipelinePredictionManager:
|
|
|
17493
17572
|
|
|
17494
17573
|
X_gen = None
|
|
17495
17574
|
sec_chosen_label = None
|
|
17575
|
+
|
|
17496
17576
|
sec_confidence = 0.0
|
|
17497
17577
|
final_confidence = 0.0
|
|
17578
|
+
min_signal = 1e-3
|
|
17498
17579
|
|
|
17499
17580
|
correct = 0
|
|
17500
17581
|
sec_correct= 0
|
|
@@ -17586,9 +17667,18 @@ class PipelinePredictionManager:
|
|
|
17586
17667
|
|
|
17587
17668
|
if dataset is not None:
|
|
17588
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)
|
|
17589
17678
|
else:
|
|
17590
17679
|
input_ids = self.pipeline._features_to_sequence(X_train)
|
|
17591
17680
|
|
|
17681
|
+
|
|
17592
17682
|
if len(input_ids) == 0:
|
|
17593
17683
|
print('[⚠️] input_encoding produced no samples — skipping '
|
|
17594
17684
|
'transformer training/prediction for this call and transformer results will be Replaced by MLP or LSTM.')
|
|
@@ -17980,12 +18070,20 @@ class PipelinePredictionManager:
|
|
|
17980
18070
|
|
|
17981
18071
|
else:
|
|
17982
18072
|
print("[=] Initiating Continuous sample prediction without Titles.")
|
|
17983
|
-
|
|
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()
|
|
17984
18082
|
|
|
17985
18083
|
lstm_pred_indices = np.argmax(lstm_probs, axis=1) if lstm_probs is not None else None
|
|
17986
18084
|
for i in range(n_samples):
|
|
17987
18085
|
outcome = self._compute_sample_prediction(
|
|
17988
|
-
i,
|
|
18086
|
+
i, chosen_probs, target_probs, target_pred_indices,
|
|
17989
18087
|
trans_probs=trans_probs, lstm_probs=lstm_probs,
|
|
17990
18088
|
lstm_pred_indices=lstm_pred_indices,
|
|
17991
18089
|
attn_weights=attn_weights, input_ids=input_ids,
|
|
@@ -18066,14 +18164,6 @@ class PipelinePredictionManager:
|
|
|
18066
18164
|
confidence = (confidence + trans_confidence / 2) + 1e-5
|
|
18067
18165
|
print(f'[=] Calibrating confidence around: {confidence:.1%}')
|
|
18068
18166
|
|
|
18069
|
-
if isinstance(chosen_label, str) and chosen_label.startswith("unknown") or float(confidence) < self.pipeline.confidence_threshold:
|
|
18070
|
-
if chosen_label is None or chosen_label.startswith('unknown'):
|
|
18071
|
-
chosen_label = 'Unknown'
|
|
18072
|
-
confidence = 1.0 - confidence #Invert confidence for unknown class
|
|
18073
|
-
print(f"\n[⚠️] Final prediction is {chosen_label} with uncertain confidence: {confidence:.1%}. Consider more consistent data for the model to learn from.")
|
|
18074
|
-
else:
|
|
18075
|
-
print(f"\n[🎯] Predicted label: {chosen_label} || With Certain Confidence: {confidence:.1%}")
|
|
18076
|
-
|
|
18077
18167
|
payload = {
|
|
18078
18168
|
'X_samples': X,
|
|
18079
18169
|
'input_ids': input_ids
|
|
@@ -18082,6 +18172,14 @@ class PipelinePredictionManager:
|
|
|
18082
18172
|
if titles is not None and len(titles) > 0:
|
|
18083
18173
|
correct, sec_correct = self.display_hybrid_results(payload, final_class_idx, results, top_k, verbose=True)
|
|
18084
18174
|
|
|
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
|
+
|
|
18085
18183
|
return results, chosen_label, confidence
|
|
18086
18184
|
|
|
18087
18185
|
else:
|