File size: 11,627 Bytes
2e41b25
 
 
 
 
 
 
 
 
 
 
8f0d906
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2e41b25
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
---
license: mit
language:
- en
tags:
- neural-network
- self-training
- anomaly-detection
- patent
- comfyui
---
# STANNO β€” Self-Training Artificial Neural Network Object

A neural network that trains another neural network. No backpropagation. Directly modifies weights.

## What is this?

STANNO is a modern, open-source implementation of the **Artificial Neurogenesis Network** concept from US Patent 5,852,815 (Thaler, 1998). Instead of using backpropagation to update weights, one network (the **TrainerNet**) computes weight updates for another network (the **TraineeNet**) by analyzing its internal state.

Think of it as: you have a student network that learns, and a teacher network that decides how the student's weights should change β€” without autodiff, without gradients.

Multiple STANNOs can be **cascaded** into pipelines where the output of one feeds the next, trained end-to-end with gradient flow across stage boundaries. A **DSANNO** (Data Scanning variant) wraps any trained STANNO and scans large datasets to find rows that match its learned representation β€” the inverse of anomaly detection.

### Patent & Attribution

This codebase is an implementation of the architecture described in **US Patent 5,852,815** (*Artificial Neurogenesis Network*) filed by Stephen L. Thaler. The original patent has expired (US utility patents run 20 years from filing date). This open-source implementation builds upon the original design with modern extensions: **CascadeSTANNO** (multi-stage gradient flow), **DSANNO** (data scanning), and integration with contemporary frameworks (PyTorch, ComfyUI).

We acknowledge and attribute all core concepts to Thaler's patent. See [Patents & Reference](#papers--reference) below for full citation details.

## ⚠️ Before you start

**STANNO is specialized**, not a general-purpose neural network. It's designed for:
- Anomaly detection βœ“
- Online learning βœ“
- Interpretability βœ“

It's **not** for:
- Regression (use PyTorch/TensorFlow instead)
- Image generation alone (use with ComfyUI + SD 1.5)
- High-accuracy function fitting

See [STANNO_IS_NOT.md](STANNO_IS_NOT.md) for details.

## Why would I use this?

- **Direct weight modification**: The trainer has explicit control over what happens to each synapse. Useful for interpretability, debugging, or unconventional training schemes.
- **Meta-learning friendly**: The trainer itself can be learned (via evolution or other methods). Different tasks can teach the trainer how to train.
- **Composable**: Three trainer implementations (Fixed, LocalRule, Evolutionary) let you pick the right tool.
- **Cascadable**: Chain multiple STANNOs into encoder-decoder pipelines or progressive compression networks. Freeze individual stages, adapt others β€” all in the same object.
- **Data scanning**: DSANNO turns any trained STANNO into a semantic scanner. Find the rows in a large dataset that most closely match the network's learned distribution, with auto-calibrated thresholds and top-k retrieval.
- **ComfyUI integration**: Nine custom nodes for image generation workflows.
- **Works with LLMs**: Filter or augment LLM inputs/outputs using STANNO's anomaly detection.

## Install

```bash
pip install git+https://github.com/nitroxido/stanno.git
```

Or clone and install locally:
```bash
git clone https://github.com/nitroxido/stanno.git
cd stanno
pip install -e .
```

For ComfyUI, the nodes auto-install via ComfyUI-Manager, or manually:
```bash
cd ComfyUI/custom_nodes
git clone https://github.com/nitroxido/comfyui-stanno.git
cd comfyui-stanno
pip install -r requirements.txt
```

## Quick Start

### Train on sin(x)

```python
import numpy as np
from stanno import STANNO
from stanno.config.schema import STANNOConfig

# Config
config = STANNOConfig(
    layers=[1, 32, 1],
    trainer_type="fixed",
    learning_rate=0.005,
)

# Data
x = np.linspace(0, 1, 512, dtype=np.float32).reshape(-1, 1)
y = np.sin(2 * np.pi * x).astype(np.float32)

# Train
stanno = STANNO(config)
stanno.fit(x, y, epochs=500, batch_size=64)

# Predict
y_pred = stanno.predict(np.array([[0.25]], dtype=np.float32))
print(f"sin(0.25) β‰ˆ {y_pred[0, 0]:.3f}")  # β‰ˆ 0.587
```

### Load from config file

```bash
python -m stanno train --config examples/sin_regression.json
python -m stanno predict --config examples/sin_regression.json --input 0.25
python -m stanno dream --config examples/sin_regression.json
```

### Anomaly filtering (pre-filter for LLM)

```python
from stanno import STANNO
from stanno.config.schema import FilterConfig
from stanno.integration.filter import STANNOFilter
from stanno.integration.llm_client import LLMClient

# Train STANNO on normal embeddings
stanno = STANNO(...)
stanno.fit(normal_embeddings, normal_embeddings, epochs=100)

# Set up filter
filter_config = FilterConfig(anomaly_threshold=0.7, block_above_threshold=True)
llm = LLMClient(llm_config)
filt = STANNOFilter(stanno, filter_config, llm)

# Score incoming prompt
score, meta = filt.score(embedding)
if not meta["blocked"]:
    response = filt.filter_and_send(messages, embedding)
```

### Online learning

```python
from stanno.integration.continual import ContinualSTANNO

cont = ContinualSTANNO(stanno)

# One sample at a time
for x_i, y_i in stream:
    loss = cont.observe(x_i, y_i)
    print(f"Step {cont.steps}: loss={loss:.4f}")

# Check held-out test set
test_loss = cont.test_loss(x_test, y_test)
print(f"Test loss: {test_loss:.4f}")
```

### Cascading (encoder β†’ decoder pipeline)

```python
from stanno import STANNO, STANNOConfig, CascadeSTANNO

# Two-stage autoencoder: compress 768-D embeddings to 64-D
enc = STANNO(STANNOConfig(layers=[768, 256, 64], learning_rate=0.05))
dec = STANNO(STANNOConfig(layers=[64, 256, 768], learning_rate=0.05))

ae = CascadeSTANNO([enc, dec])
ae.fit(embeddings, embeddings, epochs=200, batch_size=32)

# Get compressed representation
codes = ae.intermediate_output(embeddings, stage=0)  # (N, 64)

# Freeze encoder, continue training decoder
ae.freeze(0)
ae.fit(embeddings, embeddings, epochs=100)  # only decoder updates
```

CLI equivalent:
```bash
python -m stanno cascade --config examples/cascade_autoencoder.json
```

### Data scanning (DSANNO)

```python
from stanno import STANNO, STANNOConfig, DSANNO

# Train on known-good data
detector = STANNO(STANNOConfig(layers=[64, 128, 64], learning_rate=0.05))
detector.fit(normal_data, normal_data, epochs=200)

scanner = DSANNO(detector, mode="reconstruction")

# Auto-calibrate threshold from training distribution
threshold = scanner.calibrate_threshold(normal_data, percentile=95)

# Scan a large dataset β€” returns matching rows
result = scanner.scan(large_dataset, threshold=threshold)
matching_rows = large_dataset[result.matched_indices()]

# Or just get the top-k best matches
indices, scores, _ = scanner.top_k(large_dataset, k=20)
```

CLI equivalent:
```bash
python -m stanno scan --model model.stanno.pkl --data corpus.npy --top-k 20
python -m stanno scan --model model.stanno.pkl --data corpus.npy --threshold 0.05
```

## The three trainers

| Trainer | How it works | Use case |
|---------|-------------|----------|
| **Fixed** | 4-module patent-faithful design. No learning, deterministic. | Baseline, reproducibility |
| **LocalRule** | Shared MLP learns per-synapse update rules. Can meta-train. | Adaptive training, interpretability |
| **Evolutionary** | ES-based. Evolves per-layer learning rates. No autodiff. | Exploration, unconventional problems |

## ComfyUI nodes

Nine nodes in the **STANNO** category:

| Node | What it does |
|------|--------------|
| **STANNOLoad** | Create or load a STANNO model |
| **STANNOTrainImages** | Train as autoencoder on image batch |
| **STANNOScoreImages** | Filter images by reconstruction error |
| **STANNODreamCond** | Modify CLIP conditioning with dream mode |
| **STANNODynamicLoRA** | Apply dream output as LoRA patches |
| **STANNOCompositeCheck** | Route images to whichever of two STANNOs matches best |
| **STANNOScan** | DSANNO scanner: auto-calibrated threshold + top-k image retrieval |
| **STANNOCascadeLoad** | Create or load a multi-stage CascadeSTANNO |
| **STANNOCascadeTrainImages** | Train a cascade end-to-end on an image batch |

See [comfyui-stanno-integration.md](./comfyui-stanno-integration.md) for workflows and examples.

## Architecture

```
stanno/
β”œβ”€β”€ config/           # Dataclasses for all configuration
β”œβ”€β”€ core/
β”‚   β”œβ”€β”€ backend.py    # NumPy & PyTorch backend abstraction
β”‚   β”œβ”€β”€ trainer.py    # AbstractTrainerNet base class + cascade API
β”‚   β”œβ”€β”€ trainee.py    # TraineeNet (the student network)
β”‚   └── stanno.py     # STANNO orchestrator
β”œβ”€β”€ trainers/
β”‚   β”œβ”€β”€ fixed.py      # 4-module patent design (cascade-aware)
β”‚   β”œβ”€β”€ local_rule.py # Learned per-synapse rules
β”‚   └── evolutionary.py # ES-based adaptation
β”œβ”€β”€ data/             # Loaders for CSV, JSON, NumPy, builtin datasets
β”œβ”€β”€ integration/
β”‚   β”œβ”€β”€ llm_client.py    # OpenAI-compatible HTTP client (Ollama, etc.)
β”‚   β”œβ”€β”€ filter.py        # STANNOFilter for anomaly detection
β”‚   β”œβ”€β”€ continual.py     # ContinualSTANNO for online learning
β”‚   β”œβ”€β”€ cascade.py       # CascadeSTANNO β€” multi-stage chained networks
β”‚   └── dsanno.py        # DSANNO β€” data scanning and semantic retrieval
└── cli.py            # Command-line interface
```

### CLI subcommands

| Command | What it does |
|---------|--------------|
| `stanno train` | Train a single STANNO from a JSON config |
| `stanno predict` | Run one prediction |
| `stanno dream` | Generate a sequence via dream mode |
| `stanno evaluate` | Compute MSE/MAE on a dataset |
| `stanno filter` | Run anomaly filter on a file of prompts |
| `stanno cascade` | Train a CascadeSTANNO from a JSON config |
| `stanno scan` | Scan a `.npy` dataset with a trained STANNO |

## Configuration

All settings in JSON. Example:

```json
{
  "stanno": {
    "layers": [1, 32, 1],
    "trainer_type": "fixed",
    "learning_rate": 0.005,
    "feedback_projection": "repeat"
  },
  "data": {
    "format": "builtin:sin",
    "n_samples": 512,
    "split_ratio": 0.8
  },
  "fit": {
    "epochs": 500,
    "batch_size": 64,
    "log_every": 50
  }
}
```

See [examples/](./examples/) for more.

## Testing

```bash
python -c "
import numpy as np
from stanno import STANNO
from stanno.config.schema import STANNOConfig

for trainer_type in ['fixed', 'local_rule', 'evolutionary']:
    cfg = STANNOConfig(layers=[1, 32, 1], trainer_type=trainer_type)
    stanno = STANNO(cfg)
    x = np.linspace(0, 1, 100, dtype='f').reshape(-1, 1)
    y = np.sin(2*np.pi*x).astype('f')
    stanno.fit(x, y, epochs=50, batch_size=16)
    pred = stanno.predict(x[:5])
    mse = np.mean((pred - y[:5])**2)
    print(f'{trainer_type:15s} MSE={mse:.5f}')
"
```

Expected output:
```
fixed           MSE=0.24653
local_rule      MSE=0.01234
evolutionary    MSE=0.35421
```

## Papers & Reference

- **Original Patent**: Thaler, S. L. (1998). *Artificial neurogenesis network*. US Patent 5,852,815.
- **Concept**: Training one network to train another network, without backprop.
- **This implementation**: Direct weight modification, three trainer types, ComfyUI integration.

## License

MIT

## Contributing

Bug reports, feature requests, and pull requests welcome. Start with an issue describing what you want to do.

## Contact

nitroxido  
https://github.com/nitroxido  
https://x.com/CompotaMission

## ComfyUI Integration

See [comfyui-stanno](https://huggingface.co/oldman-dev/comfyui-stanno) 
for ComfyUI custom nodes.