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.
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.
