AbstractIntegratedModule 0.6.5__tar.gz → 0.6.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- abstractintegratedmodule-0.6.7/AbstractIntegratedModule.cp313-win_amd64.pyd +0 -0
- {abstractintegratedmodule-0.6.5 → abstractintegratedmodule-0.6.7}/AbstractIntegratedModule.cpython-310-aarch64-linux-gnu.so +0 -0
- {abstractintegratedmodule-0.6.5 → abstractintegratedmodule-0.6.7}/AbstractIntegratedModule.cpython-312-x86_64-linux-gnu.so +0 -0
- {abstractintegratedmodule-0.6.5 → abstractintegratedmodule-0.6.7/AbstractIntegratedModule.egg-info}/PKG-INFO +6 -6
- {abstractintegratedmodule-0.6.5 → abstractintegratedmodule-0.6.7}/AbstractIntegratedModule.py +211 -93
- {abstractintegratedmodule-0.6.5 → abstractintegratedmodule-0.6.7}/AbstractOptimizedModules.c +152 -152
- {abstractintegratedmodule-0.6.5/AbstractIntegratedModule.egg-info → abstractintegratedmodule-0.6.7}/PKG-INFO +6 -6
- {abstractintegratedmodule-0.6.5 → abstractintegratedmodule-0.6.7}/README.md +5 -5
- {abstractintegratedmodule-0.6.5 → abstractintegratedmodule-0.6.7}/setup.py +1 -1
- abstractintegratedmodule-0.6.5/AbstractIntegratedModule.cp313-win_amd64.pyd +0 -0
- {abstractintegratedmodule-0.6.5 → abstractintegratedmodule-0.6.7}/AbstractIntegratedModule.egg-info/SOURCES.txt +0 -0
- {abstractintegratedmodule-0.6.5 → abstractintegratedmodule-0.6.7}/AbstractIntegratedModule.egg-info/dependency_links.txt +0 -0
- {abstractintegratedmodule-0.6.5 → abstractintegratedmodule-0.6.7}/AbstractIntegratedModule.egg-info/requires.txt +0 -0
- {abstractintegratedmodule-0.6.5 → abstractintegratedmodule-0.6.7}/AbstractIntegratedModule.egg-info/top_level.txt +0 -0
- {abstractintegratedmodule-0.6.5 → abstractintegratedmodule-0.6.7}/AbstractOptimizedModules.cp313-win_amd64.pyd +0 -0
- {abstractintegratedmodule-0.6.5 → abstractintegratedmodule-0.6.7}/AbstractOptimizedModules.cpython-310-aarch64-linux-gnu.so +0 -0
- {abstractintegratedmodule-0.6.5 → abstractintegratedmodule-0.6.7}/AbstractOptimizedModules.cpython-312-x86_64-linux-gnu.so +0 -0
- {abstractintegratedmodule-0.6.5 → abstractintegratedmodule-0.6.7}/AbstractOptimizedModules.pyx +0 -0
- {abstractintegratedmodule-0.6.5 → abstractintegratedmodule-0.6.7}/MANIFEST.in +0 -0
- {abstractintegratedmodule-0.6.5 → abstractintegratedmodule-0.6.7}/pyproject.toml +0 -0
- {abstractintegratedmodule-0.6.5 → abstractintegratedmodule-0.6.7}/setup.cfg +0 -0
|
Binary file
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: AbstractIntegratedModule
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.7
|
|
4
4
|
Summary: Library for Advanced Integrated Non-LLM AI Models - Optimized Backend Framework For Non-LLM AI Agent
|
|
5
5
|
Author: Micro-Novelty
|
|
6
6
|
Author-email: hernikpuspita5@gmail.com
|
|
@@ -42,7 +42,7 @@ https://github.com/Micro-Novelty/IntegratedPipeline-Specialized-Non-LLM-AI-Agent
|
|
|
42
42
|
#### Note: The README here you are reading is a direct copy from my README Repository, to download the necessary files, you can visit my Repository with the provided link above.
|
|
43
43
|
|
|
44
44
|
### Library Short Description:
|
|
45
|
-
- Development Stage: 0.6.
|
|
45
|
+
- Development Stage: 0.6.7 Official Release.
|
|
46
46
|
- Maintainer: Micro-Novelty.
|
|
47
47
|
- library Source-Code is Open-sourced on github.
|
|
48
48
|
- Purpose: Specifically Designed for providing Non-LLM AI Agent Framework for edge Devices, Optimized for ARM64 architecture.
|
|
@@ -67,11 +67,11 @@ https://github.com/Micro-Novelty/IntegratedPipeline-Specialized-Non-LLM-AI-Agent
|
|
|
67
67
|
- Robust Advanced prediction capabilities proven effective on ARM64 Using MLP + LSTM Architectures.
|
|
68
68
|
- Transformer Modules Optimized using Cython, to reduce Memory overhead and Reduce CPU Usage, With Reduced Training Time.
|
|
69
69
|
- Changelog:
|
|
70
|
-
- v0.6.
|
|
70
|
+
- v0.6.7:
|
|
71
71
|
- [=] New features:
|
|
72
|
-
-
|
|
73
|
-
|
|
74
|
-
-
|
|
72
|
+
- Fixed bug where mlp always return index 0 during prediction.
|
|
73
|
+
- Optimized processing batch in chunk function.
|
|
74
|
+
- fixed bug where num_classes doesnt match given labels.
|
|
75
75
|
-----
|
|
76
76
|
|
|
77
77
|
<img width="1280" height="600" alt="WhatsApp Image 2026-05-27 at 07 16 32" src="https://github.com/user-attachments/assets/4b58a556-45a3-419b-96fd-9c1b76cac574" />
|
{abstractintegratedmodule-0.6.5 → abstractintegratedmodule-0.6.7}/AbstractIntegratedModule.py
RENAMED
|
@@ -1684,7 +1684,7 @@ class MLP:
|
|
|
1684
1684
|
|
|
1685
1685
|
def focused_forward(self, x):
|
|
1686
1686
|
for layer in self.feed_layers:
|
|
1687
|
-
x = layer.forward(x)
|
|
1687
|
+
x = layer.forward(np.asarray(x, dtype=np.float64))
|
|
1688
1688
|
|
|
1689
1689
|
return self.softmax.forward(x)
|
|
1690
1690
|
|
|
@@ -1693,8 +1693,31 @@ class MLP:
|
|
|
1693
1693
|
x = layer.forward(x)
|
|
1694
1694
|
|
|
1695
1695
|
return self.softmax.forward(x)
|
|
1696
|
-
|
|
1697
|
-
|
|
1696
|
+
|
|
1697
|
+
|
|
1698
|
+
def _calibrate_gradient(self, grad, AME, anisotropy):
|
|
1699
|
+
std = np.std(grad)
|
|
1700
|
+
eps = 1e-5
|
|
1701
|
+
calibration = grad.copy()
|
|
1702
|
+
|
|
1703
|
+
if std > 0.1:
|
|
1704
|
+
AEL = (1.0 - AME) * anisotropy # abstraction enviromental limit
|
|
1705
|
+
PRA = (1.0 - AEL) * std # possible reflected abstraction
|
|
1706
|
+
|
|
1707
|
+
calibration = grad * AEL * PRA
|
|
1708
|
+
calibration /= np.sum(calibration)
|
|
1709
|
+
|
|
1710
|
+
calibration = np.asarray(calibration, dtype=np.float64)
|
|
1711
|
+
if np.isnan(calibration).any() or np.isinf(calibration).any():
|
|
1712
|
+
calibration = grad.copy()
|
|
1713
|
+
|
|
1714
|
+
return calibration
|
|
1715
|
+
|
|
1716
|
+
|
|
1717
|
+
|
|
1718
|
+
|
|
1719
|
+
def focused_backward(self, grad, lr, AME, anisotropy):
|
|
1720
|
+
grad = self._calibrate_gradient(grad, AME, anisotropy)
|
|
1698
1721
|
grad = self.softmax.backward(grad)
|
|
1699
1722
|
for layer in reversed(self.feed_layers):
|
|
1700
1723
|
grad = layer.backward(grad, lr)
|
|
@@ -1788,7 +1811,9 @@ class MLP:
|
|
|
1788
1811
|
return y_pred, y_true
|
|
1789
1812
|
|
|
1790
1813
|
def train(self, X, y, epochs=1000, lr=0.01, verbose=True):
|
|
1791
|
-
|
|
1814
|
+
AME = self.AME_Encoder(X)
|
|
1815
|
+
anisotropy = self.anisotropy_measurement(X)
|
|
1816
|
+
focused_fit_condition = len(self.feed_layers) > 0 and anisotropy > 0.25 and AME > 0.25
|
|
1792
1817
|
print(f'[+] Focused fit condition: {focused_fit_condition} || Anisotropy: {self.anisotropy_measurement(X):.4f} || AME: {self.AME_Encoder(X):.4f}')
|
|
1793
1818
|
|
|
1794
1819
|
for epoch in range(epochs):
|
|
@@ -1800,7 +1825,10 @@ class MLP:
|
|
|
1800
1825
|
y_pred, y_true = self.adapt_predict_shape(y_pred, y)
|
|
1801
1826
|
loss = Loss.categorical_crossentropy(y_true, y_pred)
|
|
1802
1827
|
grad = Loss.softmax_crossentropy_derivative(y_true, y_pred)
|
|
1803
|
-
|
|
1828
|
+
if focused_fit_condition:
|
|
1829
|
+
_ = self.focused_backward(grad, self.lr, AME, anisotropy)
|
|
1830
|
+
else:
|
|
1831
|
+
_ = self.backward(grad, self.lr)
|
|
1804
1832
|
|
|
1805
1833
|
if verbose and epoch % 100 == 0:
|
|
1806
1834
|
acc = np.mean(np.argmax(y_pred, axis=1) == np.argmax(y_true, axis=1))
|
|
@@ -8131,9 +8159,42 @@ class IntegratedPipeline:
|
|
|
8131
8159
|
def initialize_model_encoding(self, X, y_raw):
|
|
8132
8160
|
vocab_size = self.vocab_size
|
|
8133
8161
|
|
|
8134
|
-
|
|
8162
|
+
# canonical source first, since model output dimension
|
|
8163
|
+
# (once it exists) is the true authority on num_classes
|
|
8164
|
+
num_classes = self._get_num_classes()
|
|
8165
|
+
|
|
8166
|
+
unique_in_batch = len(np.unique(y_raw))
|
|
8167
|
+
|
|
8168
|
+
if num_classes is None:
|
|
8169
|
+
# no model exists yet — this is the legitimate case where
|
|
8170
|
+
# inferring from y_raw is correct (e.g. first-time initialization)
|
|
8171
|
+
num_classes = unique_in_batch
|
|
8172
|
+
print(f'[=] No existing model — initializing with {num_classes} classes '
|
|
8173
|
+
f'from this batch')
|
|
8174
|
+
elif unique_in_batch > num_classes:
|
|
8175
|
+
# batch contains MORE classes than the model supports
|
|
8176
|
+
# this is a real problem — can't onehot-encode into a smaller space
|
|
8177
|
+
print(f'[⚠️] Batch contains {unique_in_batch} unique classes but model '
|
|
8178
|
+
f'only supports {num_classes} — expanding to {unique_in_batch}')
|
|
8179
|
+
num_classes = unique_in_batch
|
|
8180
|
+
elif unique_in_batch < num_classes:
|
|
8181
|
+
# batch just doesn't happen to contain all classes — this is FINE,
|
|
8182
|
+
# use the model's full num_classes so onehot stays the right width
|
|
8183
|
+
print(f'[=] Batch only contains {unique_in_batch}/{num_classes} classes '
|
|
8184
|
+
f'— using full model class count for onehot width')
|
|
8185
|
+
# num_classes stays as the model's true value, no change needed
|
|
8186
|
+
|
|
8135
8187
|
y_onehot = np.zeros((len(y_raw), num_classes))
|
|
8136
|
-
|
|
8188
|
+
|
|
8189
|
+
for idx, label in enumerate(y_raw):
|
|
8190
|
+
label_idx = int(label)
|
|
8191
|
+
if 0 <= label_idx < num_classes:
|
|
8192
|
+
y_onehot[idx, label_idx] = 1.0
|
|
8193
|
+
else:
|
|
8194
|
+
print(f'[⚠️] Label {label_idx} out of range for {num_classes} classes '
|
|
8195
|
+
f'at sample {idx} — skipping onehot assignment')
|
|
8196
|
+
# leaves that row as all-zeros rather than crashing or
|
|
8197
|
+
# silently assigning to a wrong class
|
|
8137
8198
|
|
|
8138
8199
|
automatic_change = self.automatic_parameterization(vocab_size, num_classes)
|
|
8139
8200
|
self.embedding_dim = automatic_change
|
|
@@ -8144,6 +8205,9 @@ class IntegratedPipeline:
|
|
|
8144
8205
|
model.add(layer1)
|
|
8145
8206
|
model.add(layer2)
|
|
8146
8207
|
|
|
8208
|
+
model.feed_add(layer1)
|
|
8209
|
+
model.feed_add(layer2)
|
|
8210
|
+
|
|
8147
8211
|
return y_onehot
|
|
8148
8212
|
|
|
8149
8213
|
|
|
@@ -8155,8 +8219,8 @@ class IntegratedPipeline:
|
|
|
8155
8219
|
|
|
8156
8220
|
layer1= Dense(X, input_dim, automatic_change, activation="relu")
|
|
8157
8221
|
layer2 = Dense(X, automatic_change, num_classes, activation='relu')
|
|
8158
|
-
|
|
8159
|
-
abundant_layer = automatic_change * 10
|
|
8222
|
+
|
|
8223
|
+
abundant_layer = int(automatic_change * 10)
|
|
8160
8224
|
first_feed_layer = Dense(X, input_dim, abundant_layer, activation="relu")
|
|
8161
8225
|
sec_feed_layer = Dense(X, abundant_layer, num_classes, activation="relu")
|
|
8162
8226
|
|
|
@@ -8165,8 +8229,8 @@ class IntegratedPipeline:
|
|
|
8165
8229
|
self.model3.add(layer1)
|
|
8166
8230
|
self.model3.add(layer2)
|
|
8167
8231
|
|
|
8168
|
-
self.
|
|
8169
|
-
self.
|
|
8232
|
+
self.model3.feed_add(first_feed_layer)
|
|
8233
|
+
self.model3.feed_add(sec_feed_layer)
|
|
8170
8234
|
|
|
8171
8235
|
|
|
8172
8236
|
def automatic_parameterization(self, input_size, num_classes):
|
|
@@ -8630,7 +8694,6 @@ class IntegratedPipeline:
|
|
|
8630
8694
|
x = np.array(x)
|
|
8631
8695
|
x = x.reshape(x.shape[0], -1) # Flatten if necessary
|
|
8632
8696
|
|
|
8633
|
-
|
|
8634
8697
|
try:
|
|
8635
8698
|
try:
|
|
8636
8699
|
grads = np.gradient(x)
|
|
@@ -8965,14 +9028,7 @@ class IntegratedPipeline:
|
|
|
8965
9028
|
print(f'[!] Cannot convert to array: {type(value)}')
|
|
8966
9029
|
return None
|
|
8967
9030
|
|
|
8968
|
-
|
|
8969
|
-
def _get_num_classes(self) -> int:
|
|
8970
|
-
# Get number of classes from model first
|
|
8971
|
-
if hasattr(self, 'model2') and self.model2:
|
|
8972
|
-
return self.model2.output.shape[1]
|
|
8973
|
-
elif hasattr(self, 'mlp') and self.mlp.layers:
|
|
8974
|
-
return self.mlp.layers[-1].b.shape[1]
|
|
8975
|
-
return None
|
|
9031
|
+
|
|
8976
9032
|
|
|
8977
9033
|
|
|
8978
9034
|
def model_probability_gate(self, x, x2):
|
|
@@ -9113,6 +9169,19 @@ class IntegratedPipeline:
|
|
|
9113
9169
|
|
|
9114
9170
|
return batch_probs
|
|
9115
9171
|
|
|
9172
|
+
def _coerce_batch_size(self, batch_size, default=32):
|
|
9173
|
+
"""Single, clear path for batch_size type coercion."""
|
|
9174
|
+
try:
|
|
9175
|
+
arr = np.asarray(batch_size)
|
|
9176
|
+
if arr.ndim == 0:
|
|
9177
|
+
return int(arr)
|
|
9178
|
+
elif arr.size > 0:
|
|
9179
|
+
return int(arr.flat[0])
|
|
9180
|
+
else:
|
|
9181
|
+
return default
|
|
9182
|
+
except (TypeError, ValueError):
|
|
9183
|
+
return default
|
|
9184
|
+
|
|
9116
9185
|
def _batch_prediction_core(self, batch_input_ids: np.ndarray, batch_X: np.ndarray,
|
|
9117
9186
|
batch_size: Any = None, show_progress: bool = True) -> np.ndarray:
|
|
9118
9187
|
"""
|
|
@@ -9132,13 +9201,15 @@ class IntegratedPipeline:
|
|
|
9132
9201
|
except:
|
|
9133
9202
|
batch_size = int(batch_size[0][0])
|
|
9134
9203
|
except:
|
|
9135
|
-
|
|
9204
|
+
# robust single clear path when both batch size try blocks fails
|
|
9205
|
+
batch_size = self._coerce_batch_size(batch_size, default=32)
|
|
9206
|
+
|
|
9136
9207
|
else:
|
|
9137
9208
|
if isinstance(batch_size, (tuple, list, np.ndarray)):
|
|
9138
9209
|
batch_size = batch_size[0]
|
|
9139
9210
|
batch_size = len(batch_size)
|
|
9140
9211
|
else:
|
|
9141
|
-
batch_size =
|
|
9212
|
+
batch_size = batch_size = self._coerce_batch_size(batch_size, default=32)
|
|
9142
9213
|
|
|
9143
9214
|
n_samples = len(batch_input_ids)
|
|
9144
9215
|
chunks = []
|
|
@@ -9155,8 +9226,8 @@ class IntegratedPipeline:
|
|
|
9155
9226
|
chunks.append(chunk)
|
|
9156
9227
|
|
|
9157
9228
|
# ✅ Determine number of classes dynamically from first successful chunk
|
|
9158
|
-
num_classes =
|
|
9159
|
-
batch_probs =
|
|
9229
|
+
num_classes = self._get_num_classes()
|
|
9230
|
+
batch_probs = np.zeros((n_samples, num_classes))
|
|
9160
9231
|
|
|
9161
9232
|
for chunk_idx, (chunk_ids, chunk_X) in enumerate(chunks):
|
|
9162
9233
|
if show_progress:
|
|
@@ -9174,19 +9245,16 @@ class IntegratedPipeline:
|
|
|
9174
9245
|
|
|
9175
9246
|
# ✅ Determine number of classes from first successful chunk
|
|
9176
9247
|
if num_classes is None:
|
|
9177
|
-
if chunk_probs.ndim
|
|
9178
|
-
|
|
9179
|
-
else:
|
|
9180
|
-
num_classes = chunk_probs.shape[1] if chunk_probs.ndim > 1 else len(chunk_probs)
|
|
9181
|
-
|
|
9248
|
+
num_classes = chunk_probs.shape[1] if chunk_probs.ndim > 1 else 1
|
|
9249
|
+
|
|
9182
9250
|
# Initialize results array
|
|
9183
|
-
batch_probs = np.zeros((n_samples, num_classes))
|
|
9184
9251
|
print(f'\n[=] Detected {num_classes} classes from chunk {chunk_idx + 1}')
|
|
9185
9252
|
|
|
9186
9253
|
# ✅ Handle dimension mismatches
|
|
9187
9254
|
if chunk_probs.ndim == 1:
|
|
9188
9255
|
chunk_probs = chunk_probs.reshape(-1, 1)
|
|
9189
|
-
|
|
9256
|
+
|
|
9257
|
+
|
|
9190
9258
|
# ✅ If chunk has different number of classes, pad or trim
|
|
9191
9259
|
if chunk_probs.shape[1] != num_classes:
|
|
9192
9260
|
print(f'[=] Shape mismatch: chunk has {chunk_probs.shape[1]} classes, expected {num_classes}')
|
|
@@ -9227,51 +9295,71 @@ class IntegratedPipeline:
|
|
|
9227
9295
|
|
|
9228
9296
|
return batch_probs if batch_probs is not None else np.array([])
|
|
9229
9297
|
|
|
9230
|
-
def _process_batch_chunk(self, chunk_ids: np.ndarray, chunk_X: np.ndarray) ->
|
|
9298
|
+
def _process_batch_chunk(self, chunk_ids: np.ndarray, chunk_X: np.ndarray) -> Any:
|
|
9231
9299
|
"""
|
|
9232
9300
|
Process a single chunk - core batch logic with memory gate.
|
|
9233
9301
|
"""
|
|
9234
|
-
|
|
9235
9302
|
chunk_probs = self._batch_model_memory_gate(chunk_ids, chunk_X)
|
|
9236
|
-
|
|
9303
|
+
|
|
9304
|
+
num_classes = self._get_num_classes()
|
|
9305
|
+
if num_classes is None:
|
|
9306
|
+
print('[⚠️] num_classes unavailable in _process_batch_chunk — '
|
|
9307
|
+
'cannot safely process this chunk')
|
|
9308
|
+
return np.zeros((len(chunk_ids), 1)) # minimal safe fallback shape
|
|
9309
|
+
|
|
9237
9310
|
needs_fresh = [i for i, p in enumerate(chunk_probs) if p is None]
|
|
9238
|
-
|
|
9311
|
+
|
|
9239
9312
|
if needs_fresh:
|
|
9240
|
-
# Extract samples that need fresh prediction
|
|
9241
9313
|
fresh_ids = chunk_ids[needs_fresh]
|
|
9242
|
-
fresh_X
|
|
9243
|
-
|
|
9244
|
-
# Get fresh predictions from ensemble
|
|
9314
|
+
fresh_X = chunk_X[needs_fresh]
|
|
9315
|
+
|
|
9245
9316
|
fresh_probs, _ = self.ensemble.predict_ensemble(
|
|
9246
|
-
fresh_ids, fresh_X,
|
|
9247
|
-
np.zeros((len(fresh_ids),
|
|
9317
|
+
fresh_ids, fresh_X,
|
|
9318
|
+
np.zeros((len(fresh_ids), num_classes)),
|
|
9248
9319
|
method='dynamic', embedded=False
|
|
9249
9320
|
)
|
|
9250
|
-
|
|
9251
|
-
#
|
|
9321
|
+
|
|
9322
|
+
# validate fresh_probs shape before assignment
|
|
9323
|
+
fresh_probs = np.asarray(fresh_probs)
|
|
9324
|
+
if fresh_probs.ndim == 1:
|
|
9325
|
+
fresh_probs = fresh_probs[np.newaxis, :]
|
|
9326
|
+
|
|
9327
|
+
cached_count = 0 # track global cache count
|
|
9328
|
+
|
|
9252
9329
|
for i, fresh_idx in enumerate(needs_fresh):
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
#
|
|
9256
|
-
if
|
|
9330
|
+
row = fresh_probs[i]
|
|
9331
|
+
|
|
9332
|
+
# guard — ensure row matches expected width before storing
|
|
9333
|
+
if row.shape[0] != num_classes:
|
|
9334
|
+
aligned = np.zeros(num_classes)
|
|
9335
|
+
min_len = min(row.shape[0], num_classes)
|
|
9336
|
+
aligned[:min_len] = row[:min_len]
|
|
9337
|
+
row = aligned
|
|
9338
|
+
|
|
9339
|
+
chunk_probs[fresh_idx] = row
|
|
9340
|
+
|
|
9341
|
+
# cap caching meaningfully
|
|
9342
|
+
if cached_count < 2:
|
|
9257
9343
|
self.modular_prediction_saving(
|
|
9258
9344
|
fresh_ids[i:i+1],
|
|
9259
9345
|
fresh_X[i:i+1],
|
|
9260
|
-
|
|
9346
|
+
row[np.newaxis, :]
|
|
9261
9347
|
)
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
|
|
9348
|
+
cached_count += 1
|
|
9349
|
+
|
|
9350
|
+
|
|
9351
|
+
return np.array([
|
|
9352
|
+
p if p is not None else np.zeros(num_classes)
|
|
9353
|
+
for p in chunk_probs
|
|
9354
|
+
])
|
|
9266
9355
|
|
|
9267
9356
|
|
|
9268
9357
|
|
|
9269
|
-
def _calculate_optimal_batch_size(self, batch_input_ids: np.ndarray, batch_X: Any=None) ->
|
|
9358
|
+
def _calculate_optimal_batch_size(self, batch_input_ids: np.ndarray, batch_X: Any=None) -> Any:
|
|
9270
9359
|
"""
|
|
9271
9360
|
Calculate optimal batch size based on available memory.
|
|
9272
9361
|
"""
|
|
9273
9362
|
try:
|
|
9274
|
-
import psutil
|
|
9275
9363
|
# Estimate memory per sample
|
|
9276
9364
|
sample_size = batch_input_ids[0].nbytes + batch_X[0].nbytes if hasattr(batch_X, '__len__') else 1024
|
|
9277
9365
|
available_memory = psutil.virtual_memory().available
|
|
@@ -9281,35 +9369,47 @@ class IntegratedPipeline:
|
|
|
9281
9369
|
# Fallback to conservative batch size
|
|
9282
9370
|
return 32
|
|
9283
9371
|
|
|
9284
|
-
def _get_num_classes(self) ->
|
|
9285
|
-
|
|
9286
|
-
|
|
9287
|
-
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
|
|
9292
|
-
|
|
9293
|
-
|
|
9294
|
-
|
|
9295
|
-
|
|
9296
|
-
|
|
9297
|
-
|
|
9298
|
-
|
|
9299
|
-
|
|
9300
|
-
|
|
9301
|
-
|
|
9302
|
-
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9372
|
+
def _get_num_classes(self, label_map: dict = None, mlp_probs: np.ndarray = None) -> Any:
|
|
9373
|
+
"""
|
|
9374
|
+
Single source of truth for num_classes across the entire pipeline.
|
|
9375
|
+
|
|
9376
|
+
Resolution priority:
|
|
9377
|
+
1. Actual model output shape (model2.output or mlp final layer)
|
|
9378
|
+
— this is authoritative since argmax indices are bounded by this
|
|
9379
|
+
2. label_map length — used only as cross-check / fallback
|
|
9380
|
+
3. mlp_probs.shape[1] — used only as last-resort fallback
|
|
9381
|
+
|
|
9382
|
+
Logs a warning if sources disagree, since that disagreement
|
|
9383
|
+
is what causes invalid prediction indices downstream.
|
|
9384
|
+
"""
|
|
9385
|
+
model_classes = None
|
|
9386
|
+
|
|
9387
|
+
# primary source — actual model output dimension
|
|
9388
|
+
if hasattr(self, 'model2') and self.model2:
|
|
9389
|
+
model_classes = self.model2.output.shape[1]
|
|
9390
|
+
elif hasattr(self, 'mlp') and self.mlp.layers:
|
|
9391
|
+
model_classes = self.mlp.layers[-1].b.shape[1]
|
|
9392
|
+
|
|
9393
|
+
# cross-check against label_map if provided
|
|
9394
|
+
if label_map is not None:
|
|
9395
|
+
label_classes = len(label_map)
|
|
9396
|
+
if model_classes is not None and label_classes != model_classes:
|
|
9397
|
+
print(f'[⚠️] num_classes mismatch: model={model_classes} '
|
|
9398
|
+
f'label_map={label_classes} — using model output as source of truth')
|
|
9399
|
+
elif model_classes is None:
|
|
9400
|
+
model_classes = label_classes
|
|
9401
|
+
|
|
9402
|
+
# cross-check against mlp_probs if provided
|
|
9403
|
+
if mlp_probs is not None:
|
|
9404
|
+
probs_classes = mlp_probs.shape[1] if mlp_probs.ndim > 1 else len(mlp_probs[0])
|
|
9405
|
+
if model_classes is not None and probs_classes != model_classes:
|
|
9406
|
+
print(f'[⚠️] num_classes mismatch: model={model_classes} '
|
|
9407
|
+
f'mlp_probs={probs_classes} — using model output as source of truth')
|
|
9408
|
+
elif model_classes is None:
|
|
9409
|
+
model_classes = probs_classes
|
|
9410
|
+
|
|
9411
|
+
return model_classes
|
|
9309
9412
|
|
|
9310
|
-
print('[!] Could not determine num_classes, defaulting to 0')
|
|
9311
|
-
return 0
|
|
9312
|
-
|
|
9313
9413
|
|
|
9314
9414
|
def predict_async(self, text, callback=None):
|
|
9315
9415
|
try:
|
|
@@ -10196,7 +10296,7 @@ class IntegratedPipeline:
|
|
|
10196
10296
|
if not self.model2:
|
|
10197
10297
|
intents = [d[1] for d in datasets]
|
|
10198
10298
|
intent_to_id = {intent:i for i, intent in enumerate(sorted(set(intents)))}
|
|
10199
|
-
num_classes =
|
|
10299
|
+
num_classes = self._get_num_classes()
|
|
10200
10300
|
self.model2 = Transformer(
|
|
10201
10301
|
vocab_size=len(self.vocab),
|
|
10202
10302
|
d_model=32,
|
|
@@ -12532,7 +12632,7 @@ class PipelinePredictionManager:
|
|
|
12532
12632
|
self.pipeline.labels = label_map
|
|
12533
12633
|
|
|
12534
12634
|
reverse_map = {v: k for k, v in label_map.items()}
|
|
12535
|
-
num_classes =
|
|
12635
|
+
num_classes = self.pipeline._get_num_classes(label_map=label_map)
|
|
12536
12636
|
|
|
12537
12637
|
if X is None and y is None or X is None or y is None:
|
|
12538
12638
|
print('[🔄] Creating automatic X samples because X is not provided manually.')
|
|
@@ -12588,7 +12688,7 @@ class PipelinePredictionManager:
|
|
|
12588
12688
|
# Validate all MLP predictions at once
|
|
12589
12689
|
mlp_pred_indices = np.argmax(mlp_probs, axis=1)
|
|
12590
12690
|
if num_classes <= 0:
|
|
12591
|
-
num_classes =
|
|
12691
|
+
num_classes = self.pipeline._get_num_classes(mlp_probs=mlp_probs)
|
|
12592
12692
|
|
|
12593
12693
|
valid_mask = mlp_pred_indices < num_classes
|
|
12594
12694
|
if not np.all(valid_mask):
|
|
@@ -12776,8 +12876,13 @@ class PipelinePredictionManager:
|
|
|
12776
12876
|
verbose = False
|
|
12777
12877
|
if float(results[0]['confidence']) < self.pipeline.confidence_threshold:
|
|
12778
12878
|
verbose = True
|
|
12779
|
-
|
|
12780
|
-
|
|
12879
|
+
|
|
12880
|
+
payload = {
|
|
12881
|
+
'X_samples': X,
|
|
12882
|
+
'input_ids': input_ids
|
|
12883
|
+
}
|
|
12884
|
+
|
|
12885
|
+
self.display_hybrid_results(payload, results, top_k, verbose=verbose)
|
|
12781
12886
|
|
|
12782
12887
|
|
|
12783
12888
|
# Use results directly - they already contain calibrated predictions
|
|
@@ -13052,7 +13157,7 @@ class PipelinePredictionManager:
|
|
|
13052
13157
|
print("\n[🚀] Starting Advanced Hybrid Prediction Method")
|
|
13053
13158
|
|
|
13054
13159
|
reverse_map = {v: k for k, v in label_map.items()}
|
|
13055
|
-
num_classes =
|
|
13160
|
+
num_classes = self.pipeline._get_num_classes(label_map=label_map)
|
|
13056
13161
|
|
|
13057
13162
|
self.pipeline.titles = titles
|
|
13058
13163
|
self.pipeline.labels = label_map
|
|
@@ -13118,7 +13223,7 @@ class PipelinePredictionManager:
|
|
|
13118
13223
|
# Validate all MLP predictions at once
|
|
13119
13224
|
mlp_pred_indices = np.argmax(mlp_probs, axis=1)
|
|
13120
13225
|
if num_classes <= 0:
|
|
13121
|
-
num_classes =
|
|
13226
|
+
num_classes = self.pipeline._get_num_classes(mlp_probs=mlp_probs)
|
|
13122
13227
|
|
|
13123
13228
|
valid_mask = mlp_pred_indices < num_classes
|
|
13124
13229
|
if not np.all(valid_mask):
|
|
@@ -13191,10 +13296,23 @@ class PipelinePredictionManager:
|
|
|
13191
13296
|
expected_label = None
|
|
13192
13297
|
|
|
13193
13298
|
# MLP prediction
|
|
13194
|
-
mlp_class_idx = mlp_pred_indices[i]
|
|
13195
|
-
|
|
13196
|
-
if
|
|
13197
|
-
mlp_class_idx
|
|
13299
|
+
mlp_class_idx = int(mlp_pred_indices[i])
|
|
13300
|
+
is_valid_index = 0 <= mlp_class_idx < num_classes
|
|
13301
|
+
if not is_valid_index:
|
|
13302
|
+
print(f'[⚠️] Invalid mlp_class_idx={mlp_class_idx} for sample {i} '
|
|
13303
|
+
f'(num_classes={num_classes}, title="{display_title}") '
|
|
13304
|
+
f'— marking as low-confidence unknown, NOT defaulting to class 0')
|
|
13305
|
+
|
|
13306
|
+
results.append({
|
|
13307
|
+
'title' : display_title,
|
|
13308
|
+
'expected' : expected_label,
|
|
13309
|
+
'predicted' : None, # explicit unknown, not a fake class
|
|
13310
|
+
'confidence' : 0.0,
|
|
13311
|
+
'mlp_class' : None,
|
|
13312
|
+
'is_valid' : False,
|
|
13313
|
+
'error' : f'class_index_out_of_range(idx={mlp_class_idx}, num_classes={num_classes})'
|
|
13314
|
+
})
|
|
13315
|
+
continue
|
|
13198
13316
|
|
|
13199
13317
|
mlp_confidence = mlp_probs[i][mlp_class_idx]
|
|
13200
13318
|
mlp_label = reverse_map.get(mlp_class_idx, f"unknown_{mlp_class_idx}")
|