|
| 1 | +# Postmortem: c_spec DiT Conditioning Regression (v0.5.4–v0.8.0) |
| 2 | + |
| 3 | +## Summary |
| 4 | + |
| 5 | +Models trained on versions v0.5.4 through v0.8.0 produced significantly less |
| 6 | +varied and less interesting sounds, and took longer to train. The root cause |
| 7 | +was the decision at v0.5.4 (PR #30) to pass `c_spec` (spectral conditioning) |
| 8 | +into the DiT during training and inference. Each subsequent version compounded |
| 9 | +this with additional machinery (CFG, eta mapping, Min-SNR weighting, per-sample |
| 10 | +conditioning) that patched symptoms rather than addressing the root cause. |
| 11 | + |
| 12 | +This document records the wrong decisions so they are not repeated. |
| 13 | + |
| 14 | +## Root Cause: v0.5.4 (PR #30) |
| 15 | + |
| 16 | +At v0.5.3, `train_audio_diffusion` calls the DiT as `dit(z_t, t)` — a fully |
| 17 | +unconditional forward pass. From v0.5.4 this became `dit(z_t, t, c_spec=c_spec)`, |
| 18 | +passing the spectral conditioning vector directly into the denoiser. The model |
| 19 | +now learns to denoise toward specific spectral regions rather than freely |
| 20 | +exploring the latent space. At inference, every sample in a bank is anchored |
| 21 | +to the same spectral attractor — diversity comes only from per-sample noise |
| 22 | +seeds, not from genuinely diverse latent trajectories. |
| 23 | + |
| 24 | +## Compounding Decisions |
| 25 | + |
| 26 | +| Version | Change | Why it was wrong | |
| 27 | +|---------|--------|-----------------| |
| 28 | +| v0.5.4 | `c_spec` passed to DiT in training + `target_c_spec` in bank generator | Spectral bias funnel — all bank samples converge on same region | |
| 29 | +| v0.5.7 | CFG two-pass `cfg_forward` + `guidance_scale` | Doubles forward cost per step; amplified conditioning reduces off-manifold exploration | |
| 30 | +| v0.6.0 | `--temperature` remapped to DDIM `eta` | Removes post-hoc latent scaling that provided cheap diversity | |
| 31 | +| v0.7.0 | Min-SNR loss weighting (default) + `chart_loss` fix + `grad_clip` | Training distribution collapses toward lower-variance attractors; longer training time | |
| 32 | +| v0.8.0 | Per-sample `c_spec` seeding | Adds complexity to patch a problem that shouldn't exist | |
| 33 | + |
| 34 | +## What v0.5.3 Had Right |
| 35 | + |
| 36 | +- **Unconditional DiT**: `dit(z_t, t)` — no spectral steering during diffusion. |
| 37 | +- **`noise_std` in `train_audio_decoder`**: the primary lever for sound diversity. |
| 38 | + Gaussian noise injected into the latent `z` before decoding. The docstring |
| 39 | + explicitly calls it "a feature for artistic exploration rather than a bug." |
| 40 | +- **`_apply_temperature`**: post-hoc latent scaling (`z * temperature`) with |
| 41 | + no-op at `temperature == 1.0`. Diversity at default settings comes entirely |
| 42 | + from `seed + i`. |
| 43 | +- **`c_spec` for decoder gate only**: derived post-hoc from `z.mean(dim=2)`, |
| 44 | + used by the decoder, never by the DiT. |
| 45 | +- **`chart_loss` no-op**: `A_hat = A.detach()` makes `chart_loss` always zero. |
| 46 | + This was a silent no-op due to the detach, not because of a `0.0` default. |
| 47 | + The v0.7.0 "fix" activated it, contributing to the training collapse. |
| 48 | + |
| 49 | +## Principles to Prevent Recurrence |
| 50 | + |
| 51 | +1. **Noise-seed diversity is the primary mechanism.** The diffusion model's |
| 52 | + job is to explore the latent manifold freely. Spectral steering constrains |
| 53 | + it to a subspace. If diversity is lacking, increase `noise_std` or vary |
| 54 | + seeds — do not add conditioning to the DiT. |
| 55 | + |
| 56 | +2. **`c_spec` is a decoder gate, not a DiT conditioner.** The spectral chart |
| 57 | + descriptor shapes how the decoder turns a latent into a waveform. It must |
| 58 | + never steer the diffusion process itself. |
| 59 | + |
| 60 | +3. **Preserve the `chart_loss` no-op deliberately.** The `A_hat = A.detach()` |
| 61 | + pattern in `trainer.py` makes `chart_loss` zero. This is intentional for |
| 62 | + Phase 1 — the constraint is too strong and collapses training toward |
| 63 | + lower-variance outputs. Do not "fix" this without understanding why it was |
| 64 | + a no-op in the first place. |
| 65 | + |
| 66 | +4. **`noise_std` / `NOISE_INJECT` is the diversity lever.** It corresponds to |
| 67 | + `NOISE_INJECT` in notebook 07. It must not be removed or disabled. |
| 68 | + |
| 69 | +5. **Avoid compounding fixes.** When a change reduces diversity, do not add |
| 70 | + more machinery to compensate. Revert the change. Each layer of |
| 71 | + compensation makes the system harder to understand and debug. |
| 72 | + |
| 73 | +## Resolution |
| 74 | + |
| 75 | +- `main` re-pointed to `simplify/v053-core` (v0.5.3 + neutral fixes only) |
| 76 | +- Post-v0.5.3 work preserved on `stash/post-053-complexity` branch |
| 77 | +- Neutral fixes cherry-picked back: weight_norm migration, STFT window |
| 78 | + caching, safetensors export + config.json, CI workflow |
| 79 | +- Abandoned tags v0.5.4–v0.8.0 deleted |
| 80 | +- See [issue #42](https://github.com/tuned-org-uk/latent-sound-diffusion/issues/42) |
| 81 | + for the full investigation |
0 commit comments