This fork documents a set of MNIST experiments around the class-conditional
Kuramoto image generator in un0. The goal was to understand whether sample
collapse came from the oscillator dynamics themselves, the learned coupling
matrix, or the training setup.
The experiments use RGB-resized MNIST at 32x32, a 1024-oscillator generator, batch size 512, pixel drift loss only, and FID evaluation once per epoch. The training script is:
uv run python un0/train_mnist.py --help| Run | Generator core | Trainable dynamics | Epochs logged | Best FID | Last FID |
|---|---|---|---|---|---|
| Learned Kuramoto | 1024 learned oscillators + learned class drive | Yes | 75 | 50.16 @ epoch 50 | 67.31 |
| Fixed reservoir | Same oscillator system, but dynamics frozen at init | No | 70 | 84.01 @ epoch 66 | 99.26 |
| MLP baseline | Noise + learned class embedding into a 3-layer MLP | N/A | 63 | 49.10 @ epoch 54 | 85.84 |
The MLP baseline is parameter-matched to the learned Kuramoto dynamics. With the default MNIST settings it uses:
noise_dim = 1024
class_embedding_dim = 8
hidden_dim = 451
MLP + embedding params = 1,132,663
Kuramoto dynamics params = 1,131,592
The learned Kuramoto and parameter-matched MLP both reached similar best FID in this sweep. The fixed random reservoir improved but stayed worse, suggesting that learning the representation before the decoder is important. The MLP curve also shows that the improvement is not unique to explicit oscillator dynamics.
Each grid shows 10 samples per digit class from the fixed evaluation labels.
| Learned Kuramoto, epoch 75 | Fixed reservoir, epoch 70 | MLP baseline, epoch 63 |
|---|---|---|
![]() |
![]() |
![]() |
Learned Kuramoto:
uv run python un0/train_mnist.py \
--checkpoint-dir checkpoints/mnist_pixel_bs512_epoch100_1024 \
--epochs 100 \
--max-train-seconds 0 \
--batch-size 512 \
--num-workers 0 \
--n-oscillators 1024 \
--n-conditional-oscillators 8 \
--num-steps 1 \
--queue-size 0 \
--dino-weight 0 \
--pixel-weight 1.0 \
--foreground-weight 0 \
--scheduler-total-steps 30000 \
--scheduler-warmup-steps 500 \
--fid-every-seconds 0 \
--fid-every-epochs 1 \
--fid-num-samples 2000 \
--fid-real-samples 10000Fixed reservoir:
uv run python un0/train_mnist.py \
--checkpoint-dir checkpoints/mnist_pixel_bs512_reservoir_fixeddyn_1024 \
--epochs 100 \
--max-train-seconds 0 \
--batch-size 512 \
--num-workers 0 \
--n-oscillators 1024 \
--n-conditional-oscillators 8 \
--num-steps 1 \
--queue-size 0 \
--dino-weight 0 \
--pixel-weight 1.0 \
--foreground-weight 0 \
--freeze-dynamics \
--scheduler-total-steps 30000 \
--scheduler-warmup-steps 500 \
--fid-every-seconds 0 \
--fid-every-epochs 1 \
--fid-num-samples 2000 \
--fid-real-samples 10000 \
--fid-compare-history checkpoints/mnist_pixel_bs512_epoch100_1024/fid_history.csvMLP baseline:
uv run python un0/train_mnist.py \
--checkpoint-dir checkpoints/mnist_pixel_bs512_mlp_osc_baseline_1024 \
--epochs 100 \
--max-train-seconds 0 \
--batch-size 512 \
--num-workers 0 \
--generator mlp \
--n-oscillators 1024 \
--n-conditional-oscillators 8 \
--num-steps 1 \
--queue-size 0 \
--dino-weight 0 \
--pixel-weight 1.0 \
--foreground-weight 0 \
--scheduler-total-steps 30000 \
--scheduler-warmup-steps 500 \
--fid-every-seconds 0 \
--fid-every-epochs 1 \
--fid-num-samples 2000 \
--fid-real-samples 10000 \
--fid-compare-history checkpoints/mnist_pixel_bs512_epoch100_1024/fid_history.csv \
--fid-compare-history checkpoints/mnist_pixel_bs512_reservoir_fixeddyn_1024/fid_history.csvcheckpoints/is intentionally ignored by Git. Only the selected plot and sample grids inassets/mnist_experiments/are committed.- FID is computed with clean-FID custom statistics generated from 10,000 MNIST training images resized to RGB 32x32.
- The run logs and media hooks use
sh-logandsh-mediawhen those tools are available, but the training script does not require them.



