Independent developer project · not affiliated with, endorsed by, or sponsored by BrainChip Holdings Ltd. Akida and AKD1500 are their trademarks.

Demo 04 — Train the M3 detector (smoke run)

Close the training chain once — train 60 steps on synthetic data, quantise, convert, map — before investing a single photo.

EXPERIMENTALEvent 2026-09-07Published 2026-09-27SE Inspect build 1.9.37

Needs: the training environment (guide chapter 06, step 4). A card only for the last step.

Environment: ~/m3-train-env/bin/python — not the runtime venv. TensorFlow and cnn2snn exist only here.

1. Which path is open

~/m3-train-env/bin/python tools/check_training_env.py

Expected: numpy 2.1.3, tensorflow 2.19.0, cnn2snn 2.19.3, akida 2.19.3 import; the script names the open path (train here, or train on a PC and map here).

2. The smoke run

~/m3-train-env/bin/python tools/train_smoke.py --steps 60 --batch 4

Expected: (1) synthetic scenes with known labels are generated; (2) the Keras output shape is compared with the verified Akida variant — mismatch aborts; (3) 60 training steps with a falling loss; (4) quantisation and conversion to .fbz; (5) with a card present, mapping: one sequence, one pass; one image is pushed through.

Without a card:

~/m3-train-env/bin/python tools/train_smoke.py --steps 60 --batch 4 --skip-map

A falling loss over 60 steps proves that gradients arrive; it says nothing about detection quality. The step is still first: if the chain is broken, every hour of annotation would be wasted.

3. Why the data generator matters more than the epochs

With 8-px cells the 32 × 32 grid has 3072 slots per image and typically ~40 are occupied — a 1:500 imbalance, which is why objectness is focally weighted. And two overlapping objects whose centres are 15 px apart land in different cells, so slots 2 and 3 would never be trained. The generator therefore places near-coincident centres on purpose. Check it:

python3 -c "from vorsa import synth_objects as s; b,l=s.make_batch(40, cell_px=8.0); print(s.statistik(l, grid=32, image_px=256))"

Output we got:

{'bilder': 40, 'objekte': 292, 'objekte_je_bild': 7.3, 'anteil_frei': 0.435,
 'anteil_teilverdeckt': 0.336, 'anteil_stark_verdeckt': 0.229, 'anteil_rund': 0.226,
 'felder_1_objekt': 213, 'felder_2_objekte': 27, 'felder_3_objekte': 7, 'felder_ueberfuellt': 1}

felder_2_objekte and felder_3_objekte above zero is the point; if they were zero, M3 would never learn M3.

4. Real training

From the UI: Set up → Objects & learning → Train → M3 training (steps slider, default 300). The server runs the training environment as a subprocess, streams phase/step/messages into the dialog, writes vorsa_m3.fbz, reserves card 3 for it and loads it at the next start. In operation the detector proposes candidates; the ensemble names them ("find and confirm").

Proves

The chain Keras → cnn2snn → .fbz → card is closed and shape-checked.

Does not prove

Anything about accuracy. Evidence → Limitations, point 1.

Failure modes

ModuleNotFoundError: tensorflow → you used the runtime venv. Shape-mismatch abort → variant in config.py and model_m3.py differ; do not "fix" it by skipping the check. Mapping fails while training succeeded → the card is held by the service (systemctl stop vorsa) or Demo 00.

Time: 10 minutes for the smoke run; hours for real training on the Pi.