AbstractIntegratedModule 0.4.0__tar.gz → 0.4.2__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.4.2/AbstractIntegratedModule.cp313-win_amd64.pyd +0 -0
- {abstractintegratedmodule-0.4.0 → abstractintegratedmodule-0.4.2}/AbstractIntegratedModule.cpython-310-aarch64-linux-gnu.so +0 -0
- {abstractintegratedmodule-0.4.0 → abstractintegratedmodule-0.4.2}/AbstractIntegratedModule.cpython-312-x86_64-linux-gnu.so +0 -0
- {abstractintegratedmodule-0.4.0 → abstractintegratedmodule-0.4.2/AbstractIntegratedModule.egg-info}/PKG-INFO +4 -4
- {abstractintegratedmodule-0.4.0 → abstractintegratedmodule-0.4.2}/AbstractIntegratedModule.py +381 -227
- {abstractintegratedmodule-0.4.0/AbstractIntegratedModule.egg-info → abstractintegratedmodule-0.4.2}/PKG-INFO +4 -4
- {abstractintegratedmodule-0.4.0 → abstractintegratedmodule-0.4.2}/README.md +3 -3
- {abstractintegratedmodule-0.4.0 → abstractintegratedmodule-0.4.2}/setup.py +1 -1
- abstractintegratedmodule-0.4.0/AbstractIntegratedModule.cp313-win_amd64.pyd +0 -0
- {abstractintegratedmodule-0.4.0 → abstractintegratedmodule-0.4.2}/AbstractIntegratedModule.egg-info/SOURCES.txt +0 -0
- {abstractintegratedmodule-0.4.0 → abstractintegratedmodule-0.4.2}/AbstractIntegratedModule.egg-info/dependency_links.txt +0 -0
- {abstractintegratedmodule-0.4.0 → abstractintegratedmodule-0.4.2}/AbstractIntegratedModule.egg-info/requires.txt +0 -0
- {abstractintegratedmodule-0.4.0 → abstractintegratedmodule-0.4.2}/AbstractIntegratedModule.egg-info/top_level.txt +0 -0
- {abstractintegratedmodule-0.4.0 → abstractintegratedmodule-0.4.2}/MANIFEST.in +0 -0
- {abstractintegratedmodule-0.4.0 → abstractintegratedmodule-0.4.2}/pyproject.toml +0 -0
- {abstractintegratedmodule-0.4.0 → abstractintegratedmodule-0.4.2}/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.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: Framework for Advanced Integrated Non-LLM AI Module library - Backend Framework for Non-LLM AI Agent Framework
|
|
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: Beta, 0.4.
|
|
45
|
+
- Development Stage: Beta, 0.4.2.
|
|
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.
|
|
@@ -62,11 +62,11 @@ https://github.com/Micro-Novelty/IntegratedPipeline-Specialized-Non-LLM-AI-Agent
|
|
|
62
62
|
- Proven Works on ARM64 Environment, Training and Prediction works efficient on Docker ARM64 environment with QEMU, good parallelizing behavior is guaranteed.
|
|
63
63
|
- P2P Works efficiently in ARM64 Docker + QEMU, No conflicting socket and all prediction works efficiently.
|
|
64
64
|
- AWE setup Proven Efficient on Hard-uncontrolled dataset such as Activity Recognition from the given Database.
|
|
65
|
-
|
|
65
|
+
- LSTM is Optimized efficiently for scarce data with AWE method.
|
|
66
66
|
-----
|
|
67
67
|
|
|
68
68
|
<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" />
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
|
|
71
71
|
## [+] MANN Intro
|
|
72
72
|
[=] Memory augmented Neural network (MANN) is a neural network architecture coupled with an external, dynamic memory module, allowing it to store, retrieve, and update information similarly to a computer's RAM. Unlike traditional networks that store knowledge only in weight parameters, MANNs excel at fast learning, long-term dependency handling, and episodic recall, In IntegratedPipeline, Its memory is stored in a custom database inside your local machine, then later used for memory retrieval, transfered to the AI Dictionary where it can finnaly recall its memory when input condition matched with memory.
|
{abstractintegratedmodule-0.4.0 → abstractintegratedmodule-0.4.2}/AbstractIntegratedModule.py
RENAMED
|
@@ -1442,96 +1442,101 @@ class LSTMCell:
|
|
|
1442
1442
|
def _g(self, v): return v[2*self.hidden_size:3*self.hidden_size]
|
|
1443
1443
|
def _o(self, v): return v[3*self.hidden_size:]
|
|
1444
1444
|
|
|
1445
|
-
|
|
1446
|
-
# ── forward ──────────────────────────────
|
|
1445
|
+
# _________ forward method for cell class _____________
|
|
1447
1446
|
def forward(self, x_seq: np.ndarray, h0=None, c0=None):
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
returns: hs (T, hidden), cs (T, hidden), cache (for BPTT)
|
|
1451
|
-
"""
|
|
1452
|
-
T = x_seq.shape[0]
|
|
1453
|
-
H = self.hidden_size
|
|
1447
|
+
T = x_seq.shape[0]
|
|
1448
|
+
H = self.hidden_size
|
|
1454
1449
|
expected_input = self.input_size
|
|
1455
1450
|
|
|
1456
1451
|
h = np.zeros(H) if h0 is None else h0.copy()
|
|
1457
1452
|
c = np.zeros(H) if c0 is None else c0.copy()
|
|
1458
1453
|
|
|
1459
|
-
hs
|
|
1460
|
-
|
|
1454
|
+
hs = np.zeros((T, H))
|
|
1455
|
+
cs = np.zeros((T, H))
|
|
1456
|
+
cache = []
|
|
1457
|
+
|
|
1458
|
+
# preallocate xh buffer once
|
|
1459
|
+
xh = np.empty(expected_input + H)
|
|
1461
1460
|
|
|
1462
1461
|
for t in range(T):
|
|
1463
|
-
x
|
|
1462
|
+
x = x_seq[t]
|
|
1464
1463
|
if x.ndim == 0:
|
|
1465
|
-
x = x.reshape(1)
|
|
1464
|
+
x = x.reshape(1)
|
|
1466
1465
|
if x.shape[0] < expected_input:
|
|
1467
|
-
x = np.pad(x, (0, expected_input - x.shape[0]))
|
|
1466
|
+
x = np.pad(x, (0, expected_input - x.shape[0]))
|
|
1468
1467
|
elif x.shape[0] > expected_input:
|
|
1469
|
-
x = x[:expected_input]
|
|
1470
|
-
|
|
1471
|
-
|
|
1468
|
+
x = x[:expected_input]
|
|
1469
|
+
|
|
1470
|
+
# write into buffer, no allocation
|
|
1471
|
+
xh[:expected_input] = x
|
|
1472
|
+
xh[expected_input:] = h
|
|
1473
|
+
|
|
1474
|
+
z = self.W @ xh + self.b
|
|
1475
|
+
H1, H2, H3 = H, H * 2, H * 3
|
|
1472
1476
|
|
|
1473
|
-
|
|
1474
|
-
f
|
|
1475
|
-
i
|
|
1476
|
-
g
|
|
1477
|
-
o
|
|
1477
|
+
# direct slices, no method calls
|
|
1478
|
+
f = sigmoid(z[:H1])
|
|
1479
|
+
i = sigmoid(z[H1:H2])
|
|
1480
|
+
g = np.tanh(z[H2:H3])
|
|
1481
|
+
o = sigmoid(z[H3:])
|
|
1478
1482
|
|
|
1479
|
-
c_new
|
|
1483
|
+
c_new = f * c + i * g
|
|
1480
1484
|
tanh_c = np.tanh(c_new)
|
|
1481
|
-
h_new
|
|
1485
|
+
h_new = o * tanh_c
|
|
1482
1486
|
|
|
1483
|
-
|
|
1487
|
+
# store copies — h/c will be overwritten next iteration
|
|
1488
|
+
cache.append((x.copy(), h.copy(), c.copy(),
|
|
1489
|
+
f, i, g, o, c_new, tanh_c, xh.copy()))
|
|
1484
1490
|
h, c = h_new, c_new
|
|
1485
|
-
hs[t]
|
|
1491
|
+
hs[t] = h
|
|
1492
|
+
cs[t] = c
|
|
1486
1493
|
|
|
1487
1494
|
return hs, cs, cache
|
|
1488
1495
|
|
|
1489
|
-
#
|
|
1490
|
-
def backward(self, dhs: np.ndarray, cache,
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
"""
|
|
1495
|
-
T = len(cache)
|
|
1496
|
+
# ________ backward method for Cell class __________
|
|
1497
|
+
def backward(self, dhs: np.ndarray, cache,
|
|
1498
|
+
dh_next=None, dc_next=None, T_limit=None):
|
|
1499
|
+
# T_limit avoids slicing cache list externally
|
|
1500
|
+
T = T_limit if T_limit is not None else len(cache)
|
|
1496
1501
|
H = self.hidden_size
|
|
1497
1502
|
|
|
1498
|
-
dW
|
|
1499
|
-
db
|
|
1500
|
-
dh
|
|
1501
|
-
dc
|
|
1503
|
+
dW = np.zeros_like(self.W)
|
|
1504
|
+
db = np.zeros_like(self.b)
|
|
1505
|
+
dh = np.zeros(H) if dh_next is None else dh_next.copy()
|
|
1506
|
+
dc = np.zeros(H) if dc_next is None else dc_next.copy()
|
|
1502
1507
|
dx_seq = np.zeros((T, self.input_size))
|
|
1503
1508
|
|
|
1509
|
+
# preallocate dz buffer once
|
|
1510
|
+
dz = np.empty(4 * H)
|
|
1511
|
+
H1, H2, H3 = H, H * 2, H * 3
|
|
1512
|
+
|
|
1504
1513
|
for t in reversed(range(T)):
|
|
1505
1514
|
x, h_prev, c_prev, f, i, g, o, c_new, tanh_c, xh = cache[t]
|
|
1506
1515
|
|
|
1507
|
-
dh_total = dhs[t] + dh
|
|
1516
|
+
dh_total = dhs[t] + dh
|
|
1508
1517
|
|
|
1509
|
-
# output gate
|
|
1510
1518
|
do = dh_total * tanh_c
|
|
1511
1519
|
dtanhc = dh_total * o
|
|
1512
|
-
|
|
1513
|
-
# cell state
|
|
1514
1520
|
dc_new = dtanhc * tanh_deriv(tanh_c) + dc
|
|
1515
1521
|
|
|
1516
|
-
# gates
|
|
1517
1522
|
df = dc_new * c_prev
|
|
1518
1523
|
di = dc_new * g
|
|
1519
1524
|
dg = dc_new * i
|
|
1520
|
-
dc = dc_new * f
|
|
1521
|
-
|
|
1522
|
-
# pre-activation gradients
|
|
1523
|
-
df_pre = df * sigmoid_deriv(f)
|
|
1524
|
-
di_pre = di * sigmoid_deriv(i)
|
|
1525
|
-
dg_pre = dg * tanh_deriv(g)
|
|
1526
|
-
do_pre = do * sigmoid_deriv(o)
|
|
1525
|
+
dc = dc_new * f
|
|
1527
1526
|
|
|
1528
|
-
|
|
1527
|
+
# write into preallocated dz buffer
|
|
1528
|
+
dz[:H1] = df * sigmoid_deriv(f)
|
|
1529
|
+
dz[H1:H2] = di * sigmoid_deriv(i)
|
|
1530
|
+
dz[H2:H3] = dg * tanh_deriv(g)
|
|
1531
|
+
dz[H3:] = do * sigmoid_deriv(o)
|
|
1529
1532
|
|
|
1530
|
-
|
|
1533
|
+
# nplace accumulation, no intermediate allocation
|
|
1534
|
+
dW += np.outer(dz, xh) # unavoidable alloc but outer is C-level
|
|
1531
1535
|
db += dz
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1536
|
+
|
|
1537
|
+
dxh = self.W.T @ dz
|
|
1538
|
+
dx_seq[t] = dxh[:self.input_size]
|
|
1539
|
+
dh = dxh[self.input_size:]
|
|
1535
1540
|
|
|
1536
1541
|
return {"dW": dW, "db": db}, dx_seq, dh, dc
|
|
1537
1542
|
|
|
@@ -1539,83 +1544,77 @@ class LSTMCell:
|
|
|
1539
1544
|
# ─────────────────────────────────────────────
|
|
1540
1545
|
# LSTM Network (cell + linear output head)
|
|
1541
1546
|
# ─────────────────────────────────────────────
|
|
1542
|
-
|
|
1543
1547
|
class LSTMNetwork:
|
|
1544
|
-
"""
|
|
1545
|
-
One LSTM layer + a linear output projection.
|
|
1546
|
-
input_size → hidden_size → output_size
|
|
1547
|
-
"""
|
|
1548
|
-
|
|
1549
1548
|
def __init__(self, pipeline, input_size, hidden_size, output_size, seed=0):
|
|
1550
|
-
self.cell
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
self.
|
|
1554
|
-
self.
|
|
1555
|
-
self.
|
|
1556
|
-
|
|
1557
|
-
|
|
1549
|
+
self.cell = LSTMCell(input_size, hidden_size, seed)
|
|
1550
|
+
self.weight_shaper = GeometricWeightShaping(output_size, hidden_size)
|
|
1551
|
+
self.Wy = None
|
|
1552
|
+
self.by = np.zeros(output_size)
|
|
1553
|
+
self.pipeline = pipeline
|
|
1554
|
+
self._trained = False
|
|
1555
|
+
|
|
1556
|
+
# forward method to calculate proper weight for prediction and training.
|
|
1558
1557
|
def forward(self, x_seq):
|
|
1558
|
+
# also Wy init only here, removed from train_step
|
|
1559
1559
|
if self.Wy is None:
|
|
1560
1560
|
self.Wy = self.weight_shaper.weight_shaping(x_seq)
|
|
1561
|
-
|
|
1562
1561
|
hs, cs, cache = self.cell.forward(x_seq)
|
|
1563
|
-
|
|
1564
|
-
preds = hs @ self.Wy.T + self.by # (T, output_size)
|
|
1562
|
+
preds = hs @ self.Wy.T + self.by # (T, output_size)
|
|
1565
1563
|
return preds, hs, cs, cache
|
|
1566
1564
|
|
|
1565
|
+
# calculate loss of MSE (Mean squared error.)
|
|
1567
1566
|
def loss_mse(self, preds, targets, AMR):
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
preds = preds[:targets.shape[0], :targets.shape[1]]
|
|
1575
|
-
|
|
1567
|
+
# proper and correct shape alignment
|
|
1568
|
+
min_T = min(preds.shape[0], targets.shape[0])
|
|
1569
|
+
min_F = min(preds.shape[1], targets.shape[1])
|
|
1570
|
+
preds = preds[:min_T, :min_F]
|
|
1571
|
+
targets = targets[:min_T, :min_F]
|
|
1572
|
+
|
|
1576
1573
|
diff = preds - targets
|
|
1577
|
-
|
|
1574
|
+
loss = (1.0 - AMR) * np.mean(diff ** 2)
|
|
1575
|
+
dloss = diff / (min_T * min_F) # FIX 3 — normalize by full element count
|
|
1576
|
+
return loss, dloss
|
|
1578
1577
|
|
|
1578
|
+
# backward method for the network to calculate proper weights with cell backward
|
|
1579
1579
|
def backward(self, dpreds, hs, cache):
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
hs = hs[:min_T, :] # (min_T, hidden)
|
|
1580
|
+
min_T = min(dpreds.shape[0], hs.shape[0])
|
|
1581
|
+
dpreds = dpreds[:min_T]
|
|
1582
|
+
hs = hs[:min_T]
|
|
1584
1583
|
|
|
1585
|
-
dWy = dpreds.T @ hs
|
|
1584
|
+
dWy = dpreds.T @ hs
|
|
1586
1585
|
dby = dpreds.sum(axis=0)
|
|
1587
|
-
dhs = dpreds @ self.Wy
|
|
1586
|
+
dhs = dpreds @ self.Wy
|
|
1588
1587
|
|
|
1589
|
-
|
|
1588
|
+
# pass min_T directly, need to avoid creating a sliced list
|
|
1589
|
+
cell_grads, dx, _, _ = self.cell.backward(dhs, cache, T_limit=min_T)
|
|
1590
1590
|
return cell_grads, {"dWy": dWy, "dby": dby}, dx
|
|
1591
1591
|
|
|
1592
|
+
# update ensured proper gradient clipping
|
|
1592
1593
|
def update(self, cell_grads, out_grads, lr=1e-3, clip=5.0):
|
|
1593
|
-
"""SGD with gradient clipping."""
|
|
1594
1594
|
def clip_and_step(param, grad):
|
|
1595
|
-
|
|
1595
|
+
np.clip(grad, -clip, clip, out=grad)
|
|
1596
1596
|
param -= lr * grad
|
|
1597
1597
|
|
|
1598
|
-
clip_and_step(self.cell.W,
|
|
1599
|
-
clip_and_step(self.cell.b,
|
|
1600
|
-
clip_and_step(self.Wy,
|
|
1601
|
-
clip_and_step(self.by,
|
|
1598
|
+
clip_and_step(self.cell.W, cell_grads["dW"])
|
|
1599
|
+
clip_and_step(self.cell.b, cell_grads["db"])
|
|
1600
|
+
clip_and_step(self.Wy, out_grads["dWy"])
|
|
1601
|
+
clip_and_step(self.by, out_grads["dby"])
|
|
1602
1602
|
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1603
|
+
# train step for each LSTM fitting method
|
|
1604
|
+
def train_step(self, x_seq, targets, lr=1e-3, AMR=None):
|
|
1605
|
+
# accept precomputed AMR
|
|
1606
|
+
if AMR is None:
|
|
1607
|
+
AME = self.pipeline.AME_Encoder(x_seq)
|
|
1608
|
+
AMR = 1.0 / (1.0 + np.exp(-AME))
|
|
1606
1609
|
|
|
1607
1610
|
preds, hs, cs, cache = self.forward(x_seq)
|
|
1608
|
-
|
|
1609
|
-
AME = self.pipeline.AME_Encoder(x_seq)
|
|
1610
|
-
AMR = 1.0 / (1.0 + np.exp(-AME))
|
|
1611
|
-
|
|
1612
|
-
loss, dloss = self.loss_mse(preds, targets, AMR)
|
|
1611
|
+
loss, dloss = self.loss_mse(preds, targets, AMR)
|
|
1613
1612
|
cell_grads, out_grads, _ = self.backward(dloss, hs, cache)
|
|
1614
1613
|
self.update(cell_grads, out_grads, lr)
|
|
1615
|
-
|
|
1616
1614
|
return loss, preds
|
|
1617
1615
|
|
|
1618
1616
|
|
|
1617
|
+
|
|
1619
1618
|
# ─────────────────────────────────────────────
|
|
1620
1619
|
# LSTM Engine
|
|
1621
1620
|
# ─────────────────────────────────────────────
|
|
@@ -1654,147 +1653,265 @@ class LSTMEngine:
|
|
|
1654
1653
|
self.residual_mean = None
|
|
1655
1654
|
|
|
1656
1655
|
# ── calibrate on validation set ──────────
|
|
1657
|
-
def
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1656
|
+
def calibrate_residual(self, X_val, Y_val):
|
|
1657
|
+
if len(X_val) == 0:
|
|
1658
|
+
self.residual_mean = 0.0
|
|
1659
|
+
self.residual_std = 1.0
|
|
1660
|
+
return
|
|
1661
|
+
|
|
1662
|
+
confidence_errors = []
|
|
1663
|
+
|
|
1663
1664
|
for j in range(len(X_val)):
|
|
1664
1665
|
preds, _, _, _ = self.model.forward(X_val[j])
|
|
1665
|
-
err = (preds[:, 0] - Y_val[j, :, 0])
|
|
1666
|
-
residuals.extend(err.tolist())
|
|
1667
1666
|
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1667
|
+
pred_vals = preds[:, 0] if preds.ndim > 1 else preds
|
|
1668
|
+
|
|
1669
|
+
# get true class
|
|
1670
|
+
true_vals = Y_val[j, :, 0] if Y_val[j].ndim > 1 else Y_val[j]
|
|
1671
|
+
min_len = min(len(pred_vals), len(true_vals))
|
|
1672
|
+
|
|
1673
|
+
# sigmoid to get predicted probability
|
|
1674
|
+
pred_prob = 1.0 / (1.0 + np.exp(-pred_vals[:min_len]))
|
|
1675
|
+
true_bin = true_vals[:min_len].astype(float)
|
|
1676
|
+
|
|
1677
|
+
# confidence error — how wrong was the predicted probability
|
|
1678
|
+
# correct prediction → low error
|
|
1679
|
+
# wrong prediction → high error
|
|
1680
|
+
err = np.abs(pred_prob - true_bin)
|
|
1681
|
+
confidence_errors.extend(err.tolist())
|
|
1682
|
+
|
|
1683
|
+
errors = np.array(confidence_errors)
|
|
1684
|
+
|
|
1685
|
+
# IQR outlier removal
|
|
1686
|
+
q25, q75 = np.percentile(errors, [25, 75])
|
|
1687
|
+
iqr = q75 - q25
|
|
1688
|
+
mask = (errors >= q25 - 1.5 * iqr) & (errors <= q75 + 1.5 * iqr)
|
|
1689
|
+
clean = errors[mask] if mask.sum() > 0 else errors
|
|
1690
|
+
|
|
1691
|
+
self.residual_mean = float(clean.mean())
|
|
1692
|
+
self.residual_std = float(max(clean.std(), 1e-6))
|
|
1693
|
+
|
|
1694
|
+
# floor std on small n — can't trust calibration with few samples
|
|
1695
|
+
if len(X_val) < 20:
|
|
1696
|
+
self.residual_std = max(self.residual_std, 0.1)
|
|
1697
|
+
print(f'[!] Small val set ({len(X_val)} samples) — flooring σ to 0.1')
|
|
1698
|
+
|
|
1699
|
+
self.calibration_coverage = float(mask.mean())
|
|
1700
|
+
self.n_calibration_samples = len(X_val)
|
|
1701
|
+
|
|
1671
1702
|
print(f"[=] Calibrated: residual μ={self.residual_mean:.4f} "
|
|
1672
|
-
|
|
1703
|
+
f"σ={self.residual_std:.4f} "
|
|
1704
|
+
f"coverage={self.calibration_coverage:.1%} "
|
|
1705
|
+
f"n={self.n_calibration_samples}")
|
|
1706
|
+
|
|
1673
1707
|
|
|
1674
1708
|
# ── MC dropout forward ────────────────────
|
|
1675
|
-
def _mc_forward(self, x_seq: np.ndarray) ->
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
between every timestep, scaled to preserve expected value.
|
|
1679
|
-
"""
|
|
1680
|
-
T = x_seq.shape[0]
|
|
1681
|
-
H = self.model.cell.hidden_size
|
|
1709
|
+
def _mc_forward(self, x_seq: np.ndarray) -> np.ndarray:
|
|
1710
|
+
T = x_seq.shape[0]
|
|
1711
|
+
H = self.model.cell.hidden_size
|
|
1682
1712
|
expected_input = self.model.cell.input_size
|
|
1683
|
-
p
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1713
|
+
p = self.dropout
|
|
1714
|
+
cell = self.model.cell
|
|
1715
|
+
W = cell.W
|
|
1716
|
+
b = cell.b
|
|
1717
|
+
H1, H2, H3 = H, H * 2, H * 3 # slice boundaries precomputed
|
|
1718
|
+
|
|
1719
|
+
# FIX 1 — Wy check once before loop, not T times inside
|
|
1720
|
+
if self.model.Wy is None:
|
|
1721
|
+
self.model.Wy = self.model.weight_shaper.weight_shaping(x_seq)
|
|
1722
|
+
Wy = self.model.Wy
|
|
1723
|
+
by = self.model.by
|
|
1724
|
+
|
|
1725
|
+
h = np.zeros(H)
|
|
1726
|
+
c = np.zeros(H)
|
|
1727
|
+
xh = np.empty(expected_input + H) # preallocate concat buffer
|
|
1728
|
+
preds = np.empty(T) # preallocate output
|
|
1729
|
+
|
|
1730
|
+
# precompute dropout scale factor
|
|
1731
|
+
inv_keep = 1.0 / (1.0 - p)
|
|
1690
1732
|
|
|
1691
1733
|
for t in range(T):
|
|
1692
|
-
x
|
|
1734
|
+
x = x_seq[t]
|
|
1735
|
+
|
|
1736
|
+
# shape alignment
|
|
1693
1737
|
if x.ndim == 0:
|
|
1694
|
-
x = x.reshape(1)
|
|
1738
|
+
x = x.reshape(1)
|
|
1695
1739
|
if x.shape[0] < expected_input:
|
|
1696
|
-
x = np.pad(x, (0, expected_input - x.shape[0]))
|
|
1740
|
+
x = np.pad(x, (0, expected_input - x.shape[0]))
|
|
1697
1741
|
elif x.shape[0] > expected_input:
|
|
1698
|
-
x = x[:expected_input]
|
|
1699
|
-
|
|
1700
|
-
|
|
1742
|
+
x = x[:expected_input]
|
|
1743
|
+
|
|
1744
|
+
# need to write into preallocated buffer instead of np.concatenate
|
|
1745
|
+
xh[:expected_input] = x
|
|
1746
|
+
xh[expected_input:] = h
|
|
1747
|
+
|
|
1748
|
+
z = W @ xh + b # (4H,)
|
|
1701
1749
|
|
|
1702
|
-
#
|
|
1703
|
-
f
|
|
1704
|
-
i
|
|
1705
|
-
g
|
|
1706
|
-
o
|
|
1750
|
+
# direct slices instead of method calls
|
|
1751
|
+
f = sigmoid(z[:H1])
|
|
1752
|
+
i = sigmoid(z[H1:H2])
|
|
1753
|
+
g = np.tanh(z[H2:H3])
|
|
1754
|
+
o = sigmoid(z[H3:])
|
|
1707
1755
|
|
|
1708
1756
|
c = f * c + i * g
|
|
1709
1757
|
tanh_c = np.tanh(c)
|
|
1710
1758
|
h = o * tanh_c
|
|
1711
1759
|
|
|
1712
|
-
#
|
|
1713
|
-
mask = (np.random.rand(H) > p)
|
|
1714
|
-
h
|
|
1715
|
-
# cell state c is untouched —
|
|
1716
|
-
# preserves long-term memory
|
|
1717
|
-
if self.model.Wy is None:
|
|
1718
|
-
self.model.Wy = self.model.weight_shaper.weight_shaping(x_seq)
|
|
1760
|
+
# FIX 4 — precomputed inv_keep, inplace mask application
|
|
1761
|
+
mask = (np.random.rand(H) > p) * inv_keep
|
|
1762
|
+
h *= mask
|
|
1719
1763
|
|
|
1720
|
-
|
|
1721
|
-
preds.append(pred[0])
|
|
1764
|
+
preds[t] = (h @ Wy.T + by)[0]
|
|
1722
1765
|
|
|
1723
|
-
return
|
|
1766
|
+
return preds # (T,)
|
|
1724
1767
|
|
|
1725
|
-
# ── gate uncertainty ──────────────────────
|
|
1726
|
-
def _gate_uncertainty(self, x_seq: np.ndarray, AMR: float) ->
|
|
1768
|
+
# ── gate uncertainty for LSTM prediction──────────────────────
|
|
1769
|
+
def _gate_uncertainty(self, x_seq: np.ndarray, AMR: float) -> np.ndarray:
|
|
1727
1770
|
"""
|
|
1728
1771
|
Structural uncertainty from gate activations.
|
|
1729
|
-
|
|
1730
|
-
High uncertainty when:
|
|
1731
|
-
forget gate (f) is LOW → model is erasing memory
|
|
1732
|
-
input gate (i) is HIGH → model is overwriting with new info
|
|
1733
|
-
→ transition moment, inherently harder to predict
|
|
1734
|
-
|
|
1735
|
-
Returns per-timestep uncertainty in [0, 1].
|
|
1772
|
+
Vectorized — no Python loop over timesteps.
|
|
1736
1773
|
"""
|
|
1737
1774
|
_, _, cache = self.model.cell.forward(x_seq)
|
|
1738
|
-
gate_uncertainty = []
|
|
1739
|
-
for entry in cache:
|
|
1740
|
-
_, _, _, f, i, g, o, _, _, _ = entry
|
|
1741
|
-
# mean forget across hidden dims — low = erasing
|
|
1742
|
-
forget_instability = 1.0 - f.mean()
|
|
1743
|
-
# mean input — high = overwriting
|
|
1744
|
-
input_activity = i.mean()
|
|
1745
|
-
# combined: both high = maximally uncertain transition
|
|
1746
|
-
u = AMR * forget_instability + AMR * input_activity
|
|
1747
|
-
gate_uncertainty.append(u)
|
|
1748
1775
|
|
|
1749
|
-
|
|
1776
|
+
# cache[t] = (x, h_prev, c_prev, f, i, g, o, c_new, tanh_c, xh)
|
|
1777
|
+
# need to extract f and i directly as stacked arrays — shape (T, H)
|
|
1778
|
+
T = len(cache)
|
|
1779
|
+
|
|
1780
|
+
if T == 0:
|
|
1781
|
+
return np.array([0.0])
|
|
1782
|
+
|
|
1783
|
+
# vectorized extraction — one pass instead of T unpacks
|
|
1784
|
+
f_all = np.empty((T, cache[0][3].shape[0])) # (T, H)
|
|
1785
|
+
i_all = np.empty((T, cache[0][4].shape[0])) # (T, H)
|
|
1786
|
+
|
|
1787
|
+
for t, entry in enumerate(cache):
|
|
1788
|
+
f_all[t] = entry[3] # forget gate
|
|
1789
|
+
i_all[t] = entry[4] # input gate
|
|
1790
|
+
|
|
1791
|
+
# vectorized computation — no per-timestep Python arithmetic
|
|
1792
|
+
forget_instability = 1.0 - f_all.mean(axis=1) # (T,)
|
|
1793
|
+
input_activity = i_all.mean(axis=1) # (T,)
|
|
1750
1794
|
|
|
1795
|
+
# precompute scalar factor once
|
|
1796
|
+
scale = 1.0 - AMR
|
|
1797
|
+
gate_uncertainty = scale * (forget_instability + input_activity)
|
|
1751
1798
|
|
|
1752
|
-
|
|
1799
|
+
return np.clip(gate_uncertainty, 0.0, 1.0)
|
|
1800
|
+
|
|
1801
|
+
# empirical quantiles from actual residuals
|
|
1753
1802
|
def calibrate(self, X_val, Y_val):
|
|
1754
|
-
|
|
1803
|
+
if len(X_val) == 0:
|
|
1804
|
+
self.residual_mean = 0.0
|
|
1805
|
+
self.residual_std = 1.0
|
|
1806
|
+
self.quantiles = {}
|
|
1807
|
+
return
|
|
1808
|
+
|
|
1809
|
+
all_errors = []
|
|
1755
1810
|
for j in range(len(X_val)):
|
|
1756
|
-
preds, _, _, _ = self.model.forward(X_val[j])
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
self.
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1811
|
+
preds, _, _, _ = self.model.forward(X_val[j])
|
|
1812
|
+
y = Y_val[j]
|
|
1813
|
+
pred_vals = preds[:, 0] if preds.ndim > 1 else preds
|
|
1814
|
+
true_vals = y[:, 0] if y.ndim > 1 else y
|
|
1815
|
+
min_T = min(len(pred_vals), len(true_vals))
|
|
1816
|
+
all_errors.append(pred_vals[:min_T] - true_vals[:min_T])
|
|
1817
|
+
|
|
1818
|
+
residuals = np.concatenate(all_errors)
|
|
1819
|
+
n = len(residuals)
|
|
1820
|
+
|
|
1821
|
+
self.residual_mean = float(residuals.mean())
|
|
1822
|
+
self.residual_std = float(max(residuals.std(), 1e-6))
|
|
1823
|
+
|
|
1824
|
+
# adapt confidence levels to sample size
|
|
1825
|
+
# small n → only compute what's statistically supportable
|
|
1826
|
+
if n < 20:
|
|
1827
|
+
# only 90% interval is reliable — use 10th/90th percentile
|
|
1828
|
+
# wide enough to be honest about uncertainty
|
|
1829
|
+
levels = [10.0, 90.0]
|
|
1830
|
+
p = np.percentile(residuals, levels)
|
|
1831
|
+
self.quantiles = {
|
|
1832
|
+
0.90: (float(p[0]), float(p[1])),
|
|
1833
|
+
0.95: (float(p[0]), float(p[1])), # same as 90% — honest, not fake precision
|
|
1834
|
+
0.99: (float(p[0]), float(p[1])),
|
|
1835
|
+
}
|
|
1836
|
+
print(f'[!] n={n} too small for tail quantiles — '
|
|
1837
|
+
f'using 10th/90th for all intervals')
|
|
1838
|
+
|
|
1839
|
+
elif n < 50:
|
|
1840
|
+
# 90% and 95% supportable, 99% not reliable
|
|
1841
|
+
levels = [2.5, 5.0, 95.0, 97.5]
|
|
1842
|
+
p = np.percentile(residuals, levels)
|
|
1843
|
+
self.quantiles = {
|
|
1844
|
+
0.90: (float(p[1]), float(p[2])),
|
|
1845
|
+
0.95: (float(p[0]), float(p[3])),
|
|
1846
|
+
0.99: (float(p[0]), float(p[3])), # same as 95% — honest
|
|
1847
|
+
}
|
|
1848
|
+
print(f'[!] n={n} insufficient for 99% interval — '
|
|
1849
|
+
f'using 95% as proxy')
|
|
1850
|
+
|
|
1851
|
+
else:
|
|
1852
|
+
# full precision justified
|
|
1853
|
+
levels = [0.5, 2.5, 5.0, 95.0, 97.5, 99.5]
|
|
1854
|
+
p = np.percentile(residuals, levels)
|
|
1855
|
+
self.quantiles = {
|
|
1856
|
+
0.90: (float(p[2]), float(p[3])),
|
|
1857
|
+
0.95: (float(p[1]), float(p[4])),
|
|
1858
|
+
0.99: (float(p[0]), float(p[5])),
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
# floor std on small n
|
|
1862
|
+
if n < 20:
|
|
1863
|
+
self.residual_std = max(self.residual_std, 0.1)
|
|
1864
|
+
|
|
1865
|
+
print(f"[=] Calibrated: μ={self.residual_mean:.4f} "
|
|
1866
|
+
f"σ={self.residual_std:.4f} "
|
|
1867
|
+
f"n={n} "
|
|
1868
|
+
f"90%=[{self.quantiles[0.90][0]:.4f}, {self.quantiles[0.90][1]:.4f}]")
|
|
1774
1869
|
|
|
1775
1870
|
# interval to calculate prediction interval from MC mean + empirical quantiles
|
|
1776
1871
|
def _interval(self, mc_mean, confidence_level):
|
|
1872
|
+
# flatten mc_mean safely — handles scalar, 0-d array, or 1-d array
|
|
1873
|
+
mc_scalar = float(np.asarray(mc_mean).flat[0])
|
|
1874
|
+
|
|
1875
|
+
if confidence_level not in self.quantiles:
|
|
1876
|
+
available = sorted(self.quantiles.keys())
|
|
1877
|
+
if not available:
|
|
1878
|
+
return mc_scalar - self.residual_std, mc_scalar + self.residual_std
|
|
1879
|
+
confidence_level = min(available, key=lambda k: abs(k - confidence_level))
|
|
1880
|
+
print(f'[!] Confidence level not found, using closest: {confidence_level}')
|
|
1881
|
+
|
|
1777
1882
|
lo_bias, hi_bias = self.quantiles[confidence_level]
|
|
1778
|
-
return mc_mean + lo_bias, mc_mean + hi_bias
|
|
1779
1883
|
|
|
1884
|
+
lo = mc_scalar + float(np.asarray(lo_bias).flat[0])
|
|
1885
|
+
hi = mc_scalar + float(np.asarray(hi_bias).flat[0])
|
|
1886
|
+
|
|
1887
|
+
if lo > hi:
|
|
1888
|
+
lo, hi = hi, lo
|
|
1889
|
+
|
|
1890
|
+
return lo, hi
|
|
1780
1891
|
|
|
1781
1892
|
# MC sample counting for label confidence (last timestep)
|
|
1782
1893
|
def _label_confidence_empirical(self, mc_samples_last, label_bins):
|
|
1783
1894
|
"""
|
|
1784
1895
|
mc_samples_last : (n_samples,) — raw MC draws at last timestep
|
|
1785
1896
|
label_bins : {"Good": (0, 35), "Moderate": (35, 75), ...}
|
|
1786
|
-
|
|
1787
|
-
No distribution assumption — just count what fraction
|
|
1788
|
-
of actual MC samples land in each bin.
|
|
1789
1897
|
"""
|
|
1790
|
-
label_conf = {}
|
|
1791
1898
|
n = len(mc_samples_last)
|
|
1899
|
+
if n == 0:
|
|
1900
|
+
return {name: 0.0 for name in label_bins}
|
|
1901
|
+
|
|
1902
|
+
names = list(label_bins.keys())
|
|
1903
|
+
bounds = np.array(list(label_bins.values())) # (n_bins, 2)
|
|
1792
1904
|
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1905
|
+
# vectorized — broadcast (n_samples,) against (n_bins, 2)
|
|
1906
|
+
# samples shape: (1, n_samples), bounds shape: (n_bins, 1)
|
|
1907
|
+
samples = mc_samples_last[np.newaxis, :] # (1, n_samples)
|
|
1908
|
+
lo = bounds[:, 0, np.newaxis] # (n_bins, 1)
|
|
1909
|
+
hi = bounds[:, 1, np.newaxis] # (n_bins, 1)
|
|
1796
1910
|
|
|
1797
|
-
|
|
1911
|
+
hits = ((samples >= lo) & (samples < hi)).sum(axis=1) # (n_bins,)
|
|
1912
|
+
probs = hits / n # (n_bins,)
|
|
1913
|
+
|
|
1914
|
+
return dict(zip(names, probs.tolist()))
|
|
1798
1915
|
|
|
1799
1916
|
# LSTM training loop with confidence layers integrated into the loss and validation monitoring.
|
|
1800
1917
|
def fit_stm(self, X, Y, epochs=50, hidden=32, lr=5e-3, seq_len=20, print_every=5):
|
|
@@ -1804,7 +1921,7 @@ class LSTMEngine:
|
|
|
1804
1921
|
AME = self.pipeline.AME_Encoder(X)
|
|
1805
1922
|
AMR = 1.0 / (1.0 + np.exp(-AME))
|
|
1806
1923
|
|
|
1807
|
-
n_train = int(AMR * len(X))
|
|
1924
|
+
n_train = int((1.0 - AMR) * len(X))
|
|
1808
1925
|
X_tr, Y_tr = X[:n_train], Y[:n_train]
|
|
1809
1926
|
X_te, Y_te = X[n_train:], Y[n_train:]
|
|
1810
1927
|
|
|
@@ -1814,7 +1931,7 @@ class LSTMEngine:
|
|
|
1814
1931
|
np.random.shuffle(idx)
|
|
1815
1932
|
epoch_loss = 0.0
|
|
1816
1933
|
for j in idx:
|
|
1817
|
-
loss, _ = model.train_step(X_tr[j], Y_tr[j], lr=lr)
|
|
1934
|
+
loss, _ = model.train_step(X_tr[j], Y_tr[j], lr=lr, AMR=AMR)
|
|
1818
1935
|
epoch_loss += loss
|
|
1819
1936
|
epoch_loss /= n_train
|
|
1820
1937
|
|
|
@@ -1841,7 +1958,10 @@ class LSTMEngine:
|
|
|
1841
1958
|
|
|
1842
1959
|
print("[=] Training complete!")
|
|
1843
1960
|
print(f"[=] Final val loss: {val_loss:.6f}")
|
|
1961
|
+
print('===== CALIBRATION METHOD =====')
|
|
1962
|
+
self.calibrate_residual(X_te, Y_te)
|
|
1844
1963
|
|
|
1964
|
+
# get optimal lstm samples amount for the model to process
|
|
1845
1965
|
def lstm_optimal_samples(self, engine, x_seq, tolerance=0.005, max_n=500):
|
|
1846
1966
|
"""
|
|
1847
1967
|
Run increasing n_samples until std estimate stabilizes.
|
|
@@ -1862,7 +1982,7 @@ class LSTMEngine:
|
|
|
1862
1982
|
prev_std = current_std
|
|
1863
1983
|
return max_n
|
|
1864
1984
|
|
|
1865
|
-
|
|
1985
|
+
# derive local bins for flexibility in scarce dataset
|
|
1866
1986
|
def derive_bins_from_data(self, y_values, n_bins=4, labels=None):
|
|
1867
1987
|
"""
|
|
1868
1988
|
Use percentiles of actual data to set boundaries.
|
|
@@ -1915,7 +2035,7 @@ class LSTMEngine:
|
|
|
1915
2035
|
return bins
|
|
1916
2036
|
|
|
1917
2037
|
|
|
1918
|
-
# ── main predict ─────────────────────────
|
|
2038
|
+
# ── main predict function for the whole network ─────────────────────────
|
|
1919
2039
|
def predict(self, x_seq: np.ndarray,
|
|
1920
2040
|
label_bins: dict = None,
|
|
1921
2041
|
confidence_level: float = 0.90) -> Any:
|
|
@@ -1941,9 +2061,6 @@ class LSTMEngine:
|
|
|
1941
2061
|
label_confidence: {label: probability} if label_bins given
|
|
1942
2062
|
overall : single scalar confidence for last timestep
|
|
1943
2063
|
"""
|
|
1944
|
-
assert self.residual_std is not None, \
|
|
1945
|
-
"Call calibrate(X_val, Y_val) before predict()"
|
|
1946
|
-
|
|
1947
2064
|
# ── point prediction ──────────────────
|
|
1948
2065
|
preds_clean, _, _, _ = self.model.forward(x_seq)
|
|
1949
2066
|
AME = self.pipeline.AME_Encoder(x_seq) # geometric complexity scalar
|
|
@@ -1969,7 +2086,11 @@ class LSTMEngine:
|
|
|
1969
2086
|
|
|
1970
2087
|
# ── prediction interval ───────────────
|
|
1971
2088
|
total_std = np.sqrt(mc_std**2 + self.residual_std**2)
|
|
1972
|
-
|
|
2089
|
+
# compute interval for every timestep — always returns (T,) arrays
|
|
2090
|
+
interval_low = np.empty(len(mc_mean))
|
|
2091
|
+
interval_high = np.empty(len(mc_mean))
|
|
2092
|
+
for t in range(len(mc_mean)):
|
|
2093
|
+
interval_low[t], interval_high[t] = self._interval(mc_mean[t], confidence_level)
|
|
1973
2094
|
|
|
1974
2095
|
# ── label confidence (last timestep) ──
|
|
1975
2096
|
label_conf = None
|
|
@@ -1984,7 +2105,7 @@ class LSTMEngine:
|
|
|
1984
2105
|
# ── overall scalar confidence ─────────
|
|
1985
2106
|
# weighted combination of MC confidence and gate stability
|
|
1986
2107
|
gate_stability = 1.0 - gate_unc[-1] # high = stable
|
|
1987
|
-
overall = AMR * mc_confidence[-1] + \
|
|
2108
|
+
overall = (1.0 - AMR) * mc_confidence[-1] + \
|
|
1988
2109
|
self.pipeline.confidence_threshold * gate_stability
|
|
1989
2110
|
|
|
1990
2111
|
return {
|
|
@@ -1993,16 +2114,15 @@ class LSTMEngine:
|
|
|
1993
2114
|
"mc_std" : mc_std,
|
|
1994
2115
|
"mc_confidence" : mc_confidence,
|
|
1995
2116
|
"gate_uncertainty": gate_unc,
|
|
1996
|
-
"interval_low" :
|
|
1997
|
-
"interval_high" :
|
|
2117
|
+
"interval_low" : interval_low,
|
|
2118
|
+
"interval_high" : interval_high,
|
|
1998
2119
|
"label_confidence": label_conf,
|
|
1999
2120
|
"overall" : overall,
|
|
2000
2121
|
}
|
|
2001
2122
|
|
|
2002
2123
|
# ─────────────────────────────────────────────
|
|
2003
|
-
# Architecture summary helper
|
|
2124
|
+
# Architecture summary helper to visualize results
|
|
2004
2125
|
# ─────────────────────────────────────────────
|
|
2005
|
-
|
|
2006
2126
|
def architectural_summary(self, model: LSTMNetwork):
|
|
2007
2127
|
H = model.cell.hidden_size
|
|
2008
2128
|
I = model.cell.input_size
|
|
@@ -7515,7 +7635,11 @@ class IntegratedPipeline:
|
|
|
7515
7635
|
elif isinstance(a, np.ndarray) and np.issubdtype(a.dtype, np.character):
|
|
7516
7636
|
# catches arrays filled with string text
|
|
7517
7637
|
clean_str = ' '.join(a.astype(str).flatten()).replace('[', '').replace(']', '')
|
|
7518
|
-
|
|
7638
|
+
try:
|
|
7639
|
+
a = np.fromstring(clean_str, sep=' ')
|
|
7640
|
+
except:
|
|
7641
|
+
clean_string = clean_str.strip(",")
|
|
7642
|
+
a = np.fromstring(clean_string, sep=' ')
|
|
7519
7643
|
else:
|
|
7520
7644
|
# Ensure standard float array if it was integers or objects
|
|
7521
7645
|
a = np.asarray(a, dtype=float)
|
|
@@ -8358,18 +8482,18 @@ class IntegratedPipeline:
|
|
|
8358
8482
|
|
|
8359
8483
|
if choose_method == 'Y':
|
|
8360
8484
|
self.autonomous = True
|
|
8361
|
-
probs, details = self.ensemble.predict_ensemble(input_ids, X, y, method='dynamic')
|
|
8485
|
+
probs, details = self.ensemble.predict_ensemble(input_ids, X, y, method='dynamic', embedded=True)
|
|
8362
8486
|
|
|
8363
8487
|
else:
|
|
8364
8488
|
method = input('|| Choose one method (ex: dynamic): ')
|
|
8365
8489
|
if method:
|
|
8366
|
-
probs, details = self.ensemble.predict_ensemble(input_ids, X, y, method=method)
|
|
8490
|
+
probs, details = self.ensemble.predict_ensemble(input_ids, X, y, method=method, embedded=True)
|
|
8367
8491
|
else:
|
|
8368
8492
|
print('|| Invalid Method.. returning to dynamic prediction..')
|
|
8369
|
-
probs, details = self.ensemble.predict_ensemble(input_ids, X, y, method='dynamic')
|
|
8493
|
+
probs, details = self.ensemble.predict_ensemble(input_ids, X, y, method='dynamic', embedded=True)
|
|
8370
8494
|
else:
|
|
8371
8495
|
print('[+] Autonomous dynamic prediction: ')
|
|
8372
|
-
probs, details = self.ensemble.predict_ensemble(input_ids, X, y, method='dynamic')
|
|
8496
|
+
probs, details = self.ensemble.predict_ensemble(input_ids, X, y, method='dynamic', embedded=True)
|
|
8373
8497
|
|
|
8374
8498
|
self.modular_prediction_saving(input_ids, X, probs)
|
|
8375
8499
|
print('🚀 Memory Added!')
|
|
@@ -11772,6 +11896,21 @@ class PipelinePredictionManager:
|
|
|
11772
11896
|
if sequence_ids is not None:
|
|
11773
11897
|
print("\n[🔍] Using sequence encoding for transformer input due to low anisotropy.")
|
|
11774
11898
|
input_ids = sequence_ids.copy()
|
|
11899
|
+
|
|
11900
|
+
target_probs = self.pipeline.predict_proba(input_ids, X, type='Hybrid', embedded=True)
|
|
11901
|
+
target_probs = target_probs[:mlp_probs.shape[0], :mlp_probs.shape[1]]
|
|
11902
|
+
target_pred_indices = np.argmax(target_probs, axis=1)
|
|
11903
|
+
|
|
11904
|
+
if self.pipeline.cache and 'label_bins' in self.pipeline.cache:
|
|
11905
|
+
print('[=] label_bins cache found!')
|
|
11906
|
+
label_bins = self.pipeline.cache['label_bins']
|
|
11907
|
+
lstm_probs, _ = self.pipeline.ensemble._get_lstm_probs(input_ids, X, label_bins=label_bins)
|
|
11908
|
+
else:
|
|
11909
|
+
lstm_probs = None
|
|
11910
|
+
|
|
11911
|
+
results = []
|
|
11912
|
+
attention_data = [] if return_attention else None
|
|
11913
|
+
|
|
11775
11914
|
target_probs = self.pipeline.predict_proba(input_ids, X, type='Hybrid', embedded=True)
|
|
11776
11915
|
target_probs = target_probs[:mlp_probs.shape[0], :mlp_probs.shape[1]]
|
|
11777
11916
|
target_pred_indices = np.argmax(target_probs, axis=1)
|
|
@@ -11797,15 +11936,16 @@ class PipelinePredictionManager:
|
|
|
11797
11936
|
mlp_confidence = mlp_probs[i][mlp_class_idx]
|
|
11798
11937
|
mlp_label = reverse_map.get(mlp_class_idx, f"unknown_{mlp_class_idx}")
|
|
11799
11938
|
|
|
11800
|
-
|
|
11801
|
-
|
|
11802
|
-
|
|
11803
|
-
|
|
11939
|
+
if lstm_probs is not None:
|
|
11940
|
+
lstm_pred_indices = np.argmax(lstm_probs, axis=1)
|
|
11941
|
+
lstm_class_idx = lstm_pred_indices[i]
|
|
11942
|
+
lstm_confidence = lstm_probs[i][lstm_class_idx]
|
|
11943
|
+
else:
|
|
11944
|
+
lstm_confidence = None
|
|
11945
|
+
|
|
11946
|
+
target_class_idx = target_pred_indices[i]
|
|
11947
|
+
target_confidence = target_probs[i][target_class_idx]
|
|
11804
11948
|
|
|
11805
|
-
if float(mlp_confidence) < self.pipeline.confidence_threshold:
|
|
11806
|
-
target_class_idx = target_pred_indices[i]
|
|
11807
|
-
target_confidence = target_probs[i][target_class_idx]
|
|
11808
|
-
|
|
11809
11949
|
# Transformer prediction and blending
|
|
11810
11950
|
if trans_probs is not None:
|
|
11811
11951
|
trans_probs_i = trans_probs[i]
|
|
@@ -11821,14 +11961,18 @@ class PipelinePredictionManager:
|
|
|
11821
11961
|
# Blend predictions (MLP decides class, transformer calibrates confidence)
|
|
11822
11962
|
mlp_weight = mlp_confidence / (target_confidence + trans_confidence + eps)
|
|
11823
11963
|
trans_weight = trans_confidence / (target_confidence + trans_confidence + eps)
|
|
11964
|
+
if lstm_confidence is not None:
|
|
11965
|
+
lstm_weight = lstm_confidence / (target_confidence + lstm_confidence + eps)
|
|
11824
11966
|
|
|
11825
11967
|
calibration_weighting = calibration[target_class_idx] if target_class_idx < len(calibration) else 0.0
|
|
11826
11968
|
|
|
11827
11969
|
# Weighted blend: calibration_weighting * calibrated + (1-weight) * mlp
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
|
|
11970
|
+
if lstm_weight is not None:
|
|
11971
|
+
final_probs = mlp_weight * target_probs[i][:len(calibration)] + trans_weight * calibration[i][:len(calibration)] + lstm_weight * calibration[i][:len(calibration)]
|
|
11972
|
+
else:
|
|
11973
|
+
final_probs = mlp_weight * target_probs[i][:len(calibration)] + trans_weight * calibration[i][:len(calibration)]
|
|
11831
11974
|
|
|
11975
|
+
final_class_idx = target_class_idx
|
|
11832
11976
|
try:
|
|
11833
11977
|
final_confidence = final_probs[final_class_idx]
|
|
11834
11978
|
except IndexError:
|
|
@@ -11944,7 +12088,7 @@ class PipelinePredictionManager:
|
|
|
11944
12088
|
|
|
11945
12089
|
elif not results[0].get('models_agree', True) and confidence > self.pipeline.confidence_threshold:
|
|
11946
12090
|
if final_confidence is not None and confidence < self.pipeline.confidence_threshold:
|
|
11947
|
-
print("\n[⚠️]
|
|
12091
|
+
print("\n[⚠️] Low confidence detected, but both models don't agree. Using calibrated probabilities for final decision to ensure robustness.")
|
|
11948
12092
|
final_probs = self.pipeline.hybrid_prediction(rules, input_ids, dataset)
|
|
11949
12093
|
final_idx = final_probs[0].argmax()
|
|
11950
12094
|
original_idx = final_idx
|
|
@@ -11953,6 +12097,16 @@ class PipelinePredictionManager:
|
|
|
11953
12097
|
final_idx = int(np.argmax(final_probs[:len(reverse_map)-1]))
|
|
11954
12098
|
print(f"[⚠️] Clamping {final_idx} → {final_idx}")
|
|
11955
12099
|
final_idx = int(final_idx)
|
|
12100
|
+
else:
|
|
12101
|
+
print('[🎯] Stable confidence established, But both Models doesnt Agree, Re-evaluating...')
|
|
12102
|
+
final_probs = self.pipeline.hybrid_prediction(rules, input_ids, dataset)
|
|
12103
|
+
final_idx = final_probs[0].argmax()
|
|
12104
|
+
original_idx = final_idx
|
|
12105
|
+
|
|
12106
|
+
if final_idx > len(reverse_map):
|
|
12107
|
+
final_idx = int(np.argmax(final_probs[:len(reverse_map)-1]))
|
|
12108
|
+
print(f"[⚠️] Clamping {final_idx} → {final_idx}")
|
|
12109
|
+
final_idx = int(final_idx)
|
|
11956
12110
|
|
|
11957
12111
|
chosen_label = reverse_map.get(final_idx, f"unknown_{final_idx}")
|
|
11958
12112
|
try:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: AbstractIntegratedModule
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: Framework for Advanced Integrated Non-LLM AI Module library - Backend Framework for Non-LLM AI Agent Framework
|
|
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: Beta, 0.4.
|
|
45
|
+
- Development Stage: Beta, 0.4.2.
|
|
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.
|
|
@@ -62,11 +62,11 @@ https://github.com/Micro-Novelty/IntegratedPipeline-Specialized-Non-LLM-AI-Agent
|
|
|
62
62
|
- Proven Works on ARM64 Environment, Training and Prediction works efficient on Docker ARM64 environment with QEMU, good parallelizing behavior is guaranteed.
|
|
63
63
|
- P2P Works efficiently in ARM64 Docker + QEMU, No conflicting socket and all prediction works efficiently.
|
|
64
64
|
- AWE setup Proven Efficient on Hard-uncontrolled dataset such as Activity Recognition from the given Database.
|
|
65
|
-
|
|
65
|
+
- LSTM is Optimized efficiently for scarce data with AWE method.
|
|
66
66
|
-----
|
|
67
67
|
|
|
68
68
|
<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" />
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
|
|
71
71
|
## [+] MANN Intro
|
|
72
72
|
[=] Memory augmented Neural network (MANN) is a neural network architecture coupled with an external, dynamic memory module, allowing it to store, retrieve, and update information similarly to a computer's RAM. Unlike traditional networks that store knowledge only in weight parameters, MANNs excel at fast learning, long-term dependency handling, and episodic recall, In IntegratedPipeline, Its memory is stored in a custom database inside your local machine, then later used for memory retrieval, transfered to the AI Dictionary where it can finnaly recall its memory when input condition matched with memory.
|
|
@@ -10,7 +10,7 @@ https://github.com/Micro-Novelty/IntegratedPipeline-Specialized-Non-LLM-AI-Agent
|
|
|
10
10
|
#### 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.
|
|
11
11
|
|
|
12
12
|
### Library Short Description:
|
|
13
|
-
- Development Stage: Beta, 0.4.
|
|
13
|
+
- Development Stage: Beta, 0.4.2.
|
|
14
14
|
- Maintainer: Micro-Novelty.
|
|
15
15
|
- library Source-Code is Open-sourced on github.
|
|
16
16
|
- Purpose: Specifically Designed for providing Non-LLM AI Agent Framework for edge Devices, Optimized for ARM64 architecture.
|
|
@@ -30,11 +30,11 @@ https://github.com/Micro-Novelty/IntegratedPipeline-Specialized-Non-LLM-AI-Agent
|
|
|
30
30
|
- Proven Works on ARM64 Environment, Training and Prediction works efficient on Docker ARM64 environment with QEMU, good parallelizing behavior is guaranteed.
|
|
31
31
|
- P2P Works efficiently in ARM64 Docker + QEMU, No conflicting socket and all prediction works efficiently.
|
|
32
32
|
- AWE setup Proven Efficient on Hard-uncontrolled dataset such as Activity Recognition from the given Database.
|
|
33
|
-
|
|
33
|
+
- LSTM is Optimized efficiently for scarce data with AWE method.
|
|
34
34
|
-----
|
|
35
35
|
|
|
36
36
|
<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" />
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
|
|
39
39
|
## [+] MANN Intro
|
|
40
40
|
[=] Memory augmented Neural network (MANN) is a neural network architecture coupled with an external, dynamic memory module, allowing it to store, retrieve, and update information similarly to a computer's RAM. Unlike traditional networks that store knowledge only in weight parameters, MANNs excel at fast learning, long-term dependency handling, and episodic recall, In IntegratedPipeline, Its memory is stored in a custom database inside your local machine, then later used for memory retrieval, transfered to the AI Dictionary where it can finnaly recall its memory when input condition matched with memory.
|
|
@@ -8,7 +8,7 @@ class BinaryDistribution(Distribution):
|
|
|
8
8
|
|
|
9
9
|
setup(
|
|
10
10
|
name="AbstractIntegratedModule",
|
|
11
|
-
version="0.4.
|
|
11
|
+
version="0.4.2",
|
|
12
12
|
description="Framework for Advanced Integrated Non-LLM AI Module library - Backend Framework for Non-LLM AI Agent Framework",
|
|
13
13
|
long_description=open("README.md", encoding="utf-8").read(),
|
|
14
14
|
long_description_content_type="text/markdown",
|
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|