hotstep-v1: soft-distilled + calibrated pooled-v4
Browse files- README.md +155 -0
- calibration.json +1 -0
- evaluation/train_log.jsonl +0 -0
- mup_base_shapes.bsh +517 -0
- rvq_encoder.safetensors +3 -0
- rvq_encoder_config.json +34 -0
- training/rvq_distill_train.py +471 -0
README.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: other
|
| 3 |
+
license_name: minimax-music3-terms
|
| 4 |
+
license_link: https://huggingface.co/MiniMaxAI/MiniMax-Music3
|
| 5 |
+
base_model: Mothersuperior/open-rvq-encoder-minimax-music3-169m-pooled-v4
|
| 6 |
+
datasets:
|
| 7 |
+
- Mothersuperior/minimax-music3-rvq-distill-corpus-8k
|
| 8 |
+
tags:
|
| 9 |
+
- minimax-music3
|
| 10 |
+
- rvq
|
| 11 |
+
- audio-encoder
|
| 12 |
+
- reverse-distillation
|
| 13 |
+
- knowledge-distillation
|
| 14 |
+
---
|
| 15 |
+
|
| 16 |
+
# open-rvq-encoder-minimax-music3-169m — soft-distilled + calibrated (hotstep-v1)
|
| 17 |
+
|
| 18 |
+
[Mothersuperior/open-rvq-encoder-minimax-music3-169m-pooled-v4](https://huggingface.co/Mothersuperior/open-rvq-encoder-minimax-music3-169m-pooled-v4)
|
| 19 |
+
further-trained with a **pure soft-distillation objective** (soft cross-entropy against the teacher
|
| 20 |
+
top-50 distributions shipped in the 8k corpus) plus **learnable per-head logit temperatures**, then
|
| 21 |
+
selected on a **real-audio gate** that no other release in this lineage measures. Architecture,
|
| 22 |
+
config, and file format are identical to the base — this is a drop-in replacement.
|
| 23 |
+
|
| 24 |
+
Three things in this release are useful even if you never load the weights:
|
| 25 |
+
|
| 26 |
+
## 1. Corpus consumption fixes (apply these before training on the 8k corpus)
|
| 27 |
+
|
| 28 |
+
**uint16 wrap in `probs.npz` semantic ids.** The semantic head's `idx` values were stored as raw
|
| 29 |
+
LM token ids (`code + 151675`) in a uint16, which silently wraps them mod 65536. They arrive as
|
| 30 |
+
`code + 20603`. Subtract 20603 and mask anything outside `[0, 16384)` (the EOS token wraps to −5).
|
| 31 |
+
Verified: after unwrapping, the sampled ground-truth code lands inside the teacher top-50 for
|
| 32 |
+
~100% of frames; consumed naively, the ids are garbage and soft-distillation losses are meaningless.
|
| 33 |
+
|
| 34 |
+
**Stitched-timeline latent alignment.** The corpus audio is stitched from 200-frame DiT windows at
|
| 35 |
+
a 100-frame / **345-latent** hop (documented in the corpus card), while the naive uniform mapping
|
| 36 |
+
(`latent = frame * 441 // 128` ≈ 344.53/100 frames) drifts ~1 latent per 800 frames. Measured
|
| 37 |
+
effect: semantic top-1 against ground-truth codes decays from 0.41 at track start to 0.06 by frame
|
| 38 |
+
2400 under the uniform rule. The correct frame→latent mapping for this corpus is:
|
| 39 |
+
|
| 40 |
+
```python
|
| 41 |
+
def stitched_lat(i): # frame index -> DAV latent index
|
| 42 |
+
k = 0 if i < 125 else (i - 25) // 100 # chunk ownership (from local frame 25)
|
| 43 |
+
return 345 * k + ((i - 100 * k) * 441) // 128 + 1
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
The `+1` is a constant global offset, verified empirically as best at every probed position on
|
| 47 |
+
every probed record (top-1 flat at 0.4–0.6 across track positions once applied). The corpus's own
|
| 48 |
+
DAV latents are exact — recomputing them from the shipped FLAC through the official `dav.pth`
|
| 49 |
+
reproduces them at cosine 1.0 — the drift lives in the audio-vs-codes timeline of chunked
|
| 50 |
+
rendering. **Real audio has no such drift**: the uniform mapping remains correct at inference.
|
| 51 |
+
|
| 52 |
+
## 2. Calibrated logits (folded)
|
| 53 |
+
|
| 54 |
+
The released v4/pooled-v4 readout logits are muP-sharp and uncalibrated: harmless for
|
| 55 |
+
argmax/top-K use (what the reference adapter and the ComfyUI/diffusers nodes do), but
|
| 56 |
+
cross-entropy and softmax probabilities computed from them are meaningless (semantic soft-CE ~59
|
| 57 |
+
nats at temperature 1; per-frame logit std ~45). This release trained **learnable per-head
|
| 58 |
+
temperatures** jointly with the model (initialised from a line search: ~16 semantic, ~2 acoustic)
|
| 59 |
+
and **folded them into the head weights** at export. Argmax and top-K ordering are unchanged;
|
| 60 |
+
`softmax(logits)` and CE are now meaningful out of the box. The final folded values are in
|
| 61 |
+
`calibration.json`.
|
| 62 |
+
|
| 63 |
+
## 3. The weights
|
| 64 |
+
|
| 65 |
+
Training: pure soft-CE vs teacher top-50 (all 8 heads; depth decoder teacher-forced), 20,000 steps
|
| 66 |
+
at effective batch 64 (16 × 4 grad-accum), lr 1e-4 cosine with 200-step warmup, bf16, single
|
| 67 |
+
RTX 5090, on the drift-corrected + unwrapped 8k corpus (records < 8 s filtered; 7,726 train / 394
|
| 68 |
+
holdout by salted-hash split). Trainer included in [`training/`](training/).
|
| 69 |
+
|
| 70 |
+
### In-domain holdout (8k corpus, drift-corrected loader, calibrated CE)
|
| 71 |
+
|
| 72 |
+
| holdout metric | pooled-v4 (start) | this release (step 18500) | change |
|
| 73 |
+
|---|---|---|---|
|
| 74 |
+
| semantic soft-CE (vs teacher) | 3.299 | 3.039 | −7.9% |
|
| 75 |
+
| acoustic soft-CE | 4.618 | 4.413 | −4.4% |
|
| 76 |
+
| semantic hard-CE | 2.336 | 2.154 | −7.8% |
|
| 77 |
+
| acoustic hard-CE | 3.925 | 3.763 | −4.1% |
|
| 78 |
+
| semantic top-1 | 41.9% | 42.2% | +0.3 pt |
|
| 79 |
+
| acoustic top-1 | 16.8% | 17.6% | +0.8 pt |
|
| 80 |
+
|
| 81 |
+
In-domain argmax barely moves — the interesting result is below.
|
| 82 |
+
|
| 83 |
+
### Real-audio gate (13 commercial studio recordings, frozen official LM/depth decoder base-CE)
|
| 84 |
+
|
| 85 |
+
The metric nobody else in the lineage reports: export codes from real recordings, measure the
|
| 86 |
+
frozen official model's cross-entropy on them. Lower = the encoder's codes look more like codes
|
| 87 |
+
the official model would emit. All prior rows reproduced on the same harness:
|
| 88 |
+
|
| 89 |
+
| encoder | params | sem CE | sem t1 | ac CE | ac t1 |
|
| 90 |
+
|---|---|---|---|---|---|
|
| 91 |
+
| Serveurperso community 41M | 41M | 8.447 | 5.8% | 6.763 | 2.3% |
|
| 92 |
+
| Mothersuperior pooled-v3 | 41M | 7.261 | 8.5% | 6.347 | 4.5% |
|
| 93 |
+
| SimpleTuner v4 | 169M | 7.158 | 8.9% | 5.638 | 5.8% |
|
| 94 |
+
| Mothersuperior pooled-v4 | 169M | 6.838 | 10.9% | 5.373 | 7.5% |
|
| 95 |
+
| **this release** | **169M** | **5.866** | **16.6%** | **4.725** | **10.9%** |
|
| 96 |
+
|
| 97 |
+
The largest single-step improvement in the lineage on this metric — and it happens at the
|
| 98 |
+
argmax level (temperature folding cannot cause it), despite near-flat in-domain argmax
|
| 99 |
+
accuracy. Soft distillation appears to act as regularisation against synthetic-audio overfit,
|
| 100 |
+
transferring to real recordings. Caveat: semantic unique-code usage on the real-audio set drops
|
| 101 |
+
~14% vs the base (6,490 vs 7,515 over 64k frames; acoustic unchanged) — mild concentration,
|
| 102 |
+
far from collapse, but stated for transparency.
|
| 103 |
+
|
| 104 |
+
Ear-verified on frame-matched replay renders of five real commercial recordings across four
|
| 105 |
+
genres (indie rock, pop, alt-metal, electronic): an audible improvement over pooled-v4 on all
|
| 106 |
+
of them, though subtler to the ear than the pooled-v4 → v4 step was — consistent with
|
| 107 |
+
diminishing returns from a synthetic-only corpus at this size. Best in the lineage by both
|
| 108 |
+
metric and ear; the remaining real-audio fidelity gap likely needs real-audio objectives
|
| 109 |
+
(consistency training, LM-prior regularisation, reconstruction through the frozen generator)
|
| 110 |
+
rather than more synthetic epochs.
|
| 111 |
+
|
| 112 |
+
## Usage
|
| 113 |
+
|
| 114 |
+
Identical to the base model. Load with SimpleTuner's
|
| 115 |
+
[`minimax_music3_reference_adapter.py`](https://huggingface.co/SimpleTuner/open-rvq-encoder-minimax-music3/blob/main/minimax_music3_reference_adapter.py):
|
| 116 |
+
|
| 117 |
+
```python
|
| 118 |
+
from minimax_music3_reference_adapter import MiniMaxMusic3ReferenceAdapter
|
| 119 |
+
adapter = MiniMaxMusic3ReferenceAdapter.from_files(
|
| 120 |
+
"rvq_encoder.safetensors", "rvq_encoder_config.json", "dav.pth")
|
| 121 |
+
codes = adapter.predict_codes(waveform, sample_rate) # [frames, 8]
|
| 122 |
+
```
|
| 123 |
+
|
| 124 |
+
`dav.pth` is the official DAV encoder from
|
| 125 |
+
[MiniMaxAI/MiniMax-Music3](https://huggingface.co/MiniMaxAI/MiniMax-Music3).
|
| 126 |
+
Strict `load_state_dict` — the checkpoint keys match the base exactly.
|
| 127 |
+
|
| 128 |
+
## Further training
|
| 129 |
+
|
| 130 |
+
[`training/rvq_distill_train.py`](training/rvq_distill_train.py) is the self-contained trainer
|
| 131 |
+
used for this release: 8k-corpus zip layout, both consumption fixes above built in, warm start
|
| 132 |
+
from any encoder in this lineage, pure-soft or mixed soft/hard loss, learnable temperatures with
|
| 133 |
+
fold-at-save, holdout eval + best-checkpoint selection. Depends only on torch/numpy/safetensors
|
| 134 |
+
plus the reference adapter module for the model classes.
|
| 135 |
+
|
| 136 |
+
## Credits
|
| 137 |
+
|
| 138 |
+
This encoder exists because of a chain of community work, each stage building on the last:
|
| 139 |
+
|
| 140 |
+
- **[MiniMax](https://huggingface.co/MiniMaxAI/MiniMax-Music3)** — MiniMax Music 3 and the DAV
|
| 141 |
+
encoder. All weights and training data derive from it; use is subject to its terms.
|
| 142 |
+
- **[Serveurperso](https://github.com/ServeurpersoCom)** — the original community proof: a 41M
|
| 143 |
+
single-GPU encoder demonstrating that exact token agreement was not required, and the first
|
| 144 |
+
independent derivation of the stitched-timeline alignment contract.
|
| 145 |
+
- **[bghira / SimpleTuner](https://huggingface.co/SimpleTuner/open-rvq-encoder-minimax-music3)** —
|
| 146 |
+
the encoder architecture (v1–v4, causal depth decoder), the reference adapter, the trainer, and
|
| 147 |
+
the [reverse-distillation trace corpus](https://huggingface.co/datasets/bghira/minimax-music3-rvq-reverse-distillation).
|
| 148 |
+
- **[Mothersuperior](https://huggingface.co/Mothersuperior)** — the
|
| 149 |
+
[8k distillation corpus](https://huggingface.co/datasets/Mothersuperior/minimax-music3-rvq-distill-corpus-8k)
|
| 150 |
+
with teacher top-50 distributions (which make this release's objective possible), and the
|
| 151 |
+
pooled-v3/pooled-v4 fine-tunes this release warm-starts from.
|
| 152 |
+
- **[HOT-Step CPP](https://github.com/scragnog/HOT-Step-CPP)** — this release: the soft-distillation
|
| 153 |
+
training run, the corpus consumption fixes, logit calibration, and the real-audio gate.
|
| 154 |
+
|
| 155 |
+
Use is subject to the MiniMax Music 3 model terms and the source datasets' terms.
|
calibration.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"folded_taus": [8.8834, 1.3853, 1.4031, 1.3873, 1.4341, 1.4484, 1.4719, 1.4799]}
|
evaluation/train_log.jsonl
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
mup_base_shapes.bsh
ADDED
|
@@ -0,0 +1,517 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This is a base shape file encoded in yaml
|
| 2 |
+
# - `null` indicates a dimension is "finite", i.e. a non-"width" dimension
|
| 3 |
+
# - a number indicates the base dimension of an "infinite" dimension, i.e. some notion of "width"
|
| 4 |
+
blocks.0.conv1.bias:
|
| 5 |
+
- 128
|
| 6 |
+
blocks.0.conv1.weight:
|
| 7 |
+
- 128
|
| 8 |
+
- 128
|
| 9 |
+
- null
|
| 10 |
+
blocks.0.conv2.bias:
|
| 11 |
+
- 128
|
| 12 |
+
blocks.0.conv2.weight:
|
| 13 |
+
- 128
|
| 14 |
+
- 128
|
| 15 |
+
- null
|
| 16 |
+
blocks.0.norm.bias:
|
| 17 |
+
- 128
|
| 18 |
+
blocks.0.norm.weight:
|
| 19 |
+
- 128
|
| 20 |
+
blocks.1.conv1.bias:
|
| 21 |
+
- 128
|
| 22 |
+
blocks.1.conv1.weight:
|
| 23 |
+
- 128
|
| 24 |
+
- 128
|
| 25 |
+
- null
|
| 26 |
+
blocks.1.conv2.bias:
|
| 27 |
+
- 128
|
| 28 |
+
blocks.1.conv2.weight:
|
| 29 |
+
- 128
|
| 30 |
+
- 128
|
| 31 |
+
- null
|
| 32 |
+
blocks.1.norm.bias:
|
| 33 |
+
- 128
|
| 34 |
+
blocks.1.norm.weight:
|
| 35 |
+
- 128
|
| 36 |
+
blocks.2.conv1.bias:
|
| 37 |
+
- 128
|
| 38 |
+
blocks.2.conv1.weight:
|
| 39 |
+
- 128
|
| 40 |
+
- 128
|
| 41 |
+
- null
|
| 42 |
+
blocks.2.conv2.bias:
|
| 43 |
+
- 128
|
| 44 |
+
blocks.2.conv2.weight:
|
| 45 |
+
- 128
|
| 46 |
+
- 128
|
| 47 |
+
- null
|
| 48 |
+
blocks.2.norm.bias:
|
| 49 |
+
- 128
|
| 50 |
+
blocks.2.norm.weight:
|
| 51 |
+
- 128
|
| 52 |
+
conv_in.bias:
|
| 53 |
+
- 128
|
| 54 |
+
conv_in.weight:
|
| 55 |
+
- 128
|
| 56 |
+
- null
|
| 57 |
+
- null
|
| 58 |
+
depth_decoder.context_projection.weight:
|
| 59 |
+
- null
|
| 60 |
+
- 128
|
| 61 |
+
depth_decoder.heads.0.bias:
|
| 62 |
+
- null
|
| 63 |
+
depth_decoder.heads.0.weight:
|
| 64 |
+
- null
|
| 65 |
+
- null
|
| 66 |
+
depth_decoder.heads.1.bias:
|
| 67 |
+
- null
|
| 68 |
+
depth_decoder.heads.1.weight:
|
| 69 |
+
- null
|
| 70 |
+
- null
|
| 71 |
+
depth_decoder.heads.2.bias:
|
| 72 |
+
- null
|
| 73 |
+
depth_decoder.heads.2.weight:
|
| 74 |
+
- null
|
| 75 |
+
- null
|
| 76 |
+
depth_decoder.heads.3.bias:
|
| 77 |
+
- null
|
| 78 |
+
depth_decoder.heads.3.weight:
|
| 79 |
+
- null
|
| 80 |
+
- null
|
| 81 |
+
depth_decoder.heads.4.bias:
|
| 82 |
+
- null
|
| 83 |
+
depth_decoder.heads.4.weight:
|
| 84 |
+
- null
|
| 85 |
+
- null
|
| 86 |
+
depth_decoder.heads.5.bias:
|
| 87 |
+
- null
|
| 88 |
+
depth_decoder.heads.5.weight:
|
| 89 |
+
- null
|
| 90 |
+
- null
|
| 91 |
+
depth_decoder.heads.6.bias:
|
| 92 |
+
- null
|
| 93 |
+
depth_decoder.heads.6.weight:
|
| 94 |
+
- null
|
| 95 |
+
- null
|
| 96 |
+
depth_decoder.layers.0.k_proj.bias:
|
| 97 |
+
- null
|
| 98 |
+
depth_decoder.layers.0.k_proj.weight:
|
| 99 |
+
- null
|
| 100 |
+
- null
|
| 101 |
+
depth_decoder.layers.0.linear1.bias:
|
| 102 |
+
- null
|
| 103 |
+
depth_decoder.layers.0.linear1.weight:
|
| 104 |
+
- null
|
| 105 |
+
- null
|
| 106 |
+
depth_decoder.layers.0.linear2.bias:
|
| 107 |
+
- null
|
| 108 |
+
depth_decoder.layers.0.linear2.weight:
|
| 109 |
+
- null
|
| 110 |
+
- null
|
| 111 |
+
depth_decoder.layers.0.norm1.bias:
|
| 112 |
+
- null
|
| 113 |
+
depth_decoder.layers.0.norm1.weight:
|
| 114 |
+
- null
|
| 115 |
+
depth_decoder.layers.0.norm2.bias:
|
| 116 |
+
- null
|
| 117 |
+
depth_decoder.layers.0.norm2.weight:
|
| 118 |
+
- null
|
| 119 |
+
depth_decoder.layers.0.out_proj.bias:
|
| 120 |
+
- null
|
| 121 |
+
depth_decoder.layers.0.out_proj.weight:
|
| 122 |
+
- null
|
| 123 |
+
- null
|
| 124 |
+
depth_decoder.layers.0.q_proj.bias:
|
| 125 |
+
- null
|
| 126 |
+
depth_decoder.layers.0.q_proj.weight:
|
| 127 |
+
- null
|
| 128 |
+
- null
|
| 129 |
+
depth_decoder.layers.0.v_proj.bias:
|
| 130 |
+
- null
|
| 131 |
+
depth_decoder.layers.0.v_proj.weight:
|
| 132 |
+
- null
|
| 133 |
+
- null
|
| 134 |
+
depth_decoder.layers.1.k_proj.bias:
|
| 135 |
+
- null
|
| 136 |
+
depth_decoder.layers.1.k_proj.weight:
|
| 137 |
+
- null
|
| 138 |
+
- null
|
| 139 |
+
depth_decoder.layers.1.linear1.bias:
|
| 140 |
+
- null
|
| 141 |
+
depth_decoder.layers.1.linear1.weight:
|
| 142 |
+
- null
|
| 143 |
+
- null
|
| 144 |
+
depth_decoder.layers.1.linear2.bias:
|
| 145 |
+
- null
|
| 146 |
+
depth_decoder.layers.1.linear2.weight:
|
| 147 |
+
- null
|
| 148 |
+
- null
|
| 149 |
+
depth_decoder.layers.1.norm1.bias:
|
| 150 |
+
- null
|
| 151 |
+
depth_decoder.layers.1.norm1.weight:
|
| 152 |
+
- null
|
| 153 |
+
depth_decoder.layers.1.norm2.bias:
|
| 154 |
+
- null
|
| 155 |
+
depth_decoder.layers.1.norm2.weight:
|
| 156 |
+
- null
|
| 157 |
+
depth_decoder.layers.1.out_proj.bias:
|
| 158 |
+
- null
|
| 159 |
+
depth_decoder.layers.1.out_proj.weight:
|
| 160 |
+
- null
|
| 161 |
+
- null
|
| 162 |
+
depth_decoder.layers.1.q_proj.bias:
|
| 163 |
+
- null
|
| 164 |
+
depth_decoder.layers.1.q_proj.weight:
|
| 165 |
+
- null
|
| 166 |
+
- null
|
| 167 |
+
depth_decoder.layers.1.v_proj.bias:
|
| 168 |
+
- null
|
| 169 |
+
depth_decoder.layers.1.v_proj.weight:
|
| 170 |
+
- null
|
| 171 |
+
- null
|
| 172 |
+
depth_decoder.norm.bias:
|
| 173 |
+
- null
|
| 174 |
+
depth_decoder.norm.weight:
|
| 175 |
+
- null
|
| 176 |
+
depth_decoder.position:
|
| 177 |
+
- null
|
| 178 |
+
- null
|
| 179 |
+
- null
|
| 180 |
+
depth_decoder.prior_embeddings.0.weight:
|
| 181 |
+
- null
|
| 182 |
+
- null
|
| 183 |
+
depth_decoder.prior_embeddings.1.weight:
|
| 184 |
+
- null
|
| 185 |
+
- null
|
| 186 |
+
depth_decoder.prior_embeddings.2.weight:
|
| 187 |
+
- null
|
| 188 |
+
- null
|
| 189 |
+
depth_decoder.prior_embeddings.3.weight:
|
| 190 |
+
- null
|
| 191 |
+
- null
|
| 192 |
+
depth_decoder.prior_embeddings.4.weight:
|
| 193 |
+
- null
|
| 194 |
+
- null
|
| 195 |
+
depth_decoder.prior_embeddings.5.weight:
|
| 196 |
+
- null
|
| 197 |
+
- null
|
| 198 |
+
depth_decoder.prior_embeddings.6.weight:
|
| 199 |
+
- null
|
| 200 |
+
- null
|
| 201 |
+
heads.0.bias:
|
| 202 |
+
- null
|
| 203 |
+
heads.0.weight:
|
| 204 |
+
- null
|
| 205 |
+
- 128
|
| 206 |
+
norm_out.bias:
|
| 207 |
+
- 128
|
| 208 |
+
norm_out.weight:
|
| 209 |
+
- 128
|
| 210 |
+
position:
|
| 211 |
+
- null
|
| 212 |
+
- null
|
| 213 |
+
- 128
|
| 214 |
+
transformer.0.k_proj.bias:
|
| 215 |
+
- 128
|
| 216 |
+
transformer.0.k_proj.weight:
|
| 217 |
+
- 128
|
| 218 |
+
- 128
|
| 219 |
+
transformer.0.linear1.bias:
|
| 220 |
+
- 512
|
| 221 |
+
transformer.0.linear1.weight:
|
| 222 |
+
- 512
|
| 223 |
+
- 128
|
| 224 |
+
transformer.0.linear2.bias:
|
| 225 |
+
- 128
|
| 226 |
+
transformer.0.linear2.weight:
|
| 227 |
+
- 128
|
| 228 |
+
- 512
|
| 229 |
+
transformer.0.norm1.bias:
|
| 230 |
+
- 128
|
| 231 |
+
transformer.0.norm1.weight:
|
| 232 |
+
- 128
|
| 233 |
+
transformer.0.norm2.bias:
|
| 234 |
+
- 128
|
| 235 |
+
transformer.0.norm2.weight:
|
| 236 |
+
- 128
|
| 237 |
+
transformer.0.out_proj.bias:
|
| 238 |
+
- 128
|
| 239 |
+
transformer.0.out_proj.weight:
|
| 240 |
+
- 128
|
| 241 |
+
- 128
|
| 242 |
+
transformer.0.q_proj.bias:
|
| 243 |
+
- 128
|
| 244 |
+
transformer.0.q_proj.weight:
|
| 245 |
+
- 128
|
| 246 |
+
- 128
|
| 247 |
+
transformer.0.v_proj.bias:
|
| 248 |
+
- 128
|
| 249 |
+
transformer.0.v_proj.weight:
|
| 250 |
+
- 128
|
| 251 |
+
- 128
|
| 252 |
+
transformer.1.k_proj.bias:
|
| 253 |
+
- 128
|
| 254 |
+
transformer.1.k_proj.weight:
|
| 255 |
+
- 128
|
| 256 |
+
- 128
|
| 257 |
+
transformer.1.linear1.bias:
|
| 258 |
+
- 512
|
| 259 |
+
transformer.1.linear1.weight:
|
| 260 |
+
- 512
|
| 261 |
+
- 128
|
| 262 |
+
transformer.1.linear2.bias:
|
| 263 |
+
- 128
|
| 264 |
+
transformer.1.linear2.weight:
|
| 265 |
+
- 128
|
| 266 |
+
- 512
|
| 267 |
+
transformer.1.norm1.bias:
|
| 268 |
+
- 128
|
| 269 |
+
transformer.1.norm1.weight:
|
| 270 |
+
- 128
|
| 271 |
+
transformer.1.norm2.bias:
|
| 272 |
+
- 128
|
| 273 |
+
transformer.1.norm2.weight:
|
| 274 |
+
- 128
|
| 275 |
+
transformer.1.out_proj.bias:
|
| 276 |
+
- 128
|
| 277 |
+
transformer.1.out_proj.weight:
|
| 278 |
+
- 128
|
| 279 |
+
- 128
|
| 280 |
+
transformer.1.q_proj.bias:
|
| 281 |
+
- 128
|
| 282 |
+
transformer.1.q_proj.weight:
|
| 283 |
+
- 128
|
| 284 |
+
- 128
|
| 285 |
+
transformer.1.v_proj.bias:
|
| 286 |
+
- 128
|
| 287 |
+
transformer.1.v_proj.weight:
|
| 288 |
+
- 128
|
| 289 |
+
- 128
|
| 290 |
+
transformer.2.k_proj.bias:
|
| 291 |
+
- 128
|
| 292 |
+
transformer.2.k_proj.weight:
|
| 293 |
+
- 128
|
| 294 |
+
- 128
|
| 295 |
+
transformer.2.linear1.bias:
|
| 296 |
+
- 512
|
| 297 |
+
transformer.2.linear1.weight:
|
| 298 |
+
- 512
|
| 299 |
+
- 128
|
| 300 |
+
transformer.2.linear2.bias:
|
| 301 |
+
- 128
|
| 302 |
+
transformer.2.linear2.weight:
|
| 303 |
+
- 128
|
| 304 |
+
- 512
|
| 305 |
+
transformer.2.norm1.bias:
|
| 306 |
+
- 128
|
| 307 |
+
transformer.2.norm1.weight:
|
| 308 |
+
- 128
|
| 309 |
+
transformer.2.norm2.bias:
|
| 310 |
+
- 128
|
| 311 |
+
transformer.2.norm2.weight:
|
| 312 |
+
- 128
|
| 313 |
+
transformer.2.out_proj.bias:
|
| 314 |
+
- 128
|
| 315 |
+
transformer.2.out_proj.weight:
|
| 316 |
+
- 128
|
| 317 |
+
- 128
|
| 318 |
+
transformer.2.q_proj.bias:
|
| 319 |
+
- 128
|
| 320 |
+
transformer.2.q_proj.weight:
|
| 321 |
+
- 128
|
| 322 |
+
- 128
|
| 323 |
+
transformer.2.v_proj.bias:
|
| 324 |
+
- 128
|
| 325 |
+
transformer.2.v_proj.weight:
|
| 326 |
+
- 128
|
| 327 |
+
- 128
|
| 328 |
+
transformer.3.k_proj.bias:
|
| 329 |
+
- 128
|
| 330 |
+
transformer.3.k_proj.weight:
|
| 331 |
+
- 128
|
| 332 |
+
- 128
|
| 333 |
+
transformer.3.linear1.bias:
|
| 334 |
+
- 512
|
| 335 |
+
transformer.3.linear1.weight:
|
| 336 |
+
- 512
|
| 337 |
+
- 128
|
| 338 |
+
transformer.3.linear2.bias:
|
| 339 |
+
- 128
|
| 340 |
+
transformer.3.linear2.weight:
|
| 341 |
+
- 128
|
| 342 |
+
- 512
|
| 343 |
+
transformer.3.norm1.bias:
|
| 344 |
+
- 128
|
| 345 |
+
transformer.3.norm1.weight:
|
| 346 |
+
- 128
|
| 347 |
+
transformer.3.norm2.bias:
|
| 348 |
+
- 128
|
| 349 |
+
transformer.3.norm2.weight:
|
| 350 |
+
- 128
|
| 351 |
+
transformer.3.out_proj.bias:
|
| 352 |
+
- 128
|
| 353 |
+
transformer.3.out_proj.weight:
|
| 354 |
+
- 128
|
| 355 |
+
- 128
|
| 356 |
+
transformer.3.q_proj.bias:
|
| 357 |
+
- 128
|
| 358 |
+
transformer.3.q_proj.weight:
|
| 359 |
+
- 128
|
| 360 |
+
- 128
|
| 361 |
+
transformer.3.v_proj.bias:
|
| 362 |
+
- 128
|
| 363 |
+
transformer.3.v_proj.weight:
|
| 364 |
+
- 128
|
| 365 |
+
- 128
|
| 366 |
+
transformer.4.k_proj.bias:
|
| 367 |
+
- 128
|
| 368 |
+
transformer.4.k_proj.weight:
|
| 369 |
+
- 128
|
| 370 |
+
- 128
|
| 371 |
+
transformer.4.linear1.bias:
|
| 372 |
+
- 512
|
| 373 |
+
transformer.4.linear1.weight:
|
| 374 |
+
- 512
|
| 375 |
+
- 128
|
| 376 |
+
transformer.4.linear2.bias:
|
| 377 |
+
- 128
|
| 378 |
+
transformer.4.linear2.weight:
|
| 379 |
+
- 128
|
| 380 |
+
- 512
|
| 381 |
+
transformer.4.norm1.bias:
|
| 382 |
+
- 128
|
| 383 |
+
transformer.4.norm1.weight:
|
| 384 |
+
- 128
|
| 385 |
+
transformer.4.norm2.bias:
|
| 386 |
+
- 128
|
| 387 |
+
transformer.4.norm2.weight:
|
| 388 |
+
- 128
|
| 389 |
+
transformer.4.out_proj.bias:
|
| 390 |
+
- 128
|
| 391 |
+
transformer.4.out_proj.weight:
|
| 392 |
+
- 128
|
| 393 |
+
- 128
|
| 394 |
+
transformer.4.q_proj.bias:
|
| 395 |
+
- 128
|
| 396 |
+
transformer.4.q_proj.weight:
|
| 397 |
+
- 128
|
| 398 |
+
- 128
|
| 399 |
+
transformer.4.v_proj.bias:
|
| 400 |
+
- 128
|
| 401 |
+
transformer.4.v_proj.weight:
|
| 402 |
+
- 128
|
| 403 |
+
- 128
|
| 404 |
+
transformer.5.k_proj.bias:
|
| 405 |
+
- 128
|
| 406 |
+
transformer.5.k_proj.weight:
|
| 407 |
+
- 128
|
| 408 |
+
- 128
|
| 409 |
+
transformer.5.linear1.bias:
|
| 410 |
+
- 512
|
| 411 |
+
transformer.5.linear1.weight:
|
| 412 |
+
- 512
|
| 413 |
+
- 128
|
| 414 |
+
transformer.5.linear2.bias:
|
| 415 |
+
- 128
|
| 416 |
+
transformer.5.linear2.weight:
|
| 417 |
+
- 128
|
| 418 |
+
- 512
|
| 419 |
+
transformer.5.norm1.bias:
|
| 420 |
+
- 128
|
| 421 |
+
transformer.5.norm1.weight:
|
| 422 |
+
- 128
|
| 423 |
+
transformer.5.norm2.bias:
|
| 424 |
+
- 128
|
| 425 |
+
transformer.5.norm2.weight:
|
| 426 |
+
- 128
|
| 427 |
+
transformer.5.out_proj.bias:
|
| 428 |
+
- 128
|
| 429 |
+
transformer.5.out_proj.weight:
|
| 430 |
+
- 128
|
| 431 |
+
- 128
|
| 432 |
+
transformer.5.q_proj.bias:
|
| 433 |
+
- 128
|
| 434 |
+
transformer.5.q_proj.weight:
|
| 435 |
+
- 128
|
| 436 |
+
- 128
|
| 437 |
+
transformer.5.v_proj.bias:
|
| 438 |
+
- 128
|
| 439 |
+
transformer.5.v_proj.weight:
|
| 440 |
+
- 128
|
| 441 |
+
- 128
|
| 442 |
+
transformer.6.k_proj.bias:
|
| 443 |
+
- 128
|
| 444 |
+
transformer.6.k_proj.weight:
|
| 445 |
+
- 128
|
| 446 |
+
- 128
|
| 447 |
+
transformer.6.linear1.bias:
|
| 448 |
+
- 512
|
| 449 |
+
transformer.6.linear1.weight:
|
| 450 |
+
- 512
|
| 451 |
+
- 128
|
| 452 |
+
transformer.6.linear2.bias:
|
| 453 |
+
- 128
|
| 454 |
+
transformer.6.linear2.weight:
|
| 455 |
+
- 128
|
| 456 |
+
- 512
|
| 457 |
+
transformer.6.norm1.bias:
|
| 458 |
+
- 128
|
| 459 |
+
transformer.6.norm1.weight:
|
| 460 |
+
- 128
|
| 461 |
+
transformer.6.norm2.bias:
|
| 462 |
+
- 128
|
| 463 |
+
transformer.6.norm2.weight:
|
| 464 |
+
- 128
|
| 465 |
+
transformer.6.out_proj.bias:
|
| 466 |
+
- 128
|
| 467 |
+
transformer.6.out_proj.weight:
|
| 468 |
+
- 128
|
| 469 |
+
- 128
|
| 470 |
+
transformer.6.q_proj.bias:
|
| 471 |
+
- 128
|
| 472 |
+
transformer.6.q_proj.weight:
|
| 473 |
+
- 128
|
| 474 |
+
- 128
|
| 475 |
+
transformer.6.v_proj.bias:
|
| 476 |
+
- 128
|
| 477 |
+
transformer.6.v_proj.weight:
|
| 478 |
+
- 128
|
| 479 |
+
- 128
|
| 480 |
+
transformer.7.k_proj.bias:
|
| 481 |
+
- 128
|
| 482 |
+
transformer.7.k_proj.weight:
|
| 483 |
+
- 128
|
| 484 |
+
- 128
|
| 485 |
+
transformer.7.linear1.bias:
|
| 486 |
+
- 512
|
| 487 |
+
transformer.7.linear1.weight:
|
| 488 |
+
- 512
|
| 489 |
+
- 128
|
| 490 |
+
transformer.7.linear2.bias:
|
| 491 |
+
- 128
|
| 492 |
+
transformer.7.linear2.weight:
|
| 493 |
+
- 128
|
| 494 |
+
- 512
|
| 495 |
+
transformer.7.norm1.bias:
|
| 496 |
+
- 128
|
| 497 |
+
transformer.7.norm1.weight:
|
| 498 |
+
- 128
|
| 499 |
+
transformer.7.norm2.bias:
|
| 500 |
+
- 128
|
| 501 |
+
transformer.7.norm2.weight:
|
| 502 |
+
- 128
|
| 503 |
+
transformer.7.out_proj.bias:
|
| 504 |
+
- 128
|
| 505 |
+
transformer.7.out_proj.weight:
|
| 506 |
+
- 128
|
| 507 |
+
- 128
|
| 508 |
+
transformer.7.q_proj.bias:
|
| 509 |
+
- 128
|
| 510 |
+
transformer.7.q_proj.weight:
|
| 511 |
+
- 128
|
| 512 |
+
- 128
|
| 513 |
+
transformer.7.v_proj.bias:
|
| 514 |
+
- 128
|
| 515 |
+
transformer.7.v_proj.weight:
|
| 516 |
+
- 128
|
| 517 |
+
- 128
|
rvq_encoder.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b8e36bd36d58f467823f3e1e8c1fc4f9d3311e5527dd49768e8e7cfed68c8e31
|
| 3 |
+
size 676055160
|
rvq_encoder_config.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"codebook_vocab_sizes": [
|
| 3 |
+
16384,
|
| 4 |
+
1024,
|
| 5 |
+
1024,
|
| 6 |
+
1024,
|
| 7 |
+
1024,
|
| 8 |
+
1024,
|
| 9 |
+
1024,
|
| 10 |
+
1024
|
| 11 |
+
],
|
| 12 |
+
"conv_dilations": [
|
| 13 |
+
1,
|
| 14 |
+
3,
|
| 15 |
+
9
|
| 16 |
+
],
|
| 17 |
+
"d_model": 1088,
|
| 18 |
+
"depth_decoder": true,
|
| 19 |
+
"depth_decoder_dim": 512,
|
| 20 |
+
"depth_decoder_dropout": 0.1,
|
| 21 |
+
"depth_decoder_ff_mult": 4,
|
| 22 |
+
"depth_decoder_heads": 8,
|
| 23 |
+
"depth_decoder_layers": 2,
|
| 24 |
+
"dropout": 0.1,
|
| 25 |
+
"ff_mult": 4,
|
| 26 |
+
"latent_channels": 128,
|
| 27 |
+
"max_position_embeddings": 128,
|
| 28 |
+
"mup": true,
|
| 29 |
+
"mup_attention_multiplier": 8.0,
|
| 30 |
+
"mup_output_mult": 1.0,
|
| 31 |
+
"mup_readout_zero_init": true,
|
| 32 |
+
"num_heads": 17,
|
| 33 |
+
"num_layers": 8
|
| 34 |
+
}
|
training/rvq_distill_train.py
ADDED
|
@@ -0,0 +1,471 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
r"""Further-train a SimpleTuner-class RVQ encoder on the published distillation corpora.
|
| 2 |
+
|
| 3 |
+
Warm-starts from a released checkpoint (default: Mothersuperior pooled-v4, 169M)
|
| 4 |
+
and trains with SOFT DISTILLATION against the teacher top-50 distributions the
|
| 5 |
+
corpus ships — the axis the published runs (hard-label CE, 12 epochs) never used.
|
| 6 |
+
Checkpoints regularly; final selection happens OFFLINE on our real-audio gate
|
| 7 |
+
(export_codes_v4.py --encoder-file <ckpt> + 1-step lm_sft_train2 [Base] CE),
|
| 8 |
+
which nobody else measures.
|
| 9 |
+
|
| 10 |
+
Corpus support:
|
| 11 |
+
* Mothersuperior/minimax-music3-rvq-distill-corpus-8k (PRIMARY — ships
|
| 12 |
+
precomputed fp16 DAV latents frame-major [F,128] @ 86.1328 Hz, plus
|
| 13 |
+
top-50 PROBS [T,8,50]). Records: <id>.{flac,codes.npy,probs.npz,vae.npy,json}
|
| 14 |
+
inside data/shard-NNN.zip; manifest.jsonl has align_ok/probs_ok flags.
|
| 15 |
+
Warm-up convention: codes/probs row 0 is un-emitted; frame i <-> row i+1.
|
| 16 |
+
* bghira/minimax-music3-rvq-reverse-distillation: NOT yet wired — ships
|
| 17 |
+
top-50 LOGITS but NO DAV latents, so it needs a ChunkedDAV precache pass
|
| 18 |
+
first (see docs/plans/2026-08-18-encoder-training-plan.md). Respect its
|
| 19 |
+
deterministic dataset_split when added.
|
| 20 |
+
|
| 21 |
+
Key geometry: every 128-frame window spans EXACTLY 441 DAV latents
|
| 22 |
+
(bounds[i] = floor(i*441/128); the +128 difference is always 441), so batches
|
| 23 |
+
need no padding — which matters because GroupNorm(1, C) in the conv stack
|
| 24 |
+
normalises over length and padding would shift its statistics.
|
| 25 |
+
|
| 26 |
+
The depth decoder is TEACHER-FORCED here (ground-truth c0..c6 as priors, causal
|
| 27 |
+
mask, head i read from position i+1) — the published forward() feeds its own
|
| 28 |
+
argmax chain and is inference-only.
|
| 29 |
+
|
| 30 |
+
Usage (single GPU, 5090):
|
| 31 |
+
python rvq_distill_train.py ^
|
| 32 |
+
--corpus M:\HOT-Step-CPP\_corpora\mm3-rvq-distill-8k ^
|
| 33 |
+
--encoder-dir M:\HOT-Step-CPP\_experiments\open-rvq-pooled-v4 ^
|
| 34 |
+
--out M:\HOT-Step-CPP\_experiments\rvq-train\pv4-softdistill-r1 ^
|
| 35 |
+
--steps 20000 --batch 16 --grad-accum 4 --lr 1e-4 --vram-frac 0.85
|
| 36 |
+
"""
|
| 37 |
+
|
| 38 |
+
from __future__ import annotations
|
| 39 |
+
|
| 40 |
+
import argparse
|
| 41 |
+
import hashlib
|
| 42 |
+
import io
|
| 43 |
+
import json
|
| 44 |
+
import math
|
| 45 |
+
import os
|
| 46 |
+
import shutil
|
| 47 |
+
import sys
|
| 48 |
+
import time
|
| 49 |
+
import zipfile
|
| 50 |
+
from pathlib import Path
|
| 51 |
+
|
| 52 |
+
import numpy as np
|
| 53 |
+
import torch
|
| 54 |
+
import torch.nn.functional as F
|
| 55 |
+
from torch.utils.data import DataLoader, Dataset
|
| 56 |
+
|
| 57 |
+
ADAPTER_DIR_DEFAULT = r"M:\HOT-Step-CPP\_experiments\open-rvq-v4"
|
| 58 |
+
WINDOW = 128
|
| 59 |
+
LATENTS_PER_WINDOW = 441 # exact for every start index (441/128 ratio)
|
| 60 |
+
TOPK = 50
|
| 61 |
+
SEM_TOKEN_WRAP = 151_675 % 65_536 # = 20603; see Corpus8k.__getitem__
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def load_adapter_module(adapter_dir: str):
|
| 65 |
+
sys.path.insert(0, adapter_dir)
|
| 66 |
+
import minimax_music3_reference_adapter as ref
|
| 67 |
+
|
| 68 |
+
return ref
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
# ---------------------------------------------------------------- corpus ----
|
| 72 |
+
|
| 73 |
+
def holdout_of(record_id: str, frac: float, salt: str = "hotstep-holdout-v1") -> bool:
|
| 74 |
+
digest = hashlib.sha1(f"{salt}:{record_id}".encode()).digest()
|
| 75 |
+
return (int.from_bytes(digest[:8], "big") / 2**64) < frac
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def stitched_lat(i: int) -> int:
|
| 79 |
+
"""Frame index -> latent index in the corpus's RENDERED audio.
|
| 80 |
+
|
| 81 |
+
The 8k corpus audio is stitched from 200-frame DiT windows at a 100-frame /
|
| 82 |
+
345-LATENT hop (README: 'constant stitched timeline'; non-first-chunk
|
| 83 |
+
ownership from local frame 25). 100 frames nominally map to 344.53 latents,
|
| 84 |
+
so the uniform i*441//128 mapping drifts ~1 latent per 800 and top-1 decays
|
| 85 |
+
to ~0 by frame 2400 — measured, then eliminated by this rule. The +1 is a
|
| 86 |
+
constant global offset, verified best at every position on every record
|
| 87 |
+
probed (t1 0.4-0.6 flat vs position). Real audio has NO such drift — the
|
| 88 |
+
uniform rule stays correct at inference/export time.
|
| 89 |
+
"""
|
| 90 |
+
k = 0 if i < 125 else (i - 25) // 100
|
| 91 |
+
return 345 * k + ((i - 100 * k) * 441) // 128 + 1
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def pool_matrix(bounds: list[int]) -> torch.Tensor:
|
| 95 |
+
"""Local copy of the adapter's build_pool_matrix so DataLoader workers can
|
| 96 |
+
unpickle this Dataset without the runtime-sys.path adapter import."""
|
| 97 |
+
origin = bounds[0]
|
| 98 |
+
local = [b - origin for b in bounds]
|
| 99 |
+
pool = torch.zeros((len(local) - 1, local[-1]), dtype=torch.float32)
|
| 100 |
+
for i, (a, b) in enumerate(zip(local[:-1], local[1:])):
|
| 101 |
+
pool[i, a:b] = 1.0 / (b - a)
|
| 102 |
+
return pool
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
class Corpus8k(Dataset):
|
| 106 |
+
"""Random 128-frame windows from the Mothersuperior 8k distill corpus."""
|
| 107 |
+
|
| 108 |
+
def __init__(self, root: str, records: list[dict], seed: int, fixed_windows: bool = False):
|
| 109 |
+
self.root = Path(root)
|
| 110 |
+
self.records = records
|
| 111 |
+
self.seed = seed
|
| 112 |
+
self.fixed_windows = fixed_windows
|
| 113 |
+
self._zips: dict[str, zipfile.ZipFile] = {}
|
| 114 |
+
|
| 115 |
+
def __len__(self):
|
| 116 |
+
return len(self.records)
|
| 117 |
+
|
| 118 |
+
def _zip(self, shard: str) -> zipfile.ZipFile:
|
| 119 |
+
z = self._zips.get(shard)
|
| 120 |
+
if z is None:
|
| 121 |
+
z = zipfile.ZipFile(self.root / "data" / shard)
|
| 122 |
+
self._zips[shard] = z
|
| 123 |
+
return z
|
| 124 |
+
|
| 125 |
+
def __getitem__(self, index: int):
|
| 126 |
+
rec = self.records[index]
|
| 127 |
+
z = self._zip(rec["shard"])
|
| 128 |
+
rid = rec["id"]
|
| 129 |
+
|
| 130 |
+
def read(ext):
|
| 131 |
+
return z.read(f"{rid}/{rid}{ext}")
|
| 132 |
+
|
| 133 |
+
meta = json.loads(read(".json"))
|
| 134 |
+
warmup = int(meta.get("codes_warmup_frames", 1))
|
| 135 |
+
codes = np.load(io.BytesIO(read(".codes.npy"))) # [T_total, 8] int32
|
| 136 |
+
vae = np.load(io.BytesIO(read(".vae.npy"))) # [F_dav, 128] fp16 frame-major
|
| 137 |
+
probs_z = np.load(io.BytesIO(read(".probs.npz")))
|
| 138 |
+
p_idx, p_val = probs_z["idx"], probs_z["prob"] # [T_total, 8, 50]
|
| 139 |
+
|
| 140 |
+
emitted = codes.shape[0] - warmup
|
| 141 |
+
# window start s needs stitched-timeline latents [stitched_lat(s),
|
| 142 |
+
# stitched_lat(s)+441) and frames [s, s+128) emitted. stitched_lat runs
|
| 143 |
+
# slightly FASTER than uniform (345 per 100 frames), so walk max_start
|
| 144 |
+
# down until its window fits (a handful of iterations at most).
|
| 145 |
+
max_start = emitted - WINDOW
|
| 146 |
+
while max_start >= 0 and stitched_lat(max_start) + LATENTS_PER_WINDOW > vae.shape[0]:
|
| 147 |
+
max_start -= 1
|
| 148 |
+
if max_start < 0:
|
| 149 |
+
# belt-and-braces: manifest filtering should prevent this; fall
|
| 150 |
+
# back to a neighbour rather than killing the DataLoader worker.
|
| 151 |
+
return self[(index + 1) % len(self.records)]
|
| 152 |
+
if self.fixed_windows:
|
| 153 |
+
start = (max_start // 2) if index % 2 else 0
|
| 154 |
+
else:
|
| 155 |
+
g = np.random.default_rng(
|
| 156 |
+
(self.seed * 1_000_003 + index) ^ int.from_bytes(os.urandom(4), "big")
|
| 157 |
+
)
|
| 158 |
+
start = int(g.integers(0, max_start + 1))
|
| 159 |
+
|
| 160 |
+
lat_start = stitched_lat(start)
|
| 161 |
+
latents = vae[lat_start : lat_start + LATENTS_PER_WINDOW].astype(np.float32)
|
| 162 |
+
|
| 163 |
+
bounds = [(start + i) * 441 // 128 for i in range(WINDOW + 1)]
|
| 164 |
+
pool = pool_matrix(bounds) # [128, 441] fp32
|
| 165 |
+
|
| 166 |
+
rows = slice(start + warmup, start + warmup + WINDOW)
|
| 167 |
+
target_codes = codes[rows].astype(np.int64) # [128, 8]
|
| 168 |
+
t_idx = p_idx[rows].astype(np.int64) # [128, 8, 50]
|
| 169 |
+
t_val = p_val[rows].astype(np.float32)
|
| 170 |
+
# Head 0's ids were stored as raw LM token ids (code + 151675) and the
|
| 171 |
+
# uint16 dtype wrapped them mod 65536 -> code + 20603 (verified: own
|
| 172 |
+
# code lands in the unwrapped top-50 for ~100% of frames). Unwrap, and
|
| 173 |
+
# zero out anything outside the semantic vocab (e.g. EOS 151670 -> -5).
|
| 174 |
+
sem = t_idx[:, 0, :] - SEM_TOKEN_WRAP
|
| 175 |
+
bad = (sem < 0) | (sem >= 16384)
|
| 176 |
+
sem[bad] = 0
|
| 177 |
+
t_val[:, 0, :][bad] = 0.0
|
| 178 |
+
t_idx[:, 0, :] = sem
|
| 179 |
+
t_val = t_val / np.clip(t_val.sum(-1, keepdims=True), 1e-8, None)
|
| 180 |
+
|
| 181 |
+
return (
|
| 182 |
+
torch.from_numpy(latents), # [441, 128]
|
| 183 |
+
pool, # [128, 441]
|
| 184 |
+
torch.from_numpy(target_codes),
|
| 185 |
+
torch.from_numpy(t_idx),
|
| 186 |
+
torch.from_numpy(t_val),
|
| 187 |
+
)
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
def load_manifest(root: str, min_duration_s: float = 8.0) -> list[dict]:
|
| 191 |
+
# min_duration_s: a 128-frame window needs 5.12 s of emitted codes plus
|
| 192 |
+
# stitched-latent margin; 8 s is conservative and drops only 24/8120
|
| 193 |
+
# early-EOS outliers (min in corpus: 3.7 s).
|
| 194 |
+
records = []
|
| 195 |
+
with open(Path(root) / "manifest.jsonl", encoding="utf-8") as fh:
|
| 196 |
+
for line in fh:
|
| 197 |
+
rec = json.loads(line)
|
| 198 |
+
if rec.get("align_ok") and rec.get("probs_ok") and rec.get("duration_s", 0) >= min_duration_s:
|
| 199 |
+
records.append(rec)
|
| 200 |
+
return records
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
# ----------------------------------------------------------------- model ----
|
| 204 |
+
|
| 205 |
+
def encoder_trunk(model, latents: torch.Tensor, pool: torch.Tensor) -> torch.Tensor:
|
| 206 |
+
"""conv stack + pooled transformer -> per-frame hidden [B, 128, d_model]."""
|
| 207 |
+
h = model.conv_in(latents.transpose(1, 2))
|
| 208 |
+
for block in model.blocks:
|
| 209 |
+
h = block(h)
|
| 210 |
+
h = torch.bmm(pool.to(h.dtype), h.transpose(1, 2))
|
| 211 |
+
h = h + model.position[:, : pool.shape[1]].to(h.dtype)
|
| 212 |
+
layers = model.transformer if isinstance(model.transformer, torch.nn.ModuleList) else model.transformer.layers
|
| 213 |
+
for layer in layers:
|
| 214 |
+
h = layer(h)
|
| 215 |
+
return model.norm_out(h)
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
def depth_teacher_forced(dd, frame_context: torch.Tensor, codes: torch.Tensor) -> list[torch.Tensor]:
|
| 219 |
+
"""Parallel teacher-forced depth pass. codes [B, F, 8] ground truth.
|
| 220 |
+
Sequence = [ctx, e0(c0), .., e6(c6)]; causal mask; head i reads position i+1."""
|
| 221 |
+
batch, frames, _ = frame_context.shape
|
| 222 |
+
parts = [dd.context_projection(frame_context).flatten(0, 1).unsqueeze(1)]
|
| 223 |
+
for i in range(7):
|
| 224 |
+
parts.append(dd.prior_embeddings[i](codes[..., i]).flatten(0, 1).unsqueeze(1))
|
| 225 |
+
hidden = dd._decode(torch.cat(parts, dim=1)) # [B*F, 8, D]
|
| 226 |
+
return [dd.heads[i](hidden[:, i + 1]).view(batch, frames, -1) for i in range(7)]
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
def soft_ce(logits: torch.Tensor, t_idx: torch.Tensor, t_val: torch.Tensor) -> torch.Tensor:
|
| 230 |
+
"""-sum p_teacher * log q_student over the teacher's top-50 support."""
|
| 231 |
+
logq = F.log_softmax(logits.float(), dim=-1)
|
| 232 |
+
return -(t_val * logq.gather(-1, t_idx)).sum(-1).mean()
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
def compute_losses(model, batch, device, acoustic_weight: float, hard_weight: float, log_taus=None):
|
| 236 |
+
latents, pool, codes, t_idx, t_val = (x.to(device, non_blocking=True) for x in batch)
|
| 237 |
+
hidden = encoder_trunk(model, latents, pool)
|
| 238 |
+
sem_logits = model.heads[0](hidden) # [B, F, 16384]
|
| 239 |
+
ac_logits = depth_teacher_forced(model.depth_decoder, hidden, codes)
|
| 240 |
+
if log_taus is not None:
|
| 241 |
+
# Released checkpoints carry uncalibrated (muP-sharp) readouts —
|
| 242 |
+
# harmless for argmax/top-K, fatal for CE. Learnable per-head
|
| 243 |
+
# temperature; folded into head weights at save time.
|
| 244 |
+
taus = log_taus.exp()
|
| 245 |
+
sem_logits = sem_logits / taus[0]
|
| 246 |
+
ac_logits = [ac_logits[i] / taus[i + 1] for i in range(7)]
|
| 247 |
+
|
| 248 |
+
losses = {"sem_soft": soft_ce(sem_logits, t_idx[..., 0, :], t_val[..., 0, :])}
|
| 249 |
+
ac = [soft_ce(ac_logits[i], t_idx[..., i + 1, :], t_val[..., i + 1, :]) for i in range(7)]
|
| 250 |
+
losses["ac_soft"] = torch.stack(ac).mean()
|
| 251 |
+
|
| 252 |
+
if hard_weight > 0:
|
| 253 |
+
losses["sem_hard"] = F.cross_entropy(sem_logits.flatten(0, 1).float(), codes[..., 0].flatten())
|
| 254 |
+
ach = [
|
| 255 |
+
F.cross_entropy(ac_logits[i].flatten(0, 1).float(), codes[..., i + 1].flatten())
|
| 256 |
+
for i in range(7)
|
| 257 |
+
]
|
| 258 |
+
losses["ac_hard"] = torch.stack(ach).mean()
|
| 259 |
+
|
| 260 |
+
total = losses["sem_soft"] + acoustic_weight * losses["ac_soft"]
|
| 261 |
+
if hard_weight > 0:
|
| 262 |
+
total = total + hard_weight * (losses["sem_hard"] + acoustic_weight * losses["ac_hard"])
|
| 263 |
+
losses["total"] = total
|
| 264 |
+
|
| 265 |
+
with torch.no_grad():
|
| 266 |
+
losses["sem_t1"] = (sem_logits.argmax(-1) == codes[..., 0]).float().mean()
|
| 267 |
+
losses["ac_t1"] = torch.stack(
|
| 268 |
+
[(ac_logits[i].argmax(-1) == codes[..., i + 1]).float().mean() for i in range(7)]
|
| 269 |
+
).mean()
|
| 270 |
+
return losses
|
| 271 |
+
|
| 272 |
+
|
| 273 |
+
@torch.no_grad()
|
| 274 |
+
def evaluate(model, loader, device, acoustic_weight, autocast_ctx, log_taus=None):
|
| 275 |
+
model.eval()
|
| 276 |
+
sums: dict[str, float] = {}
|
| 277 |
+
n = 0
|
| 278 |
+
for batch in loader:
|
| 279 |
+
with autocast_ctx():
|
| 280 |
+
losses = compute_losses(model, batch, device, acoustic_weight, hard_weight=1.0, log_taus=log_taus)
|
| 281 |
+
for k, v in losses.items():
|
| 282 |
+
sums[k] = sums.get(k, 0.0) + float(v)
|
| 283 |
+
n += 1
|
| 284 |
+
model.train()
|
| 285 |
+
return {k: v / max(n, 1) for k, v in sums.items()}
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
# ------------------------------------------------------------------ main ----
|
| 289 |
+
|
| 290 |
+
def main():
|
| 291 |
+
ap = argparse.ArgumentParser()
|
| 292 |
+
ap.add_argument("--corpus", default=r"M:\HOT-Step-CPP\_corpora\mm3-rvq-distill-8k")
|
| 293 |
+
ap.add_argument("--encoder-dir", default=r"M:\HOT-Step-CPP\_experiments\open-rvq-pooled-v4")
|
| 294 |
+
ap.add_argument("--encoder-file", default="rvq_encoder.safetensors")
|
| 295 |
+
ap.add_argument("--config-file", default="rvq_encoder_config.json")
|
| 296 |
+
ap.add_argument("--adapter-dir", default=ADAPTER_DIR_DEFAULT)
|
| 297 |
+
ap.add_argument("--out", required=True)
|
| 298 |
+
ap.add_argument("--steps", type=int, default=20000)
|
| 299 |
+
ap.add_argument("--batch", type=int, default=16)
|
| 300 |
+
ap.add_argument("--grad-accum", type=int, default=4)
|
| 301 |
+
ap.add_argument("--lr", type=float, default=1e-4)
|
| 302 |
+
ap.add_argument("--warmup", type=int, default=200)
|
| 303 |
+
ap.add_argument("--weight-decay", type=float, default=0.01)
|
| 304 |
+
ap.add_argument("--acoustic-weight", type=float, default=1.0)
|
| 305 |
+
ap.add_argument("--hard-weight", type=float, default=0.0,
|
| 306 |
+
help="mix in hard-label CE alongside soft distillation")
|
| 307 |
+
ap.add_argument("--sem-tau", type=float, default=16.0,
|
| 308 |
+
help="initial semantic logit temperature (line-searched on pooled-v4)")
|
| 309 |
+
ap.add_argument("--ac-tau", type=float, default=2.0,
|
| 310 |
+
help="initial acoustic logit temperature")
|
| 311 |
+
ap.add_argument("--holdout-frac", type=float, default=0.05)
|
| 312 |
+
ap.add_argument("--eval-every", type=int, default=500)
|
| 313 |
+
ap.add_argument("--save-every", type=int, default=1000)
|
| 314 |
+
ap.add_argument("--workers", type=int, default=2)
|
| 315 |
+
ap.add_argument("--seed", type=int, default=1)
|
| 316 |
+
ap.add_argument("--vram-frac", type=float, default=0.85)
|
| 317 |
+
ap.add_argument("--device", default="cuda" if torch.cuda.is_available() else "cpu")
|
| 318 |
+
ap.add_argument("--smoke", action="store_true", help="2 optimizer steps on CPU-sized batch, then exit")
|
| 319 |
+
args = ap.parse_args()
|
| 320 |
+
|
| 321 |
+
torch.manual_seed(args.seed)
|
| 322 |
+
device = torch.device(args.device)
|
| 323 |
+
if device.type == "cuda":
|
| 324 |
+
torch.cuda.set_per_process_memory_fraction(args.vram_frac)
|
| 325 |
+
|
| 326 |
+
ref = load_adapter_module(args.adapter_dir)
|
| 327 |
+
from safetensors.torch import load_file, save_file
|
| 328 |
+
|
| 329 |
+
enc_dir = Path(args.encoder_dir)
|
| 330 |
+
config = ref.RVQEncoderConfig.from_dict(
|
| 331 |
+
json.loads((enc_dir / args.config_file).read_text(encoding="utf-8"))
|
| 332 |
+
)
|
| 333 |
+
model = ref.MiniMaxMusicRVQEncoder(config)
|
| 334 |
+
model.load_state_dict(load_file(str(enc_dir / args.encoder_file)), strict=True)
|
| 335 |
+
model.to(device).train()
|
| 336 |
+
n_params = sum(p.numel() for p in model.parameters())
|
| 337 |
+
print(f"[model] {n_params/1e6:.1f}M params, warm start from {enc_dir / args.encoder_file}")
|
| 338 |
+
|
| 339 |
+
records = load_manifest(args.corpus)
|
| 340 |
+
train_recs = [r for r in records if not holdout_of(r["id"], args.holdout_frac)]
|
| 341 |
+
hold_recs = [r for r in records if holdout_of(r["id"], args.holdout_frac)]
|
| 342 |
+
print(f"[data] {len(records)} usable records -> {len(train_recs)} train / {len(hold_recs)} holdout")
|
| 343 |
+
|
| 344 |
+
train_ds = Corpus8k(args.corpus, train_recs, args.seed)
|
| 345 |
+
hold_ds = Corpus8k(args.corpus, hold_recs, args.seed, fixed_windows=True)
|
| 346 |
+
loader_kw = dict(
|
| 347 |
+
batch_size=args.batch,
|
| 348 |
+
num_workers=args.workers,
|
| 349 |
+
pin_memory=device.type == "cuda",
|
| 350 |
+
persistent_workers=args.workers > 0,
|
| 351 |
+
)
|
| 352 |
+
train_loader = DataLoader(train_ds, shuffle=True, drop_last=True, **loader_kw)
|
| 353 |
+
hold_loader = DataLoader(hold_ds, shuffle=False, **loader_kw)
|
| 354 |
+
|
| 355 |
+
log_taus = torch.nn.Parameter(
|
| 356 |
+
torch.log(torch.tensor([args.sem_tau] + [args.ac_tau] * 7, dtype=torch.float32, device=device))
|
| 357 |
+
)
|
| 358 |
+
decay, no_decay = [], []
|
| 359 |
+
for name, p in model.named_parameters():
|
| 360 |
+
(no_decay if p.ndim <= 1 or "position" in name else decay).append(p)
|
| 361 |
+
opt = torch.optim.AdamW(
|
| 362 |
+
[
|
| 363 |
+
{"params": decay, "weight_decay": args.weight_decay},
|
| 364 |
+
{"params": no_decay, "weight_decay": 0.0},
|
| 365 |
+
{"params": [log_taus], "weight_decay": 0.0},
|
| 366 |
+
],
|
| 367 |
+
lr=args.lr, betas=(0.9, 0.95),
|
| 368 |
+
)
|
| 369 |
+
|
| 370 |
+
def lr_at(step):
|
| 371 |
+
if step < args.warmup:
|
| 372 |
+
return args.lr * (step + 1) / args.warmup
|
| 373 |
+
t = (step - args.warmup) / max(args.steps - args.warmup, 1)
|
| 374 |
+
return args.lr * 0.5 * (1 + math.cos(math.pi * min(t, 1.0)))
|
| 375 |
+
|
| 376 |
+
use_bf16 = device.type == "cuda"
|
| 377 |
+
|
| 378 |
+
def autocast_ctx():
|
| 379 |
+
return torch.autocast("cuda", dtype=torch.bfloat16) if use_bf16 else torch.autocast("cpu", enabled=False)
|
| 380 |
+
|
| 381 |
+
out = Path(args.out)
|
| 382 |
+
out.mkdir(parents=True, exist_ok=True)
|
| 383 |
+
shutil.copyfile(enc_dir / args.config_file, out / "rvq_encoder_config.json")
|
| 384 |
+
(out / "run_args.json").write_text(json.dumps(vars(args), indent=1), encoding="utf-8")
|
| 385 |
+
log_path = out / "train_log.jsonl"
|
| 386 |
+
|
| 387 |
+
def save_ckpt(tag: str):
|
| 388 |
+
ck = out / tag
|
| 389 |
+
ck.mkdir(exist_ok=True)
|
| 390 |
+
state = {k: v.detach().to(torch.float32).cpu() for k, v in model.state_dict().items()}
|
| 391 |
+
# Fold the learned temperatures into the readout heads so the saved
|
| 392 |
+
# file is a plain calibrated encoder (argmax/top-K ordering unchanged,
|
| 393 |
+
# CE now meaningful). heads.0 = semantic; depth_decoder.heads.i = c(i+1).
|
| 394 |
+
taus = log_taus.detach().exp().cpu()
|
| 395 |
+
for key in ("heads.0.weight", "heads.0.bias"):
|
| 396 |
+
state[key] = state[key] / taus[0]
|
| 397 |
+
for i in range(7):
|
| 398 |
+
for suffix in ("weight", "bias"):
|
| 399 |
+
key = f"depth_decoder.heads.{i}.{suffix}"
|
| 400 |
+
state[key] = state[key] / taus[i + 1]
|
| 401 |
+
save_file(state, str(ck / "rvq_encoder.safetensors"))
|
| 402 |
+
shutil.copyfile(out / "rvq_encoder_config.json", ck / "rvq_encoder_config.json")
|
| 403 |
+
(ck / "calibration.json").write_text(
|
| 404 |
+
json.dumps({"folded_taus": [round(float(t), 4) for t in taus]}), encoding="utf-8"
|
| 405 |
+
)
|
| 406 |
+
print(f"[ckpt] {ck} (taus folded: {[round(float(t), 2) for t in taus]})")
|
| 407 |
+
|
| 408 |
+
best_hold = float("inf")
|
| 409 |
+
step = 0
|
| 410 |
+
t0 = time.time()
|
| 411 |
+
opt.zero_grad(set_to_none=True)
|
| 412 |
+
data_iter = iter(train_loader)
|
| 413 |
+
while step < args.steps:
|
| 414 |
+
for micro in range(args.grad_accum):
|
| 415 |
+
try:
|
| 416 |
+
batch = next(data_iter)
|
| 417 |
+
except StopIteration:
|
| 418 |
+
data_iter = iter(train_loader)
|
| 419 |
+
batch = next(data_iter)
|
| 420 |
+
with autocast_ctx():
|
| 421 |
+
losses = compute_losses(
|
| 422 |
+
model, batch, device, args.acoustic_weight, args.hard_weight, log_taus=log_taus
|
| 423 |
+
)
|
| 424 |
+
(losses["total"] / args.grad_accum).backward()
|
| 425 |
+
if not torch.isfinite(losses["total"]):
|
| 426 |
+
raise FloatingPointError(f"non-finite loss at step {step}: {losses}")
|
| 427 |
+
torch.nn.utils.clip_grad_norm_(list(model.parameters()) + [log_taus], 1.0)
|
| 428 |
+
for group in opt.param_groups:
|
| 429 |
+
group["lr"] = lr_at(step)
|
| 430 |
+
opt.step()
|
| 431 |
+
opt.zero_grad(set_to_none=True)
|
| 432 |
+
step += 1
|
| 433 |
+
|
| 434 |
+
if step % 25 == 0 or args.smoke:
|
| 435 |
+
row = {k: round(float(v.detach()), 4) for k, v in losses.items()}
|
| 436 |
+
row.update(
|
| 437 |
+
step=step, lr=round(lr_at(step), 8), elapsed_s=round(time.time() - t0, 1),
|
| 438 |
+
taus=[round(float(t), 2) for t in log_taus.detach().exp()],
|
| 439 |
+
)
|
| 440 |
+
print(f"[{step}/{args.steps}] " + " ".join(f"{k}={v}" for k, v in row.items() if k != "step"))
|
| 441 |
+
with open(log_path, "a", encoding="utf-8") as fh:
|
| 442 |
+
fh.write(json.dumps(row) + "\n")
|
| 443 |
+
|
| 444 |
+
if args.smoke and step >= 2:
|
| 445 |
+
print("[smoke] OK — forward/backward/step ran clean")
|
| 446 |
+
return
|
| 447 |
+
|
| 448 |
+
if step % args.eval_every == 0:
|
| 449 |
+
ev = evaluate(model, hold_loader, device, args.acoustic_weight, autocast_ctx, log_taus=log_taus)
|
| 450 |
+
row = {("hold_" + k): round(float(v), 4) for k, v in ev.items()}
|
| 451 |
+
row["step"] = step
|
| 452 |
+
print(f"[eval @{step}] " + " ".join(f"{k}={v}" for k, v in row.items() if k != "step"))
|
| 453 |
+
with open(log_path, "a", encoding="utf-8") as fh:
|
| 454 |
+
fh.write(json.dumps(row) + "\n")
|
| 455 |
+
score = ev["sem_hard"] + args.acoustic_weight * ev["ac_hard"]
|
| 456 |
+
if score < best_hold:
|
| 457 |
+
best_hold = score
|
| 458 |
+
save_ckpt("best_holdout")
|
| 459 |
+
|
| 460 |
+
if step % args.save_every == 0:
|
| 461 |
+
save_ckpt(f"step{step:06d}")
|
| 462 |
+
|
| 463 |
+
save_ckpt("final")
|
| 464 |
+
print(f"[done] {args.steps} steps in {(time.time() - t0)/3600:.2f} h; best holdout hard-CE sum {best_hold:.4f}")
|
| 465 |
+
print("[next] run each kept checkpoint through the REAL-AUDIO gate: "
|
| 466 |
+
"export_codes_v4.py --encoder-file <ckpt>\\rvq_encoder.safetensors "
|
| 467 |
+
"--config-file <ckpt>\\rvq_encoder_config.json, then the 1-step lm_sft_train2 [Base] CE.")
|
| 468 |
+
|
| 469 |
+
|
| 470 |
+
if __name__ == "__main__":
|
| 471 |
+
main()
|