AbstractIntegratedModule 1.1.3__tar.gz → 1.1.5__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.3 → abstractintegratedmodule-1.1.5}/AbstractIntegratedModule.egg-info/PKG-INFO +1 -1
  2. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/AbstractIntegratedModule.py +306 -46
  3. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/AbstractOptimizedModules.c +200 -200
  4. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/PKG-INFO +1 -1
  5. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/README.md +61 -17
  6. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/pyproject.toml +1 -1
  7. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/setup.py +1 -1
  8. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/AbstractIntegratedModule.egg-info/SOURCES.txt +0 -0
  9. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/AbstractIntegratedModule.egg-info/dependency_links.txt +0 -0
  10. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/AbstractIntegratedModule.egg-info/requires.txt +0 -0
  11. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/AbstractIntegratedModule.egg-info/top_level.txt +0 -0
  12. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/AbstractOptimizedModules.pyx +0 -0
  13. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/MANIFEST.in +0 -0
  14. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/abstract_model_storage/Cargo.toml +0 -0
  15. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/abstract_model_storage/pyproject.toml +0 -0
  16. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/abstract_model_storage/src/lib.rs +0 -0
  17. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/abstract_model_storage/target/debug/build/libsqlite3-sys-ed07b882cd2aa5e2/out/bindgen.rs +0 -0
  18. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/abstract_model_storage/target/debug/build/serde_core-ebc15f2e9cad7f5f/out/private.rs +0 -0
  19. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/abstract_model_storage/target/debug/build/target-lexicon-08527f45de28143d/out/host.rs +0 -0
  20. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/abstract_model_storage/target/release/build/libsqlite3-sys-bf0400df4523274c/out/bindgen.rs +0 -0
  21. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/abstract_model_storage/target/release/build/serde_core-5cdb76131825e4af/out/private.rs +0 -0
  22. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/abstract_model_storage/target/release/build/target-lexicon-43eb95a0588bf457/out/host.rs +0 -0
  23. {abstractintegratedmodule-1.1.3 → abstractintegratedmodule-1.1.5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AbstractIntegratedModule
3
- Version: 1.1.3
3
+ Version: 1.1.5
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>
@@ -485,12 +485,12 @@ class GeometricWeightShaping:
485
485
  subnet = x[:min(10, x.shape[0]), :min(10, x.shape[1])]
486
486
  gradient = np.gradient(subnet.flatten())
487
487
 
488
- grad_energy = np.mean(np.linalg.norm(gradient, axis=-1))
488
+ mean_vector_mag = np.mean(np.linalg.norm(gradient, axis=-1))
489
489
  X_mag = np.mean(np.linalg.norm(X, axis=-1))
490
490
  # Regular AME Equations, higher AME provides capabilities for the model to experience errors during abstraction
491
491
  # Lower AME means lower chance for un optimal abstraction.
492
492
 
493
- AME = np.log1p(X_mag) * np.log1p(grad_energy)
493
+ AME = np.log1p(X_mag) * np.log1p(mean_vector_mag)
494
494
  return AME
495
495
 
496
496
  # anisotropy provides the model the standard complexity of the data geometry, allowing it to know how complex the data needs to be processed.
@@ -1920,6 +1920,48 @@ class MLP:
1920
1920
 
1921
1921
  return self.softmax.forward(x)
1922
1922
 
1923
+ def k_fold_split(self, X, y, k=5, seed=42, min_fold_size=2):
1924
+ X = np.asarray(X)
1925
+ y = np.asarray(y)
1926
+ n = len(X)
1927
+
1928
+ # adapt k downward for small datasets, never produce
1929
+ # folds smaller than min_fold_size
1930
+ effective_k = min(k, max(2, n // min_fold_size))
1931
+ if effective_k < k:
1932
+ print(f'[⚠️] k_fold_split: reduced k from {k} to {effective_k} '
1933
+ f'for n={n} samples, to avoid folds smaller than '
1934
+ f'{min_fold_size} samples')
1935
+
1936
+ rng = np.random.default_rng(seed)
1937
+ idx = rng.permutation(n)
1938
+ folds = np.array_split(idx, effective_k)
1939
+
1940
+ for i in range(effective_k):
1941
+ val_idx = folds[i]
1942
+ train_idx = np.concatenate([folds[j] for j in range(effective_k) if j != i])
1943
+ yield X[train_idx], y[train_idx], X[val_idx], y[val_idx]
1944
+
1945
+
1946
+ def confusion_matrix(self, y_true, y_pred, num_classes):
1947
+ y_true = np.asarray(y_true)
1948
+ y_pred = np.asarray(y_pred)
1949
+
1950
+ cm = np.zeros((num_classes, num_classes), dtype=int)
1951
+
1952
+ # Convert to label arrays: use argmax only if input is one-hot/prob (2D+),
1953
+ # otherwise need to treat as already being class labels.
1954
+ true_labels = np.argmax(y_true, axis=1) if y_true.ndim > 1 else y_true
1955
+ pred_labels = np.argmax(y_pred, axis=1) if y_pred.ndim > 1 else y_pred
1956
+
1957
+ for t, p in zip(true_labels, pred_labels):
1958
+ if 0 <= t < num_classes and 0 <= p < num_classes:
1959
+ cm[t, p] += 1
1960
+ else:
1961
+ print(f'[⚠️] confusion_matrix: label out of range '
1962
+ f'(true={t}, pred={p}, num_classes={num_classes}) — skipped')
1963
+ return cm
1964
+
1923
1965
  def performance_calculation(self, x, AME=None, anisotropy=None):
1924
1966
  eps = 1e-5
1925
1967
  standard_low_error_mean = eps
@@ -1943,7 +1985,7 @@ class MLP:
1943
1985
 
1944
1986
  return performance_score
1945
1987
 
1946
- def forward(self, x, AME=None, anisotropy=None):
1988
+ def forward(self, x, y=None, AME=None, anisotropy=None, condition=None):
1947
1989
  eps = 1e-5
1948
1990
  performance_score = self.performance_calculation(x, AME=AME, anisotropy=anisotropy)
1949
1991
 
@@ -1951,6 +1993,11 @@ class MLP:
1951
1993
  x = layer.forward(x, performance_score)
1952
1994
 
1953
1995
  output = self.softmax.forward(x)
1996
+ if not condition == 'training' and y is not None:
1997
+ if y.shape == output.shape:
1998
+ acc = np.mean(np.argmax(output, axis=1) == np.argmax(y, axis=1))
1999
+ loss = Loss.categorical_crossentropy(y, output)
2000
+ print(f"[=] MLP Forward validation score: | loss: {loss:.4f} | Acc: {acc:.2%}")
1954
2001
 
1955
2002
  return output
1956
2003
 
@@ -2175,7 +2222,7 @@ class MLP:
2175
2222
  print(f'[+] MLP Training started with: {parameters} Parameters.')
2176
2223
  for epoch in range(epochs):
2177
2224
  if not focused_fit_condition:
2178
- y_pred = self.forward(X, AME=AME, anisotropy=anisotropy)
2225
+ y_pred = self.forward(X, y=y, AME=AME, anisotropy=anisotropy, condition='training')
2179
2226
  else:
2180
2227
  y_pred = self.focused_forward(X, AME=AME, anisotropy=anisotropy)
2181
2228
 
@@ -3402,7 +3449,7 @@ class WeightedEnsemblePredictor:
3402
3449
  AME = self.pipeline.model2.AME_Encoder(input_ids)
3403
3450
 
3404
3451
  trans_probs, attn_weights = self.pipeline.model2.forward(input_ids, AME=AME, embedded=embedded)
3405
- mlp_probs = self.pipeline.model3.forward(X_mlp)
3452
+ mlp_probs = self.pipeline.model3.forward(X_mlp, y=y_true)
3406
3453
  lstm_probs, lstm_weight_hint = self._get_lstm_probs(input_ids, X_mlp, label_bins=label_bins)
3407
3454
 
3408
3455
  established_agreement = self.query_node._establish_node_connection("PredictEnsemble")
@@ -3956,6 +4003,7 @@ class ExplainabilityModule:
3956
4003
  def data_preparation(self, titles, labels):
3957
4004
  datasets = []
3958
4005
  raw = []
4006
+
3959
4007
  for title in titles:
3960
4008
  tupled_title = (str(title))
3961
4009
  datasets.append(tupled_title)
@@ -3978,6 +4026,7 @@ class ExplainabilityModule:
3978
4026
  filled = int(value * max_width)
3979
4027
  return '█' * filled + '░' * (max_width - filled)
3980
4028
 
4029
+
3981
4030
  def _learn_from_feedback(self, text, correct_label, wrong_result, batch_size=2):
3982
4031
  eps = 1e-5
3983
4032
  print(f"\n[📚] Learning: '{text}' → {correct_label}...")
@@ -4447,9 +4496,8 @@ class ExplainabilityModule:
4447
4496
  print('[=] Note: Very little Consistency meaning Transformer attention quality is Healthy and focused')
4448
4497
 
4449
4498
  if isinstance(final_conf, np.ndarray):
4450
- final_conf = 1.0 / (1.0 + np.std(final_conf))
4451
- # growth deviation of arrayed final confidence helped to distinguish noise from unnecessary distribution,
4452
- # with real covariance of distribution from the data.
4499
+ final_conf = 1.0 / (1.0 + np.exp(-final_conf))
4500
+ # Apply a sigmoid transformation to ensure the confidence is between 0 and 1
4453
4501
 
4454
4502
  if np.isnan(final_conf).any() or np.isinf(final_conf).any():
4455
4503
  final_conf = self.pipeline.confidence_threshold
@@ -5944,10 +5992,13 @@ class ModelStorage:
5944
5992
 
5945
5993
  try:
5946
5994
  weights = result
5947
- self.pipeline.network_model.cell.W = np.array(weights['lstm_W'])
5948
- self.pipeline.network_model.cell.b = np.array(weights['lstm_b'])
5949
- self.pipeline.network_model.Wy = np.array(weights['Wy']) if weights['Wy'] else None
5950
- self.pipeline.network_model.by = np.array(weights['by'])
5995
+ if isinstance(weights, str):
5996
+ weights = json.loads(weights)
5997
+
5998
+ self.pipeline.network_model.cell.W = np.array(weights.get('lstm_W'))
5999
+ self.pipeline.network_model.cell.b = np.array(weights.get('lstm_b'))
6000
+ self.pipeline.network_model.Wy = np.array(weights.get('Wy')) if weights.get('Wy') else None
6001
+ self.pipeline.network_model.by = np.array(weights.get('by'))
5951
6002
  self.pipeline.lstm_engine.residual_mean = weights.get('residual_mean', 0.0)
5952
6003
  self.pipeline.lstm_engine.residual_std = weights.get('residual_std', 1.0)
5953
6004
  self.pipeline.lstm_engine.n_samples = weights.get('n_samples', self.pipeline.lstm_engine.n_samples)
@@ -5963,6 +6014,7 @@ class ModelStorage:
5963
6014
 
5964
6015
  except Exception as e:
5965
6016
  print(f'[!] Cant load any Weights due to: {e}')
6017
+ traceback.print_exc()
5966
6018
 
5967
6019
 
5968
6020
  def load_transformer_dict(self, memory_name):
@@ -9823,6 +9875,134 @@ class IntegratedPipeline:
9823
9875
  "Clear and reinitialize memory")
9824
9876
 
9825
9877
 
9878
+ def k_fold_cross_validate(self, X, y, input_dim, n_classes, k=5, seed=42,
9879
+ epochs=None, lr=None, log_to_diagnostics=True):
9880
+ """
9881
+ Orchestrates init → train → predict across k folds. Each fold gets
9882
+ a FRESH model (no weight carryover between folds), and the model
9883
+ that existed before k-fold started is restored afterward — k-fold
9884
+ here is purely an evaluation procedure, not a permanent side effect.
9885
+ """
9886
+ X = np.asarray(X)
9887
+ y = np.asarray(y)
9888
+
9889
+ # preserve pre-existing model, restore it once CV is done
9890
+ original_model = getattr(self, 'model3', None)
9891
+
9892
+ recorder = None
9893
+ if log_to_diagnostics:
9894
+ try:
9895
+ from abstract_diagnostics import get_recorder
9896
+ recorder = get_recorder(self.memory_name)
9897
+ except ImportError:
9898
+ pass # diagnostics package not installed — silently skip, no crash
9899
+
9900
+ aggregate_cm = np.zeros((n_classes, n_classes), dtype=int)
9901
+ fold_accuracies = []
9902
+
9903
+ for fold_idx, (X_tr, y_tr, X_val, y_val) in enumerate(
9904
+ self.model3.k_fold_split(X, y, k=k, seed=seed)
9905
+ ):
9906
+ # FRESH model every fold — this is the load-bearing line
9907
+ self.initialize_model_(X_tr, input_dim, n_classes)
9908
+ self.model3.train(X_tr, y_tr,
9909
+ epochs=epochs or self.mlp_training_epochs,
9910
+ lr=lr or self.mlp_lr)
9911
+
9912
+ y_pred = self.model3.forward(X_val)
9913
+ cm = self.model3.confusion_matrix(y_val, y_pred, n_classes)
9914
+ aggregate_cm += cm
9915
+
9916
+ fold_acc = np.trace(cm) / max(cm.sum(), 1)
9917
+ fold_accuracies.append(fold_acc)
9918
+ print(f'[=] Fold {fold_idx + 1}/{k}: accuracy={fold_acc:.2%}')
9919
+
9920
+ if recorder:
9921
+ recorder.log_scalar('mlp/kfold_accuracy', fold_acc, step=fold_idx)
9922
+
9923
+ self.model3 = original_model # restore — CV was evaluation only
9924
+
9925
+ mean_acc, std_acc = float(np.mean(fold_accuracies)), float(np.std(fold_accuracies))
9926
+ print(f'[=] K-Fold CV complete: {mean_acc:.2%} ± {std_acc:.2%}')
9927
+
9928
+ if recorder:
9929
+ recorder.log_scalar('mlp/kfold_mean_accuracy', mean_acc)
9930
+ recorder.log_scalar('mlp/kfold_std_accuracy', std_acc)
9931
+
9932
+ return {
9933
+ 'fold_accuracies': fold_accuracies,
9934
+ 'mean_accuracy': mean_acc,
9935
+ 'std_accuracy': std_acc,
9936
+ 'confusion_matrix': aggregate_cm,
9937
+ }
9938
+
9939
+ def evaluate_mlp_performance(self, X, y, label_map, k=5, seed=42):
9940
+ """
9941
+ input_dim and n_classes are ALWAYS derived from the actual X/y
9942
+ given to this call — never accepted as separate parameters that
9943
+ could silently drift out of sync with the real data (the exact
9944
+ bug class this whole session was about).
9945
+ """
9946
+ X = np.asarray(X)
9947
+ y = np.asarray(y)
9948
+
9949
+ if X.ndim == 1:
9950
+ X = X.reshape(-1, 1)
9951
+
9952
+ # derived input_dim directly from X, matching this call's
9953
+ # ACTUAL data.
9954
+ input_dim = X.shape[1]
9955
+ model_classes = self._get_num_classes()
9956
+
9957
+ #derived n_classes the same authoritative way used
9958
+ # throughout the rest of the pipeline (_get_num_classes as
9959
+ # primary source, cross-checked against y itself)
9960
+
9961
+ y_arr = np.asarray(y)
9962
+
9963
+ onehot_validation = self._validate_onehot(y)
9964
+ if onehot_validation:
9965
+ if model_classes != len(label_map):
9966
+ model_classes = len(label_map)
9967
+ if model_classes > np.max(y):
9968
+ y = np.eye(model_classes)[np.asarray(y)]
9969
+ else:
9970
+ print('[⚠️] Warning: Proper Y onehot encoding fails, modifying number of classes to exactly match Y values to do one final one hot encoding...')
9971
+ try:
9972
+ model_classes = np.max(y) + 1
9973
+ y = np.eye(model_classes)[np.asarray(y)]
9974
+ except:
9975
+ print('[!] Error: One hot encoding failed, please check your Y values and label map for consistency, passing raw Y and skipping MLP Training...')
9976
+ unsuitable = True
9977
+ y = y.copy() # fallback to raw y if one-hot fails
9978
+
9979
+ if y_arr.ndim > 1:
9980
+ n_classes_from_y = y_arr.shape[1]
9981
+ else:
9982
+ n_classes_from_y = int(y_arr.max()) + 1
9983
+
9984
+ if model_classes is not None and model_classes != n_classes_from_y:
9985
+ print(f'[⚠️] evaluate_mlp_performance: n_classes mismatch — '
9986
+ f'model reports {model_classes}, y data implies '
9987
+ f'{n_classes_from_y}. Using y-derived value ({n_classes_from_y}) '
9988
+ f'since k-fold must match the ACTUAL labels being evaluated.')
9989
+ n_classes = n_classes_from_y
9990
+
9991
+ print(f'[=] evaluate_mlp_performance: derived input_dim={input_dim}, '
9992
+ f'n_classes={n_classes} from provided X/y (shapes: '
9993
+ f'X={X.shape}, y={y_arr.shape})')
9994
+
9995
+ result = self.k_fold_cross_validate(X, y, input_dim, n_classes, k=k, seed=seed)
9996
+
9997
+ print('========= MLP Performance Evaluation Summary ============')
9998
+ print(f'[=>] MLP performance evaluation complete: mean accuracy={result["mean_accuracy"]:.2%}, '
9999
+ f'std accuracy={result["std_accuracy"]:.2%}')
10000
+ print(f'[=>] Confusion matrix:\n{result["confusion_matrix"]}')
10001
+ print(f'[=>] Fold accuracies: {result["fold_accuracies"]}')
10002
+ print(f'[=>] K-Fold CV complete: {result["mean_accuracy"]:.2%} ± {result["std_accuracy"]:.2%}')
10003
+ return result
10004
+
10005
+
9826
10006
  def _validate_tuple_memory(self, memory: tuple, num_classes: int) -> tuple:
9827
10007
  """
9828
10008
  Validate tuple memory entry.
@@ -11505,7 +11685,7 @@ class IntegratedPipeline:
11505
11685
  method='dynamic', embedded=embedded
11506
11686
  )
11507
11687
  else:
11508
- fresh_probs = self.model3.forward(fresh_X_raw)
11688
+ fresh_probs = self.model3.forward(fresh_X_raw, y=y_true)
11509
11689
  try:
11510
11690
  fresh_trans_probs, _ = self.model2.forward(fresh_input_ids, embedded=False)
11511
11691
  except:
@@ -12878,6 +13058,7 @@ class IntegratedPipeline:
12878
13058
 
12879
13059
  return float(np.asarray(arr).flat[-1]) # ultimate fallback, never crashes
12880
13060
 
13061
+
12881
13062
  def _get_ensemble_confidence_for_true_class(self, X, Y, input_ids=None, eps=1e-3):
12882
13063
  """
12883
13064
  Computes, per sample, how confident the MLP + Transformer ensemble
@@ -12921,7 +13102,7 @@ class IntegratedPipeline:
12921
13102
  if X.shape != self.model3.layers[0].W.shape:
12922
13103
  X = np.reshape(X, (n_samples, self.model3.layers[0].W.shape[0]))
12923
13104
 
12924
- mlp_probs = np.asarray(self.model3.forward(X), dtype=np.float64)
13105
+ mlp_probs = np.asarray(self.model3.forward(X, y=Y), dtype=np.float64)
12925
13106
  if mlp_probs.ndim == 1:
12926
13107
  mlp_probs = mlp_probs.reshape(1, -1)
12927
13108
  except Exception as e:
@@ -12987,6 +13168,7 @@ class IntegratedPipeline:
12987
13168
 
12988
13169
  return confidences
12989
13170
 
13171
+
12990
13172
  def lstm_setup_inference(self, raw_X, raw_Y, input_ids=None):
12991
13173
  print("\n" + "=" * 55)
12992
13174
  print("===== LSTM SETUP INFERENCE =====")
@@ -13139,6 +13321,7 @@ class IntegratedPipeline:
13139
13321
  raise Warning('[!] Dataset is None or empty! Make sure you provide a dataset or create it automatically.')
13140
13322
 
13141
13323
  if not self.model2:
13324
+ print(datasets)
13142
13325
  intents = [d[1] for d in datasets]
13143
13326
  intent_to_id = {intent: i for i, intent in enumerate(sorted(set(intents)))}
13144
13327
  num_classes = self._get_num_classes(label_map=label_map)
@@ -13224,6 +13407,7 @@ class IntegratedPipeline:
13224
13407
 
13225
13408
  if isinstance(X_provided, (str, np.str_)):
13226
13409
  X_provided = _safe_parse_string(X_provided)
13410
+
13227
13411
 
13228
13412
  if isinstance(X_provided, np.ndarray) and np.issubdtype(X_provided.dtype, np.character):
13229
13413
  joined = ' '.join(X_provided.astype(str).flatten())
@@ -13316,7 +13500,7 @@ class IntegratedPipeline:
13316
13500
 
13317
13501
  issues = []
13318
13502
 
13319
- # 1. Values outside [0, 1] — catches your [5,3,1,4,3,2] case directly
13503
+ # 1. Values outside [0, 1].
13320
13504
  out_of_range = (y_true < 0) | (y_true > 1)
13321
13505
  if out_of_range.any():
13322
13506
  bad_rows = np.where(out_of_range.any(axis=1))[0]
@@ -13349,7 +13533,7 @@ class IntegratedPipeline:
13349
13533
  )
13350
13534
 
13351
13535
  if issues:
13352
- print(f"[>] [{context}] y_true failed one-hot validation, one-hot encoding y sample...")
13536
+ print(f"[>] [{context}] y sample is not properly one-hot encoded, one-hot encoding y sample...")
13353
13537
  return True
13354
13538
 
13355
13539
  return False
@@ -13358,6 +13542,7 @@ class IntegratedPipeline:
13358
13542
  def transformer_utilities(self, X_provided= None, X_raw=None, y_true=None, rules=None,
13359
13543
  datasets=None, label_map=None, batch_size=2, min_signal=1e-3,
13360
13544
  max_samples_for_focused_fit=500):
13545
+ unsuitable = False
13361
13546
  if X_provided is not None:
13362
13547
  X_raw = X_provided
13363
13548
 
@@ -13520,8 +13705,14 @@ class IntegratedPipeline:
13520
13705
  if n_classes > np.max(y):
13521
13706
  y = np.eye(n_classes)[np.asarray(y)]
13522
13707
  else:
13523
- print('[⚠️] Warning: Y onehot encoding fails, Returning Y samples as is, This may cause Exploding Gradient in MLP Training!')
13524
- y = y.copy()
13708
+ print('[⚠️] Warning: Proper Y onehot encoding fails, modifying number of classes to exactly match Y values to do one final one hot encoding...')
13709
+ try:
13710
+ n_classes = np.max(y) + 1
13711
+ y = np.eye(n_classes)[np.asarray(y)]
13712
+ except:
13713
+ print('[!] Error: One hot encoding failed, please check your Y values and label map for consistency, passing raw Y and skipping MLP Training...')
13714
+ unsuitable = True
13715
+ y = y.copy() # fallback to raw y if one-hot fails
13525
13716
 
13526
13717
  if self.model2 is None:
13527
13718
  self.model2 = Transformer(
@@ -13534,9 +13725,15 @@ class IntegratedPipeline:
13534
13725
  if self.use_transformer:
13535
13726
  self.model2.train(sequence_inputs, y_true, epochs=self.transformer_training_epochs, mode=mode, lr=lr, embedded=True, batch_size=batch_size)
13536
13727
 
13537
- X = self.shape_adaptation(hybrid_X, input_dim)
13728
+ X = self.shape_adaptation(hybrid_X, input_dim)
13729
+
13730
+
13538
13731
  self.initialize_model_(X, input_dim, n_classes)
13539
- self.model3.train(X, y, epochs=self.mlp_training_epochs, lr=self.mlp_lr, max_samples_for_focused_fit=max_samples_for_focused_fit)
13732
+ if not unsuitable:
13733
+ self.model3.train(X, y, epochs=self.mlp_training_epochs, lr=self.mlp_lr, max_samples_for_focused_fit=max_samples_for_focused_fit)
13734
+ else:
13735
+ print('[->] MLP Training skipped due to unproper Y samples.')
13736
+
13540
13737
  self.lstm_setup_inference(X, y, input_ids=sequence_inputs)
13541
13738
  if self.lstm_engine:
13542
13739
  self.storage.save_weights(self.memory_name, model_type='Pipeline')
@@ -14312,6 +14509,7 @@ class AsyncResultQueue:
14312
14509
  logger.debug(f"[=] Submitted request {request_id}: {texts}")
14313
14510
  return request_id
14314
14511
 
14512
+
14315
14513
  async def wait_for_result(self, request_id: str, timeout: int = 30) -> Dict:
14316
14514
  """
14317
14515
  Wait for a specific request to complete.
@@ -16176,7 +16374,6 @@ class PipelineAsyncManager:
16176
16374
  class PipelinePredictionManager:
16177
16375
  def __init__(self, pipeline, label_csv='labels.csv', target_title='title', label='label'):
16178
16376
  self.pipeline = pipeline
16179
-
16180
16377
  try:
16181
16378
  print("📖 Loading labels from text file...")
16182
16379
  self.titles, self.y_raw, self.label_map = self.load_labels_from_csv(label_csv, target_title, label)
@@ -16297,8 +16494,10 @@ class PipelinePredictionManager:
16297
16494
  X_gen = None
16298
16495
  use_embedded = False
16299
16496
  attn_weights = None
16497
+
16300
16498
  trans_probs = None
16301
16499
  mlp_probs = None
16500
+ target_probs = None
16302
16501
 
16303
16502
  print(f"\n[🚀] Regular Prediction Initiated...")
16304
16503
  self.pipeline.titles = titles
@@ -16315,6 +16514,30 @@ class PipelinePredictionManager:
16315
16514
  _, y, _, _ = self.pipeline.mlp_training_features(rules, dataset)
16316
16515
  else:
16317
16516
  dataset, _ = self.pipeline.data_preparation(titles, label_map)
16517
+
16518
+ 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:
16519
+ X_train, X_val, y_train, y_val = self._prepare_train_val_split(
16520
+ X, y, min_val_per_class=5, min_frac=0.1, max_frac=0.3
16521
+ )
16522
+
16523
+ onehot_validation = self.pipeline._validate_onehot(y_train)
16524
+ if onehot_validation:
16525
+ if num_classes != len(label_map):
16526
+ num_classes = len(label_map)
16527
+ if num_classes > np.max(y_train):
16528
+ y_train = np.eye(num_classes)[np.asarray(y_train)]
16529
+ y_val = np.eye(num_classes)[np.asarray(y_val)]
16530
+ else:
16531
+ print('[⚠️] Warning: Y onehot encoding fails, Returning y samples as is, This may cause Exploding Gradient in MLP Training!')
16532
+ y_train = y_train
16533
+ y_val = y_val
16534
+
16535
+ X_mean = X_train.mean(axis=0)
16536
+ X_std = X_train.std(axis=0) + 1e-8
16537
+
16538
+ X_train = (X_train - X_mean) / X_std
16539
+ X = (X_val - X_mean) / X_std
16540
+ y = y_val.copy()
16318
16541
 
16319
16542
  if X_gen is not None:
16320
16543
  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)
@@ -16386,15 +16609,8 @@ class PipelinePredictionManager:
16386
16609
  X_tfidf = X
16387
16610
 
16388
16611
  # Forward pass through MLP
16389
- if hasattr(self.pipeline.mlp, 'predict_proba'):
16390
- if X_tfidf is None:
16391
- X_tfidf = X
16392
- mlp_probs = self.pipeline.model3.predict_proba(X)
16393
- else:
16394
- # Fallback if predict_proba not available
16395
- logits = self.pipeline.model3.forward(X)
16396
- mlp_probs = self.pipeline._softmax(logits)
16397
-
16612
+ mlp_probs = self.pipeline.model3.forward(X, y=y)
16613
+
16398
16614
  # Validate all MLP predictions at once
16399
16615
  mlp_pred_indices = np.argmax(mlp_probs, axis=1)
16400
16616
  if num_classes <= 0:
@@ -16674,6 +16890,7 @@ class PipelinePredictionManager:
16674
16890
  self.pipeline.titles = titles
16675
16891
  self.pipeline.labels = label_map
16676
16892
 
16893
+ num_classes = self.pipeline._get_num_classes(label_map=label_map)
16677
16894
  try:
16678
16895
 
16679
16896
  if titles is not None and rules is not None:
@@ -16685,10 +16902,34 @@ class PipelinePredictionManager:
16685
16902
  else:
16686
16903
  datasets, _ = self.pipeline.data_preparation(titles, label_map)
16687
16904
 
16905
+ if X is not None or y is not None and len(X) > 0 and len(y) > 0:
16906
+ X_train, X_val, y_train, y_val = self._prepare_train_val_split(
16907
+ X, y, min_val_per_class=5, min_frac=0.1, max_frac=0.3
16908
+ )
16909
+
16910
+ onehot_validation = self.pipeline._validate_onehot(y_train)
16911
+ if onehot_validation:
16912
+ if num_classes != len(label_map):
16913
+ num_classes = len(label_map)
16914
+ if num_classes > np.max(y_train):
16915
+ y_train = np.eye(num_classes)[np.asarray(y_train)]
16916
+ y_val = np.eye(num_classes)[np.asarray(y_val)]
16917
+ else:
16918
+ print('[⚠️] Warning: Y onehot encoding fails, Returning y samples as is, This may cause Exploding Gradient in MLP Training!')
16919
+ y_train = y_train
16920
+ y_val = y_val
16921
+
16922
+ X_mean = X_train.mean(axis=0)
16923
+ X_std = X_train.std(axis=0) + 1e-8
16924
+
16925
+ X_train = (X_train - X_mean) / X_std
16926
+ X = (X_val - X_mean) / X_std
16927
+ y = y_val.copy()
16928
+
16688
16929
  if X_gen is not None:
16689
- 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)
16930
+ 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)
16690
16931
  else:
16691
- self.pipeline.transformer_utilities(X_provided=X, X_raw=X, y_true=y, rules=rules, datasets=dataset, label_map=label_map, batch_size=batch_size)
16932
+ 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)
16692
16933
 
16693
16934
  reverse_map = {v: k for k, v in label_map.items()}
16694
16935
 
@@ -17209,7 +17450,7 @@ class PipelinePredictionManager:
17209
17450
  signals.append(f'AME={AME:.3f}')
17210
17451
 
17211
17452
  if error_counts is not None and len(error_counts) > 0:
17212
- # FIX — max, not mean: catches localized single-class failure
17453
+ # max: catches localized single-class failure
17213
17454
  # that a flat average would dilute away
17214
17455
  worst_class_idx = int(np.argmax(error_counts))
17215
17456
  max_error = float(error_counts[worst_class_idx])
@@ -17248,6 +17489,7 @@ class PipelinePredictionManager:
17248
17489
  AME = None
17249
17490
  use_embedded = False
17250
17491
  dataset = None
17492
+ target_probs = None
17251
17493
 
17252
17494
  X_gen = None
17253
17495
  sec_chosen_label = None
@@ -17431,12 +17673,8 @@ class PipelinePredictionManager:
17431
17673
  X = np.asarray(X)
17432
17674
 
17433
17675
  # MLP forward pass
17434
- if hasattr(self.pipeline.mlp, 'predict_proba'):
17435
- mlp_probs = self.pipeline.model3.predict_proba(X)
17436
- else:
17437
- logits = self.pipeline.model3.forward(X)
17438
- mlp_probs = self.pipeline._softmax(logits)
17439
-
17676
+ mlp_probs = self.pipeline.model3.forward(X, y=y_val)
17677
+
17440
17678
  # Validate all MLP predictions at once
17441
17679
  mlp_pred_indices = np.argmax(mlp_probs, axis=1)
17442
17680
  if num_classes <= 0:
@@ -17479,6 +17717,7 @@ class PipelinePredictionManager:
17479
17717
  return result, cached['prediction'], cached['confidence']
17480
17718
  else:
17481
17719
  print(f'[!] Similarity: {cached['similarity']} is low, Cannot pick label due to low certainty, Initiating advanced prediction...')
17720
+ target_probs = mlp_probs.copy()
17482
17721
  else:
17483
17722
  print('[=] No verified output from cache available that matched samples, starting advanced prediction...')
17484
17723
  if self.pipeline.use_transformer:
@@ -17487,7 +17726,7 @@ class PipelinePredictionManager:
17487
17726
 
17488
17727
  target_probs = self.pipeline.predict_proba(input_ids, X, type='Hybrid', embedded=use_embedded)
17489
17728
  else:
17490
- target_probs = mlp_probs
17729
+ target_probs = mlp_probs.copy()
17491
17730
 
17492
17731
  target_probs = target_probs[:mlp_probs.shape[0], :mlp_probs.shape[1]]
17493
17732
  target_probs = self.pipeline.model3.continuous_predictive_correction(self, target_probs, mlp_pred_indices)
@@ -17571,7 +17810,9 @@ class PipelinePredictionManager:
17571
17810
 
17572
17811
  # Transformer prediction and blending
17573
17812
  if trans_probs is not None and attn_weights is not None:
17574
- trans_probs_i = trans_probs[i]
17813
+ if i < len(trans_probs):
17814
+ trans_probs_i = trans_probs[i]
17815
+
17575
17816
  trans_class_idx = np.argmax(trans_probs_i)
17576
17817
  if isinstance(trans_probs_i, float):
17577
17818
  trans_confidence = target_confidence
@@ -17649,7 +17890,10 @@ class PipelinePredictionManager:
17649
17890
  agreement = mlp_class_idx == trans_class_idx
17650
17891
 
17651
17892
  else:
17652
- final_probs = mlp_probs[i]
17893
+ if i < len(mlp_probs):
17894
+ final_probs = mlp_probs[i]
17895
+ else:
17896
+ final_probs = mlp_probs[0]
17653
17897
 
17654
17898
  final_class_idx = target_class_idx
17655
17899
  final_confidence = target_confidence[0] if isinstance(target_confidence, np.ndarray) else target_confidence
@@ -17834,9 +18078,10 @@ class PipelinePredictionManager:
17834
18078
  'X_samples': X,
17835
18079
  'input_ids': input_ids
17836
18080
  }
18081
+ print('[=] Displaying Test Performance Results....')
17837
18082
  if titles is not None and len(titles) > 0:
17838
18083
  correct, sec_correct = self.display_hybrid_results(payload, final_class_idx, results, top_k, verbose=True)
17839
-
18084
+
17840
18085
  return results, chosen_label, confidence
17841
18086
 
17842
18087
  else:
@@ -18122,7 +18367,8 @@ class PipelinePredictionManager:
18122
18367
  else:
18123
18368
  print(f'[⚡] Final Prediction: {chosen_label} with confidence: {confidence:.1%}')
18124
18369
  chosen_label = chosen_label
18125
-
18370
+
18371
+ self.pipeline.evaluate_mlp_performance(X, y, self.label_map)
18126
18372
  # delete pipelines cache
18127
18373
  print('[🔍] Pipelines Cache Cleaned!')
18128
18374
  self.pipeline.cache.clear()
@@ -18184,8 +18430,7 @@ class PipelinePredictionManager:
18184
18430
 
18185
18431
  except Exception as e:
18186
18432
  print(f'[!] Cant check and calibrate probs based on penalty due to: {e}')
18187
- traceback.print_exc()
18188
-
18433
+
18189
18434
  return final_probs
18190
18435
 
18191
18436
 
@@ -20301,6 +20546,21 @@ def PermissiveTest():
20301
20546
  ("Watching Slack", "communication"),
20302
20547
  ("Programming in Visual Studio Code", "focused_work"),
20303
20548
  ("Watching netflix.com - Chrome", "break"),
20549
+ ("Listening to Spotify", "entertainment"),
20550
+ ("Playing Steam Game", "gaming"),
20551
+ ("Checking Discord messages", "communication"),
20552
+ ("Reading documentation on StackOverflow", "research"),
20553
+ ("Downloading files from Google Drive", "file_work"),
20554
+ ("Using Terminal to run scripts", "system_work"),
20555
+ ("Analyzing data in Excel", "data_work"),
20556
+ ("Attending Zoom meeting", "communication"),
20557
+ ("Designing in Photoshop", "creative"),
20558
+ ("Learning from Coursera course", "learning"),
20559
+ ("Using Calculator utility", "utility"),
20560
+ ("Browsing Facebook on Chrome", "social_media"),
20561
+ ("Reading an eBook in PDF format", "reading"),
20562
+ ("Listening to a podcast", "audio_learning"),
20563
+ ("Using Google Translate", "utility"),
20304
20564
  ]
20305
20565
  rules = [
20306
20566
  # === WORK / PRODUCTIVITY ===