AbstractIntegratedModule 1.2.5__tar.gz → 1.2.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-1.2.5 → abstractintegratedmodule-1.2.7}/AbstractIntegratedModule.egg-info/PKG-INFO +1 -1
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/AbstractIntegratedModule.py +124 -40
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/AbstractOptimizedModules.c +200 -200
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/PKG-INFO +1 -1
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/README.md +110 -40
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/pyproject.toml +1 -1
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/setup.py +1 -1
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/AbstractIntegratedModule.egg-info/SOURCES.txt +0 -0
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/AbstractIntegratedModule.egg-info/dependency_links.txt +0 -0
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/AbstractIntegratedModule.egg-info/requires.txt +0 -0
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/AbstractIntegratedModule.egg-info/top_level.txt +0 -0
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/AbstractOptimizedModules.pyx +0 -0
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/MANIFEST.in +0 -0
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/abstract_model_storage/Cargo.toml +0 -0
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/abstract_model_storage/pyproject.toml +0 -0
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/abstract_model_storage/src/lib.rs +0 -0
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/abstract_model_storage/target/debug/build/libsqlite3-sys-ed07b882cd2aa5e2/out/bindgen.rs +0 -0
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/abstract_model_storage/target/debug/build/serde_core-ebc15f2e9cad7f5f/out/private.rs +0 -0
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/abstract_model_storage/target/debug/build/target-lexicon-08527f45de28143d/out/host.rs +0 -0
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/abstract_model_storage/target/release/build/libsqlite3-sys-bf0400df4523274c/out/bindgen.rs +0 -0
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/abstract_model_storage/target/release/build/serde_core-5cdb76131825e4af/out/private.rs +0 -0
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/abstract_model_storage/target/release/build/target-lexicon-43eb95a0588bf457/out/host.rs +0 -0
- {abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/setup.cfg +0 -0
{abstractintegratedmodule-1.2.5 → abstractintegratedmodule-1.2.7}/AbstractIntegratedModule.py
RENAMED
|
@@ -1825,9 +1825,11 @@ class SNNNetwork:
|
|
|
1825
1825
|
optimizer.step(self.layers)
|
|
1826
1826
|
return loss, logits
|
|
1827
1827
|
|
|
1828
|
-
def predict(self, input_spikes):
|
|
1828
|
+
def predict(self, input_spikes, task='classification'):
|
|
1829
1829
|
self.eval()
|
|
1830
1830
|
logits = self.forward(input_spikes)
|
|
1831
|
+
if task == 'regression':
|
|
1832
|
+
return logits[:, 0], logits
|
|
1831
1833
|
return np.argmax(logits, axis=1), logits
|
|
1832
1834
|
|
|
1833
1835
|
def firing_rates(self):
|
|
@@ -10979,12 +10981,17 @@ class AgentDistributedInference:
|
|
|
10979
10981
|
rules = payload.get('rules')
|
|
10980
10982
|
X = payload.get('X')
|
|
10981
10983
|
y = payload.get('y')
|
|
10984
|
+
|
|
10982
10985
|
|
|
10983
10986
|
# Sanitize if needed
|
|
10984
10987
|
if test_titles:
|
|
10985
10988
|
test_titles = self._sanitize_structured(test_titles)
|
|
10986
10989
|
if label_map:
|
|
10987
10990
|
label_map = self._sanitize_structured(label_map)
|
|
10991
|
+
if label_map is None or not isinstance(label_map, dict):
|
|
10992
|
+
raise ValueError('[>] Label map is None or invalid label map instance!')
|
|
10993
|
+
|
|
10994
|
+
|
|
10988
10995
|
if rules:
|
|
10989
10996
|
rules = self._sanitize_structured(rules)
|
|
10990
10997
|
if X is not None:
|
|
@@ -10992,10 +10999,12 @@ class AgentDistributedInference:
|
|
|
10992
10999
|
if y is not None:
|
|
10993
11000
|
y = self._sanitize_structured(y)
|
|
10994
11001
|
|
|
10995
|
-
if X is None
|
|
10996
|
-
print('[=] Got necessary titles, label_map
|
|
11002
|
+
if X is None and y is None and test_titles is not None and rules is not None:
|
|
11003
|
+
print('[=] Got necessary titles, label_map, rules samples')
|
|
11004
|
+
elif X is not None and y is not None and test_titles is None and rules is None:
|
|
11005
|
+
print('[=] Got necessary titles, rules, label_map, X And Y samples.')
|
|
10997
11006
|
else:
|
|
10998
|
-
|
|
11007
|
+
raise ValueError('[>] All samples are None!')
|
|
10999
11008
|
else:
|
|
11000
11009
|
text = payload.get('text')
|
|
11001
11010
|
if text:
|
|
@@ -11006,9 +11015,8 @@ class AgentDistributedInference:
|
|
|
11006
11015
|
text = str(payload) if payload else None
|
|
11007
11016
|
|
|
11008
11017
|
if not text and not test_titles:
|
|
11009
|
-
print('[
|
|
11010
|
-
|
|
11011
|
-
|
|
11018
|
+
print('[INFO] No text or test_titles in message payload.')
|
|
11019
|
+
|
|
11012
11020
|
# Run the actual prediction
|
|
11013
11021
|
print(f'[=] Initiating prediction method')
|
|
11014
11022
|
try:
|
|
@@ -13340,16 +13348,16 @@ class IntegratedPipeline:
|
|
|
13340
13348
|
if value.shape[0] != num_classes and \
|
|
13341
13349
|
(value.ndim < 2 or value.shape[1] != num_classes):
|
|
13342
13350
|
return (True,
|
|
13343
|
-
f"Array shape {value.shape} doesn't match "
|
|
13351
|
+
f"[>] Array shape {value.shape} doesn't match "
|
|
13344
13352
|
f"{num_classes} classes in key '{key}'",
|
|
13345
13353
|
"Hybrid feature stored incorrectly, clear entry")
|
|
13346
13354
|
if value.size == 0:
|
|
13347
13355
|
return (True,
|
|
13348
|
-
f"Empty array in key '{key}'",
|
|
13356
|
+
f"[>] Empty array in key '{key}'",
|
|
13349
13357
|
"Corrupted array, clear entry")
|
|
13350
13358
|
if not np.isfinite(value).all():
|
|
13351
13359
|
return (True,
|
|
13352
|
-
f"NaN/Inf values in array at key '{key}'",
|
|
13360
|
+
f"[>] NaN/Inf values in array at key '{key}'",
|
|
13353
13361
|
"Corrupted values, clear entry")
|
|
13354
13362
|
|
|
13355
13363
|
# validate tuple values in dict
|
|
@@ -13359,10 +13367,10 @@ class IntegratedPipeline:
|
|
|
13359
13367
|
)
|
|
13360
13368
|
if corrupted:
|
|
13361
13369
|
return (True,
|
|
13362
|
-
f"Corrupted tuple value at key '{key}': {reason}",
|
|
13370
|
+
f"[>] Corrupted tuple value at key '{key}': {reason}",
|
|
13363
13371
|
fix)
|
|
13364
13372
|
|
|
13365
|
-
return (False, "Valid dict structure", None)
|
|
13373
|
+
return (False, "[>] Valid dict structure", None)
|
|
13366
13374
|
|
|
13367
13375
|
|
|
13368
13376
|
def initialize_fitting(self, text):
|
|
@@ -13381,23 +13389,22 @@ class IntegratedPipeline:
|
|
|
13381
13389
|
unique_in_batch = len(np.unique(y_raw))
|
|
13382
13390
|
|
|
13383
13391
|
if num_classes is None:
|
|
13384
|
-
# no model exists
|
|
13392
|
+
# no model exists — this is the legitimate case where
|
|
13385
13393
|
# inferring from y_raw is correct (e.g. first-time initialization)
|
|
13386
13394
|
num_classes = unique_in_batch
|
|
13387
13395
|
print(f'[=] No existing model — initializing with {num_classes} classes '
|
|
13388
13396
|
f'from this batch')
|
|
13389
13397
|
elif unique_in_batch > num_classes:
|
|
13390
|
-
# batch contains
|
|
13391
|
-
#
|
|
13398
|
+
# batch contains more classes than the model supports
|
|
13399
|
+
# a problem — can't onehot-encode into a smaller space
|
|
13392
13400
|
print(f'[⚠️] Batch contains {unique_in_batch} unique classes but model '
|
|
13393
13401
|
f'only supports {num_classes} — expanding to {unique_in_batch}')
|
|
13394
13402
|
num_classes = unique_in_batch
|
|
13395
13403
|
elif unique_in_batch < num_classes:
|
|
13396
13404
|
# batch just doesn't happen to contain all classes.
|
|
13397
|
-
# use
|
|
13405
|
+
# use model's full num_classes so onehot stays the right width
|
|
13398
13406
|
print(f'[=] Batch only contains {unique_in_batch}/{num_classes} classes '
|
|
13399
13407
|
f'— using full model class count for onehot width')
|
|
13400
|
-
# num_classes stays as the model's true value, no change needed
|
|
13401
13408
|
|
|
13402
13409
|
y_onehot = np.zeros((len(y_raw), num_classes))
|
|
13403
13410
|
|
|
@@ -13408,9 +13415,7 @@ class IntegratedPipeline:
|
|
|
13408
13415
|
else:
|
|
13409
13416
|
print(f'[⚠️] Label {label_idx} out of range for {num_classes} classes '
|
|
13410
13417
|
f'at sample {idx} — skipping onehot assignment')
|
|
13411
|
-
|
|
13412
|
-
# silently assigning to a wrong class
|
|
13413
|
-
|
|
13418
|
+
|
|
13414
13419
|
return y_onehot
|
|
13415
13420
|
|
|
13416
13421
|
|
|
@@ -20651,9 +20656,9 @@ class SNNUtility:
|
|
|
20651
20656
|
# SNN Network Build
|
|
20652
20657
|
#-----------------
|
|
20653
20658
|
def build_snn_network(n_in, n_hidden, n_classes, rng, decay=0.95,
|
|
20654
|
-
threshold=0.1, input_noise_std=0.0, reduction='mean'):
|
|
20659
|
+
threshold=0.1, input_noise_std=0.0, reduction='mean', p=0.2):
|
|
20655
20660
|
layer1 = LIFDenseLayer(n_in, n_hidden, decay=decay, threshold=threshold, rng=rng)
|
|
20656
|
-
drop1 = SpikeDropout(p=
|
|
20661
|
+
drop1 = SpikeDropout(p=p, rng=rng)
|
|
20657
20662
|
readout = ReadoutLayer(n_hidden, n_classes, decay=decay, rng=rng)
|
|
20658
20663
|
net = SNNNetwork([layer1, drop1, readout], readout_reduction=reduction,
|
|
20659
20664
|
input_noise_std=input_noise_std, rng=rng)
|
|
@@ -21117,23 +21122,69 @@ def print_confidence_report(results, top_k=3, max_print_threshold=50):
|
|
|
21117
21122
|
f"|| Top-{top_k}: [{top_str}]")
|
|
21118
21123
|
return pred_cache
|
|
21119
21124
|
|
|
21125
|
+
def initialize_snn_network(X=None, y=None, label_map=None, n_hidden=64, n_in=None, n_classes=None, seed=0, random_state=42):
|
|
21126
|
+
try:
|
|
21127
|
+
utility = SNNUtility()
|
|
21128
|
+
rng = utility.seed_everything(seed)
|
|
21129
|
+
if n_in is not None and n_classes is not None:
|
|
21130
|
+
net, hidden_layer = build_snn_network(n_in, n_hidden, n_classes, rng)
|
|
21131
|
+
else:
|
|
21132
|
+
X_train, X_test, y_train, y_test, X_val, y_val = _prepare_train_val_split(X=X, y=y, random_state=random_state)
|
|
21133
|
+
n_in = X_train.shape[1]
|
|
21134
|
+
n_classes = max(len(label_map), int(y_train.max() + 1))
|
|
21135
|
+
net, hidden_layer = build_snn_network(n_in, n_hidden, n_classes, rng)
|
|
21120
21136
|
|
|
21137
|
+
print(f'[>] Initialized SNN Network with hidden dim: {n_hidden}')
|
|
21138
|
+
return net, hidden_layer, utility
|
|
21139
|
+
except Exception as e:
|
|
21140
|
+
print(f'[>] Error initializing SNN Network and hidden layer: {e}')
|
|
21141
|
+
return None, None, None
|
|
21121
21142
|
|
|
21122
|
-
def train_snn_network(X=None, y=None, net=None, hidden_layer=None, label_map=None,
|
|
21143
|
+
def train_snn_network(X=None, y=None, net=None, hidden_layer=None, utility=None, task='classification', label_map=None,
|
|
21123
21144
|
epochs=50, batch_size=32, n_steps=20,
|
|
21124
21145
|
n_hidden=64, lr=2e-3, seed=0, pretrain_steps=200, unsupervised_stdp=False,
|
|
21125
|
-
random_state=42):
|
|
21146
|
+
random_state=42, ):
|
|
21126
21147
|
tracker = DiagnosticsTracker()
|
|
21127
|
-
utility = SNNUtility()
|
|
21128
21148
|
cache = {}
|
|
21149
|
+
reg = (task == 'regression')
|
|
21150
|
+
if utility is None:
|
|
21151
|
+
utility = SNNUtility()
|
|
21129
21152
|
|
|
21130
21153
|
rng = utility.seed_everything(seed)
|
|
21131
|
-
|
|
21154
|
+
if reg:
|
|
21155
|
+
X = np.asarray(X, dtype=np.float64)
|
|
21156
|
+
y = np.asarray(y, dtype=np.float64)
|
|
21157
|
+
if X.ndim > 2:
|
|
21158
|
+
X = X.reshape(len(X), -1) # (N, W, F) -> (N, W*F)
|
|
21159
|
+
if y.ndim == 1:
|
|
21160
|
+
y = y.reshape(-1, 1)
|
|
21161
|
+
if len(X) != len(y):
|
|
21162
|
+
raise ValueError(f'X has {len(X)} samples but y has {len(y)}')
|
|
21163
|
+
n = len(X)
|
|
21164
|
+
a, b = int(0.70 * n), int(0.85 * n) # chronological: never shuffle a time series
|
|
21165
|
+
X_train, X_val, X_test = X[:a], X[a:b], X[b:]
|
|
21166
|
+
y_train, y_val, y_test = y[:a], y[a:b], y[b:]
|
|
21167
|
+
lo, hi = X_train.min(0), X_train.max(0) # [0,1] scaling for spike encoding (train stats)
|
|
21168
|
+
span = np.where(hi - lo < 1e-9, 1.0, hi - lo)
|
|
21169
|
+
scale_x = lambda A: np.clip((A - lo) / span, 0.0, 1.0)
|
|
21170
|
+
X_train, X_val, X_test = scale_x(X_train), scale_x(X_val), scale_x(X_test)
|
|
21171
|
+
y_mu, y_sd = y_train.mean(0), y_train.std(0) + 1e-9 # per-output standardisation
|
|
21172
|
+
y_train_net = (y_train - y_mu) / y_sd
|
|
21173
|
+
n_classes = y.shape[1] # readout width = number of regression outputs
|
|
21174
|
+
loss_fn = mse_loss
|
|
21175
|
+
else:
|
|
21176
|
+
|
|
21177
|
+
X_train, X_test, y_train, y_test, X_val, y_val = _prepare_train_val_split(X=X, y=y, random_state=random_state)
|
|
21178
|
+
y_train_net = y_train
|
|
21179
|
+
n_classes = max(len(label_map), int(y_train.max() + 1))
|
|
21180
|
+
|
|
21132
21181
|
n_in = X_train.shape[1]
|
|
21133
|
-
|
|
21134
|
-
if net is None and hidden_layer is None:
|
|
21182
|
+
if net is None or hidden_layer is None:
|
|
21135
21183
|
net, hidden_layer = build_snn_network(n_in, n_hidden, n_classes, rng)
|
|
21136
21184
|
|
|
21185
|
+
if reg:
|
|
21186
|
+
net.regression_scalers = {'lo': lo, 'span': span, 'y_mu': y_mu, 'y_sd': y_sd}
|
|
21187
|
+
|
|
21137
21188
|
encoder = PoissonEncoder(n_steps=n_steps, max_rate=1.0, rng=rng)
|
|
21138
21189
|
optimizer = Adam(lr=lr, weight_decay=1e-5)
|
|
21139
21190
|
homeostasis = ThresholdHomeostasis(target_rate=0.15, adjust_rate=0.05)
|
|
@@ -21169,16 +21220,28 @@ def train_snn_network(X=None, y=None, net=None, hidden_layer=None, label_map=Non
|
|
|
21169
21220
|
|
|
21170
21221
|
# Evaluation
|
|
21171
21222
|
net.eval()
|
|
21172
|
-
|
|
21173
|
-
|
|
21174
|
-
|
|
21175
|
-
|
|
21176
|
-
|
|
21177
|
-
|
|
21178
|
-
|
|
21179
|
-
|
|
21180
|
-
|
|
21181
|
-
|
|
21223
|
+
preds, _ = net.predict(encoder.encode(X_test), task=task)
|
|
21224
|
+
train_preds, _ = net.predict(encoder.encode(X_train), task=task)
|
|
21225
|
+
if reg:
|
|
21226
|
+
p_te = np.asarray(preds).reshape(len(X_test), -1) * y_sd + y_mu # back to original units
|
|
21227
|
+
p_tr = np.asarray(train_preds).reshape(len(X_train), -1) * y_sd + y_mu
|
|
21228
|
+
test_rmse = float(np.sqrt(np.mean((p_te - y_test) ** 2)))
|
|
21229
|
+
test_mae = float(np.mean(np.abs(p_te - y_test)))
|
|
21230
|
+
train_rmse = float(np.sqrt(np.mean((p_tr - y_train) ** 2)))
|
|
21231
|
+
r2 = float(1.0 - np.sum((p_te - y_test) ** 2) / (np.sum((y_test - y_train.mean(0)) ** 2) + 1e-12))
|
|
21232
|
+
print(f"[=] Epoch {epoch:2d} || train_loss={np.mean(epoch_losses):.4f} train_rmse={train_rmse:,.1f} || "
|
|
21233
|
+
f"test_rmse={test_rmse:,.1f} test_mae={test_mae:,.1f} R2={r2:.3f} || "
|
|
21234
|
+
f"hidden_threshold={hidden_layer.threshold:.3f}")
|
|
21235
|
+
test_score = r2
|
|
21236
|
+
else:
|
|
21237
|
+
test_acc = utility.accuracy(preds, y_test)
|
|
21238
|
+
train_acc = utility.accuracy(train_preds, y_train)
|
|
21239
|
+
print(f"[=] Epoch {epoch:2d} || train_loss={np.mean(epoch_losses):.4f} "
|
|
21240
|
+
f"train_acc={train_acc*100:5.1f} || test_acc={test_acc*100:5.1f}% || "
|
|
21241
|
+
f"hidden_threshold={hidden_layer.threshold:.3f}")
|
|
21242
|
+
test_score = test_acc
|
|
21243
|
+
|
|
21244
|
+
tracker.record(net, epoch, loss=loss, test_acc=test_score)
|
|
21182
21245
|
|
|
21183
21246
|
report = network_report(net)
|
|
21184
21247
|
print_report(report)
|
|
@@ -21188,8 +21251,25 @@ def train_snn_network(X=None, y=None, net=None, hidden_layer=None, label_map=Non
|
|
|
21188
21251
|
|
|
21189
21252
|
return encoder, net, report, warnings
|
|
21190
21253
|
|
|
21254
|
+
def _snn_predict_regression(X, net, encoder, n_mc=20):
|
|
21255
|
+
sc = getattr(net, 'regression_scalers', None)
|
|
21256
|
+
if sc is None:
|
|
21257
|
+
raise ValueError('[>] net has no regression_scalers: train it with train_snn_network(task="regression") first')
|
|
21258
|
+
X = np.asarray(X, dtype=np.float64)
|
|
21259
|
+
X = X.reshape(len(X), -1) # (N, W*F)
|
|
21260
|
+
Xs = np.clip((X - sc['lo']) / sc['span'], 0.0, 1.0)
|
|
21261
|
+
|
|
21262
|
+
# the Poisson encoder is stochastic, its repeated to get a prediction and a spread
|
|
21263
|
+
outs = np.stack([net.predict(encoder.encode(Xs), task='regression')[1] for _ in range(n_mc)]) # (n_mc, N, n_out)
|
|
21264
|
+
mean = outs.mean(0) * sc['y_sd'] + sc['y_mu']
|
|
21265
|
+
std = outs.std(0) * sc['y_sd']
|
|
21266
|
+
|
|
21267
|
+
if mean.shape[1] == 1:
|
|
21268
|
+
mean, std = mean[:, 0], std[:, 0]
|
|
21269
|
+
return {'prediction': mean, 'std': std}
|
|
21191
21270
|
|
|
21192
|
-
|
|
21271
|
+
|
|
21272
|
+
def snn_predict(X=None, y=None, task='classification', label_map=None, encoder=None, net=None, random_state=42, max_print_threshold=50):
|
|
21193
21273
|
utility = SNNUtility()
|
|
21194
21274
|
reverse_map = None
|
|
21195
21275
|
|
|
@@ -21223,9 +21303,13 @@ def snn_predict(X=None, y=None, label_map=None, encoder=None, net=None, random_s
|
|
|
21223
21303
|
if label_map and isinstance(label_map, dict):
|
|
21224
21304
|
reverse_map = {v: k for k, v in label_map.items()}
|
|
21225
21305
|
|
|
21306
|
+
if task == 'regression':
|
|
21307
|
+
result = _snn_predict_regression(X, net, encoder)
|
|
21308
|
+
return result
|
|
21309
|
+
|
|
21226
21310
|
try:
|
|
21227
21311
|
test_spikes = encoder.encode(X)
|
|
21228
|
-
preds, logits = net.predict(test_spikes)
|
|
21312
|
+
preds, logits = net.predict(test_spikes, task=task)
|
|
21229
21313
|
if reverse_map is not None:
|
|
21230
21314
|
# SNN-specific: uncertainty from the encoder's own stochasticity
|
|
21231
21315
|
mc_results = mc_confidence(net, encoder, X, n_samples=20, class_names=reverse_map)
|