AbstractIntegratedModule 0.7.6__tar.gz → 0.7.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.7.6 → abstractintegratedmodule-0.7.7}/AbstractIntegratedModule.egg-info/PKG-INFO +7 -5
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/AbstractIntegratedModule.py +321 -104
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/AbstractOptimizedModules.c +352 -301
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/PKG-INFO +7 -5
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/README.md +6 -4
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/setup.py +1 -1
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/AbstractIntegratedModule.egg-info/SOURCES.txt +0 -0
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/AbstractIntegratedModule.egg-info/dependency_links.txt +0 -0
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/AbstractIntegratedModule.egg-info/requires.txt +0 -0
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/AbstractIntegratedModule.egg-info/top_level.txt +0 -0
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/AbstractOptimizedModules.pyx +0 -0
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/MANIFEST.in +0 -0
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/abstract_model_storage/Cargo.toml +0 -0
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/abstract_model_storage/pyproject.toml +0 -0
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/abstract_model_storage/src/lib.rs +0 -0
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/abstract_model_storage/target/debug/build/libsqlite3-sys-ed07b882cd2aa5e2/out/bindgen.rs +0 -0
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/abstract_model_storage/target/debug/build/serde_core-ebc15f2e9cad7f5f/out/private.rs +0 -0
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/abstract_model_storage/target/debug/build/target-lexicon-08527f45de28143d/out/host.rs +0 -0
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/abstract_model_storage/target/release/build/libsqlite3-sys-bf0400df4523274c/out/bindgen.rs +0 -0
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/abstract_model_storage/target/release/build/serde_core-5cdb76131825e4af/out/private.rs +0 -0
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/abstract_model_storage/target/release/build/target-lexicon-43eb95a0588bf457/out/host.rs +0 -0
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/pyproject.toml +0 -0
- {abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: AbstractIntegratedModule
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.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.7.
|
|
45
|
+
- Development Stage: 0.7.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.
|
|
@@ -66,10 +66,12 @@ https://github.com/Micro-Novelty/IntegratedPipeline-Specialized-Non-LLM-AI-Agent
|
|
|
66
66
|
- Robust Advanced prediction capabilities proven effective on ARM64 Using MLP + LSTM Architectures.
|
|
67
67
|
- Transformer Modules Optimized using Cython, to reduce Memory overhead and Reduce CPU Usage, With Reduced Training Time.
|
|
68
68
|
- Changelog:
|
|
69
|
-
- v0.7.
|
|
69
|
+
- v0.7.7:
|
|
70
70
|
- [=] New features:
|
|
71
|
-
-
|
|
72
|
-
-
|
|
71
|
+
- Fixed shape handling fragility in batching predictions.
|
|
72
|
+
- Ensuring proper number of classes during batcthing predictions to prevent shape error.
|
|
73
|
+
- Fixed text encoder and encode function that can cause token mismatch.
|
|
74
|
+
|
|
73
75
|
|
|
74
76
|
-----
|
|
75
77
|
|
{abstractintegratedmodule-0.7.6 → abstractintegratedmodule-0.7.7}/AbstractIntegratedModule.py
RENAMED
|
@@ -8057,7 +8057,11 @@ class IntegratedPipeline:
|
|
|
8057
8057
|
self._agent_mode = os.environ.get('AGENT_MODE', 'single')
|
|
8058
8058
|
self._agent_port = int(os.environ.get('AGENT_PORT', 5555))
|
|
8059
8059
|
self._use_async = os.environ.get('USE_ASYNC_QUEUE', 'true').lower() == 'true'
|
|
8060
|
-
|
|
8060
|
+
|
|
8061
|
+
# Special token indices — reserve before any real words
|
|
8062
|
+
self._PAD_IDX = 0
|
|
8063
|
+
self._UNK_IDX = 1
|
|
8064
|
+
self._SPECIAL_TOKENS = {'[PAD]': self._PAD_IDX, '[UNK]': self._UNK_IDX}
|
|
8061
8065
|
|
|
8062
8066
|
print(f'[= PORT =] IntegratedPipeline initialized on port {self._agent_port}')
|
|
8063
8067
|
|
|
@@ -8087,9 +8091,12 @@ class IntegratedPipeline:
|
|
|
8087
8091
|
self.autonomous = False
|
|
8088
8092
|
self.show_explainability_details = True
|
|
8089
8093
|
self.froze_learning = False
|
|
8094
|
+
self._cache_save_count = None
|
|
8095
|
+
self._prob_save_count = None
|
|
8090
8096
|
|
|
8091
8097
|
self.temperature = 1.0
|
|
8092
8098
|
self.transformer_lr = 0.1
|
|
8099
|
+
self.max_seq_len = 16
|
|
8093
8100
|
|
|
8094
8101
|
self.memory_name = memory_name
|
|
8095
8102
|
|
|
@@ -8280,7 +8287,7 @@ class IntegratedPipeline:
|
|
|
8280
8287
|
f'only supports {num_classes} — expanding to {unique_in_batch}')
|
|
8281
8288
|
num_classes = unique_in_batch
|
|
8282
8289
|
elif unique_in_batch < num_classes:
|
|
8283
|
-
# batch just doesn't happen to contain all classes
|
|
8290
|
+
# batch just doesn't happen to contain all classes.
|
|
8284
8291
|
# use the model's full num_classes so onehot stays the right width
|
|
8285
8292
|
print(f'[=] Batch only contains {unique_in_batch}/{num_classes} classes '
|
|
8286
8293
|
f'— using full model class count for onehot width')
|
|
@@ -8352,40 +8359,115 @@ class IntegratedPipeline:
|
|
|
8352
8359
|
return layer1, layer2
|
|
8353
8360
|
|
|
8354
8361
|
|
|
8362
|
+
|
|
8355
8363
|
def text_encoder(self, texts):
|
|
8364
|
+
"""
|
|
8365
|
+
Build vocabulary from texts incrementally.
|
|
8366
|
+
Reserves 0=[PAD], 1=[UNK].
|
|
8367
|
+
"""
|
|
8356
8368
|
vocab = self.vocab
|
|
8357
|
-
|
|
8369
|
+
|
|
8370
|
+
if not vocab:
|
|
8371
|
+
vocab.update(self._SPECIAL_TOKENS)
|
|
8372
|
+
|
|
8373
|
+
# track next free index from actual max value in vocab,
|
|
8374
|
+
# not len() which breaks if any entries were ever removed
|
|
8375
|
+
idx = max(vocab.values()) + 1 if vocab else len(self._SPECIAL_TOKENS)
|
|
8376
|
+
|
|
8358
8377
|
for item in texts:
|
|
8359
|
-
|
|
8360
|
-
|
|
8378
|
+
text = item[0] if isinstance(item, tuple) else item
|
|
8379
|
+
if not isinstance(text, str) or not text.strip():
|
|
8380
|
+
continue # skip None/empty safely
|
|
8381
|
+
|
|
8382
|
+
for word in text.lower().split():
|
|
8361
8383
|
if word not in vocab:
|
|
8362
8384
|
vocab[word] = idx
|
|
8363
8385
|
idx += 1
|
|
8364
8386
|
|
|
8365
8387
|
|
|
8366
|
-
def encode(self, sentence, vocab, max_len=
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
|
|
8372
|
-
|
|
8388
|
+
def encode(self, sentence, vocab, max_len=None):
|
|
8389
|
+
"""
|
|
8390
|
+
Encode sentence to token ids.
|
|
8391
|
+
Unknown words → [UNK] (1). Shorter sequences → [PAD] (0) padded.
|
|
8392
|
+
"""
|
|
8393
|
+
# guard against None/empty input
|
|
8394
|
+
if not isinstance(sentence, str) or not sentence.strip():
|
|
8395
|
+
pad_len = max_len or 6
|
|
8396
|
+
return [self._PAD_IDX] * pad_len
|
|
8397
|
+
|
|
8398
|
+
# dynamic max_len from vocab's seen sentence lengths
|
|
8399
|
+
# if not explicitly provided, derive from pipeline config or vocab size
|
|
8400
|
+
if max_len is None:
|
|
8401
|
+
max_len = getattr(self, 'max_seq_len', 16) # configurable, default 16 not 6
|
|
8402
|
+
|
|
8403
|
+
tokens = sentence.lower().split()
|
|
8404
|
+
ids = [vocab.get(w, self._UNK_IDX) for w in tokens]
|
|
8405
|
+
|
|
8406
|
+
# informative truncation warning with actual token count
|
|
8407
|
+
if len(tokens) > max_len:
|
|
8408
|
+
print(f'[!] Truncated "{sentence[:40]}..." '
|
|
8409
|
+
f'({len(tokens)} tokens → {max_len})')
|
|
8373
8410
|
|
|
8411
|
+
# pad or truncate to exact max_len
|
|
8412
|
+
ids = ids[:max_len]
|
|
8413
|
+
ids.extend([self._PAD_IDX] * (max_len - len(ids)))
|
|
8374
8414
|
|
|
8415
|
+
return ids
|
|
8375
8416
|
|
|
8376
8417
|
def input_encoding(self, datasets):
|
|
8377
|
-
texts
|
|
8418
|
+
texts = [d[0] for d in datasets]
|
|
8378
8419
|
intents = [d[1] for d in datasets]
|
|
8379
|
-
|
|
8380
|
-
|
|
8420
|
+
|
|
8421
|
+
intent_to_id = {intent: i for i, intent in enumerate(sorted(set(intents)))}
|
|
8422
|
+
batch_classes = len(intent_to_id)
|
|
8423
|
+
|
|
8424
|
+
model_classes = self._get_num_classes()
|
|
8425
|
+
|
|
8426
|
+
if model_classes is None:
|
|
8427
|
+
num_classes = batch_classes
|
|
8428
|
+
print(f'[=] No existing model — using batch class count: {num_classes}')
|
|
8429
|
+
|
|
8430
|
+
elif batch_classes > model_classes:
|
|
8431
|
+
print(f'[⚠️] input_encoding: batch has {batch_classes} classes '
|
|
8432
|
+
f'but model only supports {model_classes} — '
|
|
8433
|
+
f'expanding num_classes to {batch_classes}')
|
|
8434
|
+
num_classes = batch_classes
|
|
8435
|
+
|
|
8436
|
+
elif batch_classes < model_classes:
|
|
8437
|
+
# batch is a SUBSET of known classes
|
|
8438
|
+
print(f'[=] input_encoding: batch has {batch_classes}/{model_classes} '
|
|
8439
|
+
f'classes — using full model class count')
|
|
8440
|
+
num_classes = model_classes
|
|
8441
|
+
|
|
8442
|
+
# remap intent_to_id to GLOBAL indices from the stored label_map
|
|
8443
|
+
# so index 2 in this batch actually means class 2 globally,
|
|
8444
|
+
if hasattr(self, 'label_map') and self.label_map:
|
|
8445
|
+
remapped = {}
|
|
8446
|
+
for intent in intent_to_id:
|
|
8447
|
+
if intent in self.label_map:
|
|
8448
|
+
remapped[intent] = self.label_map[intent]
|
|
8449
|
+
else:
|
|
8450
|
+
# genuinely new intent not in global map — append at end
|
|
8451
|
+
remapped[intent] = max(self.label_map.values()) + 1
|
|
8452
|
+
print(f'[⚠️] Unknown intent "{intent}" not in label_map — '
|
|
8453
|
+
f'assigned index {remapped[intent]}')
|
|
8454
|
+
intent_to_id = remapped
|
|
8455
|
+
else:
|
|
8456
|
+
num_classes = model_classes
|
|
8457
|
+
|
|
8381
8458
|
labels = [intent_to_id[i] for i in intents]
|
|
8382
8459
|
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8460
|
+
# validate all labels are in range
|
|
8461
|
+
max_label = max(labels) if labels else 0
|
|
8462
|
+
if max_label >= num_classes:
|
|
8463
|
+
print(f'[⚠️] Max label index {max_label} >= num_classes {num_classes} '
|
|
8464
|
+
f'— expanding num_classes to {max_label + 1}')
|
|
8465
|
+
num_classes = max_label + 1
|
|
8386
8466
|
|
|
8387
|
-
|
|
8388
|
-
|
|
8467
|
+
reverse_map = {i: intent for intent, i in intent_to_id.items()}
|
|
8468
|
+
|
|
8469
|
+
self.texts = texts
|
|
8470
|
+
self.intents = intents
|
|
8389
8471
|
self.reverse_map = reverse_map
|
|
8390
8472
|
|
|
8391
8473
|
self.model2 = Transformer(
|
|
@@ -8393,17 +8475,21 @@ class IntegratedPipeline:
|
|
|
8393
8475
|
d_model=32,
|
|
8394
8476
|
n_heads=4,
|
|
8395
8477
|
num_classes=num_classes
|
|
8396
|
-
)
|
|
8478
|
+
)
|
|
8397
8479
|
|
|
8480
|
+
# safe y_true construction with explicit bounds guard per row
|
|
8398
8481
|
y_true = np.zeros((len(labels), num_classes))
|
|
8399
|
-
for i,l in enumerate(labels):
|
|
8400
|
-
|
|
8401
|
-
|
|
8402
|
-
|
|
8482
|
+
for i, l in enumerate(labels):
|
|
8483
|
+
if 0 <= l < num_classes:
|
|
8484
|
+
y_true[i, l] = 1.0
|
|
8485
|
+
else:
|
|
8486
|
+
print(f'[⚠️] Label {l} out of range for num_classes={num_classes} '
|
|
8487
|
+
f'at sample {i} (intent="{intents[i]}") — row left as zeros')
|
|
8403
8488
|
|
|
8404
|
-
input_ids_list = [
|
|
8405
|
-
|
|
8406
|
-
|
|
8489
|
+
input_ids_list = [
|
|
8490
|
+
np.array(self.encode(text, self.vocab))
|
|
8491
|
+
for text in texts
|
|
8492
|
+
]
|
|
8407
8493
|
|
|
8408
8494
|
return input_ids_list, y_true
|
|
8409
8495
|
|
|
@@ -9553,107 +9639,212 @@ class IntegratedPipeline:
|
|
|
9553
9639
|
|
|
9554
9640
|
def _batch_hybrid_prediction(self, batch_input_ids, batch_X_raw, y_true):
|
|
9555
9641
|
print('[+] Initiating hybrid prediction batching...')
|
|
9556
|
-
|
|
9642
|
+
|
|
9643
|
+
num_classes = self._get_num_classes()
|
|
9644
|
+
if num_classes is None:
|
|
9645
|
+
print('[⚠️] num_classes unavailable — cannot safely batch predict')
|
|
9646
|
+
return np.zeros((len(batch_input_ids), 1))
|
|
9647
|
+
|
|
9648
|
+
zero_row = np.zeros(num_classes) # reusable fallback shape
|
|
9557
9649
|
batch_probs = self._batch_model_memory_gate(batch_input_ids, batch_X_raw)
|
|
9558
|
-
|
|
9650
|
+
fresh_probs = None # FIX 1 — explicit init, no UnboundLocalError
|
|
9651
|
+
|
|
9559
9652
|
needs_prediction = [i for i, p in enumerate(batch_probs) if p is None]
|
|
9560
|
-
|
|
9653
|
+
|
|
9561
9654
|
if needs_prediction:
|
|
9562
9655
|
fresh_input_ids = batch_input_ids[needs_prediction]
|
|
9563
|
-
fresh_X_raw
|
|
9564
|
-
|
|
9565
|
-
#
|
|
9656
|
+
fresh_X_raw = batch_X_raw[needs_prediction]
|
|
9657
|
+
|
|
9658
|
+
# slice y_true to match the fresh subset only
|
|
9659
|
+
if y_true is not None and hasattr(y_true, '__len__'):
|
|
9660
|
+
fresh_y_true = y_true[needs_prediction] \
|
|
9661
|
+
if len(y_true) == len(batch_input_ids) \
|
|
9662
|
+
else np.zeros((len(needs_prediction), num_classes))
|
|
9663
|
+
else:
|
|
9664
|
+
fresh_y_true = np.zeros((len(needs_prediction), num_classes))
|
|
9665
|
+
|
|
9566
9666
|
fresh_probs, details = self.ensemble.predict_ensemble(
|
|
9567
|
-
fresh_input_ids, fresh_X_raw,
|
|
9667
|
+
fresh_input_ids, fresh_X_raw, fresh_y_true,
|
|
9668
|
+
method='dynamic', embedded=False
|
|
9568
9669
|
)
|
|
9569
|
-
|
|
9670
|
+
|
|
9671
|
+
# validate fresh_probs shape before assignment
|
|
9672
|
+
fresh_probs = np.asarray(fresh_probs)
|
|
9673
|
+
if fresh_probs.ndim == 1:
|
|
9674
|
+
fresh_probs = fresh_probs[np.newaxis, :]
|
|
9675
|
+
|
|
9570
9676
|
for i, idx in enumerate(needs_prediction):
|
|
9571
|
-
|
|
9572
|
-
|
|
9573
|
-
if
|
|
9677
|
+
row = fresh_probs[i]
|
|
9678
|
+
|
|
9679
|
+
# align to expected num_classes if shape drifted
|
|
9680
|
+
if row.shape[0] != num_classes:
|
|
9681
|
+
aligned = np.zeros(num_classes)
|
|
9682
|
+
min_len = min(row.shape[0], num_classes)
|
|
9683
|
+
aligned[:min_len] = row[:min_len]
|
|
9684
|
+
row = aligned
|
|
9685
|
+
|
|
9686
|
+
batch_probs[idx] = row
|
|
9687
|
+
|
|
9688
|
+
# instance-level counter
|
|
9689
|
+
if getattr(self, '_cache_save_count', 0) < 2:
|
|
9574
9690
|
self.modular_prediction_saving(
|
|
9575
|
-
fresh_input_ids[i:i+1],
|
|
9576
|
-
fresh_X_raw[i:i+1],
|
|
9577
|
-
|
|
9691
|
+
fresh_input_ids[i:i+1],
|
|
9692
|
+
fresh_X_raw[i:i+1],
|
|
9693
|
+
row[np.newaxis, :]
|
|
9578
9694
|
)
|
|
9695
|
+
self._cache_save_count = getattr(self, '_cache_save_count', 0) + 1
|
|
9579
9696
|
|
|
9697
|
+
# build valid_probs with guaranteed consistent shape
|
|
9580
9698
|
valid_probs = []
|
|
9581
|
-
for p in batch_probs:
|
|
9699
|
+
for i, p in enumerate(batch_probs):
|
|
9582
9700
|
if p is None:
|
|
9583
|
-
#
|
|
9584
|
-
valid_probs.append(
|
|
9701
|
+
# zero_row always defined, no UnboundLocalError
|
|
9702
|
+
valid_probs.append(zero_row.copy())
|
|
9585
9703
|
elif isinstance(p, list):
|
|
9586
|
-
|
|
9587
|
-
|
|
9704
|
+
arr = np.array(p, dtype=np.float64)
|
|
9705
|
+
if arr.shape[0] != num_classes:
|
|
9706
|
+
aligned = np.zeros(num_classes)
|
|
9707
|
+
aligned[:min(arr.shape[0], num_classes)] = arr[:num_classes]
|
|
9708
|
+
arr = aligned
|
|
9709
|
+
valid_probs.append(arr)
|
|
9710
|
+
elif isinstance(p, np.ndarray):
|
|
9711
|
+
if p.shape[0] != num_classes:
|
|
9712
|
+
aligned = np.zeros(num_classes)
|
|
9713
|
+
aligned[:min(p.shape[0], num_classes)] = p[:num_classes]
|
|
9714
|
+
p = aligned
|
|
9588
9715
|
valid_probs.append(p)
|
|
9589
|
-
|
|
9716
|
+
else:
|
|
9717
|
+
print(f'[⚠️] Unexpected type in batch_probs[{i}]: {type(p)} — using zeros')
|
|
9718
|
+
valid_probs.append(zero_row.copy())
|
|
9719
|
+
|
|
9720
|
+
# single explicit conversion with clear error, no nested bare except
|
|
9721
|
+
if not valid_probs:
|
|
9722
|
+
print('[⚠️] No valid probabilities collected — returning zeros')
|
|
9723
|
+
return np.zeros((len(batch_input_ids), num_classes))
|
|
9724
|
+
|
|
9590
9725
|
try:
|
|
9591
|
-
|
|
9592
|
-
|
|
9593
|
-
|
|
9594
|
-
|
|
9595
|
-
except
|
|
9596
|
-
print(f'[
|
|
9726
|
+
result = np.stack(valid_probs) # stack guarantees shape (N, num_classes)
|
|
9727
|
+
# np.array() on ragged list gives object array
|
|
9728
|
+
# np.stack() fails fast if shapes disagree
|
|
9729
|
+
return result
|
|
9730
|
+
except ValueError as e:
|
|
9731
|
+
print(f'[⚠️] Stack failed (shape mismatch): {e} — '
|
|
9732
|
+
f'shapes: {[p.shape for p in valid_probs]}')
|
|
9733
|
+
# last resort return
|
|
9597
9734
|
return valid_probs
|
|
9598
|
-
|
|
9735
|
+
|
|
9599
9736
|
|
|
9600
9737
|
def _batch_predict_proba(self, batch_input_ids, batch_X, type='Hybrid'):
|
|
9601
9738
|
batch_size = len(batch_input_ids)
|
|
9602
|
-
idx_total = 0
|
|
9603
9739
|
|
|
9604
|
-
|
|
9740
|
+
output_memory = self._batch_model_memory_gate(batch_input_ids, batch_X)
|
|
9741
|
+
num_classes = self._get_num_classes()
|
|
9742
|
+
if num_classes is None:
|
|
9743
|
+
print('[⚠️] num_classes unavailable in prediction batching function!')
|
|
9744
|
+
return np.zeros((batch_size, 1))
|
|
9745
|
+
|
|
9746
|
+
zero_row = np.zeros(num_classes)
|
|
9605
9747
|
batch_probs = [None] * batch_size
|
|
9748
|
+
fresh_probs = None # explicit init, no UnboundLocalError
|
|
9749
|
+
|
|
9750
|
+
|
|
9606
9751
|
for i in range(batch_size):
|
|
9607
9752
|
probs = self.model_probability_gate(
|
|
9608
|
-
batch_input_ids[i:i+1],
|
|
9753
|
+
batch_input_ids[i:i+1],
|
|
9609
9754
|
batch_X[i:i+1]
|
|
9610
9755
|
)
|
|
9611
9756
|
if probs is not None:
|
|
9612
9757
|
batch_probs[i] = probs[0]
|
|
9613
|
-
|
|
9614
|
-
|
|
9758
|
+
|
|
9759
|
+
|
|
9615
9760
|
needs_prediction = [i for i, p in enumerate(batch_probs) if p is None]
|
|
9616
|
-
|
|
9761
|
+
|
|
9617
9762
|
if needs_prediction:
|
|
9618
9763
|
fresh_input_ids = batch_input_ids[needs_prediction]
|
|
9619
|
-
fresh_X
|
|
9620
|
-
|
|
9621
|
-
# Batch transformer and MLP
|
|
9764
|
+
fresh_X = batch_X[needs_prediction]
|
|
9765
|
+
|
|
9622
9766
|
transformer_pred, fresh_probs, attn_weights = self.model2.predict(fresh_input_ids)
|
|
9623
9767
|
mlp_pred = self.mlp.forward(fresh_X)
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
9628
|
-
|
|
9768
|
+
|
|
9769
|
+
# coerce indices to int
|
|
9770
|
+
mlp_pred_indices = np.argmax(mlp_pred, axis=1).astype(int)
|
|
9771
|
+
trans_pred_indices = np.argmax(fresh_probs, axis=1).astype(int)
|
|
9772
|
+
|
|
9629
9773
|
for i, idx in enumerate(needs_prediction):
|
|
9630
|
-
|
|
9631
|
-
|
|
9774
|
+
|
|
9775
|
+
# validate indices in range before use
|
|
9776
|
+
mlp_cls = int(mlp_pred_indices[i])
|
|
9777
|
+
trans_cls = int(trans_pred_indices[i])
|
|
9778
|
+
|
|
9779
|
+
mlp_cls = mlp_cls if 0 <= mlp_cls < num_classes else 0
|
|
9780
|
+
trans_cls = trans_cls if 0 <= trans_cls < num_classes else 0
|
|
9781
|
+
|
|
9782
|
+
if mlp_cls != trans_cls:
|
|
9632
9783
|
calibrated = self._calibrate_probs(
|
|
9633
|
-
fresh_probs[i:i+1],
|
|
9634
|
-
[
|
|
9784
|
+
fresh_probs[i:i+1],
|
|
9785
|
+
[mlp_cls],
|
|
9635
9786
|
attn_weights[i:i+1] if attn_weights is not None else None,
|
|
9636
9787
|
fresh_input_ids[i:i+1]
|
|
9637
9788
|
)
|
|
9638
|
-
|
|
9789
|
+
# validate calibrated output shape
|
|
9790
|
+
row = np.asarray(calibrated[0])
|
|
9791
|
+
if row.shape[0] != num_classes:
|
|
9792
|
+
aligned = np.zeros(num_classes)
|
|
9793
|
+
aligned[:min(row.shape[0], num_classes)] = row[:num_classes]
|
|
9794
|
+
row = aligned
|
|
9795
|
+
batch_probs[idx] = row
|
|
9796
|
+
|
|
9639
9797
|
else:
|
|
9640
|
-
#
|
|
9641
|
-
probs_i
|
|
9642
|
-
|
|
9643
|
-
|
|
9644
|
-
probs_i
|
|
9798
|
+
# models agree — boost confidence on agreed class
|
|
9799
|
+
probs_i = fresh_probs[i].copy()
|
|
9800
|
+
probs_i[trans_cls] = min(probs_i[trans_cls] * 1.2, 0.95)
|
|
9801
|
+
row_sum = probs_i.sum()
|
|
9802
|
+
probs_i /= row_sum if row_sum > 1e-8 else 1.0
|
|
9645
9803
|
batch_probs[idx] = probs_i
|
|
9646
|
-
|
|
9647
|
-
#
|
|
9648
|
-
if
|
|
9804
|
+
|
|
9805
|
+
# instance-level save counter, not local idx_total
|
|
9806
|
+
if getattr(self, '_prob_save_count', 0) < 2:
|
|
9649
9807
|
self.modular_probability_saving(
|
|
9650
|
-
fresh_input_ids[i:i+1],
|
|
9651
|
-
fresh_X[i:i+1],
|
|
9808
|
+
fresh_input_ids[i:i+1],
|
|
9809
|
+
fresh_X[i:i+1],
|
|
9652
9810
|
np.array([batch_probs[idx]])
|
|
9653
9811
|
)
|
|
9654
|
-
|
|
9655
|
-
return np.array(batch_probs)
|
|
9812
|
+
self._prob_save_count = getattr(self, '_prob_save_count', 0) + 1
|
|
9656
9813
|
|
|
9814
|
+
else:
|
|
9815
|
+
raise Warning('[!] Data is None before batching!')
|
|
9816
|
+
|
|
9817
|
+
# safe final assembly with consistent shape
|
|
9818
|
+
valid_probs = []
|
|
9819
|
+
for i, p in enumerate(batch_probs):
|
|
9820
|
+
if p is None:
|
|
9821
|
+
valid_probs.append(zero_row.copy())
|
|
9822
|
+
elif isinstance(p, list):
|
|
9823
|
+
arr = np.array(p, dtype=np.float64)
|
|
9824
|
+
if arr.shape[0] != num_classes:
|
|
9825
|
+
aligned = np.zeros(num_classes)
|
|
9826
|
+
aligned[:min(arr.shape[0], num_classes)] = arr[:num_classes]
|
|
9827
|
+
arr = aligned
|
|
9828
|
+
valid_probs.append(arr)
|
|
9829
|
+
elif isinstance(p, np.ndarray):
|
|
9830
|
+
if p.shape[0] != num_classes:
|
|
9831
|
+
aligned = np.zeros(num_classes)
|
|
9832
|
+
aligned[:min(p.shape[0], num_classes)] = p[:num_classes]
|
|
9833
|
+
p = aligned
|
|
9834
|
+
valid_probs.append(p)
|
|
9835
|
+
else:
|
|
9836
|
+
if output_memory is not None:
|
|
9837
|
+
print('[=] Unexpected Sample type in batch probability, Using previous memory to fill gaps in Samples Ambiguity')
|
|
9838
|
+
valid_probs.append(output_memory)
|
|
9839
|
+
else:
|
|
9840
|
+
print(f'[⚠️] Unexpected Sample type in batch_probs[{i}]: {type(p)} — using zeros to fill in valid probability')
|
|
9841
|
+
valid_probs.append(zero_row.copy())
|
|
9842
|
+
|
|
9843
|
+
try:
|
|
9844
|
+
return np.stack(valid_probs)
|
|
9845
|
+
except ValueError as e:
|
|
9846
|
+
print(f'[⚠️] Stack failed: {e} — shapes: {[p.shape for p in valid_probs]}')
|
|
9847
|
+
return valid_probs
|
|
9657
9848
|
|
|
9658
9849
|
|
|
9659
9850
|
def hybrid_prediction(self, rules, input_ids, dataset):
|
|
@@ -10370,6 +10561,23 @@ class IntegratedPipeline:
|
|
|
10370
10561
|
|
|
10371
10562
|
return X, Y
|
|
10372
10563
|
|
|
10564
|
+
def _sync_vocab_to_embedding(self):
|
|
10565
|
+
"""Extend token_embedding rows if vocab has grown since model creation.
|
|
10566
|
+
Preserves all existing trained weights — only appends new rows.
|
|
10567
|
+
"""
|
|
10568
|
+
if self.model2 is None:
|
|
10569
|
+
return
|
|
10570
|
+
current_emb_size = self.model2.token_embedding.shape[0]
|
|
10571
|
+
current_vocab_size = len(self.vocab)
|
|
10572
|
+
if current_vocab_size > current_emb_size:
|
|
10573
|
+
d_model = self.model2.d_model
|
|
10574
|
+
n_new = current_vocab_size - current_emb_size
|
|
10575
|
+
new_rows = np.random.randn(n_new, d_model) * 0.02
|
|
10576
|
+
self.model2.token_embedding = np.vstack([
|
|
10577
|
+
self.model2.token_embedding, new_rows
|
|
10578
|
+
])
|
|
10579
|
+
print(f'[=] token embedding grown: {current_emb_size} → {current_vocab_size}')
|
|
10580
|
+
|
|
10373
10581
|
|
|
10374
10582
|
def _extract_cache_samples_for_lstm(self, target_count):
|
|
10375
10583
|
"""
|
|
@@ -10531,25 +10739,34 @@ class IntegratedPipeline:
|
|
|
10531
10739
|
|
|
10532
10740
|
def sequence_encoding(self, datasets, max_len=32):
|
|
10533
10741
|
input_sequences = []
|
|
10534
|
-
for item in datasets:
|
|
10535
|
-
if not self.model2:
|
|
10536
|
-
intents = [d[1] for d in datasets]
|
|
10537
|
-
intent_to_id = {intent:i for i, intent in enumerate(sorted(set(intents)))}
|
|
10538
|
-
num_classes = self._get_num_classes()
|
|
10539
|
-
self.model2 = Transformer(
|
|
10540
|
-
vocab_size=len(self.vocab),
|
|
10541
|
-
d_model=32,
|
|
10542
|
-
n_heads=4,
|
|
10543
|
-
num_classes=num_classes
|
|
10544
|
-
)
|
|
10545
10742
|
|
|
10546
|
-
|
|
10547
|
-
|
|
10548
|
-
|
|
10549
|
-
|
|
10550
|
-
|
|
10551
|
-
|
|
10552
|
-
|
|
10743
|
+
if datasets:
|
|
10744
|
+
for item in datasets:
|
|
10745
|
+
if not self.model2:
|
|
10746
|
+
intents = [d[1] for d in datasets]
|
|
10747
|
+
intent_to_id = {intent:i for i, intent in enumerate(sorted(set(intents)))}
|
|
10748
|
+
num_classes = self._get_num_classes()
|
|
10749
|
+
self.model2 = Transformer(
|
|
10750
|
+
vocab_size=len(self.vocab),
|
|
10751
|
+
d_model=32,
|
|
10752
|
+
n_heads=4,
|
|
10753
|
+
num_classes=num_classes
|
|
10754
|
+
)
|
|
10755
|
+
|
|
10756
|
+
self._sync_vocab_to_embedding()
|
|
10757
|
+
text = item[0] if isinstance(item, tuple) else item
|
|
10758
|
+
|
|
10759
|
+
token_ids = self.encode(text, self.vocab, max_len=max_len)
|
|
10760
|
+
token_embs = self.model2.token_embedding[token_ids] # (max_len, d_model)
|
|
10761
|
+
pos_embs = self.model2.pos_embedding[:max_len] # (max_len, d_model)
|
|
10762
|
+
|
|
10763
|
+
sequence_input = token_embs + pos_embs
|
|
10764
|
+
input_sequences.append(sequence_input)
|
|
10765
|
+
return np.stack(input_sequences) # shape: (batch, max_len, d_model)
|
|
10766
|
+
else:
|
|
10767
|
+
raise Warning('[!] Dataset is None! make sure you provide a dataset or create it Automatically!')
|
|
10768
|
+
|
|
10769
|
+
return None
|
|
10553
10770
|
|
|
10554
10771
|
def transformer_pooled_features(self, sequence_inputs):
|
|
10555
10772
|
# mean/max/std pooling over sequence dimension
|