NicoLab28 commited on
Commit
12a7266
·
verified ·
1 Parent(s): ef89d0f

v3: calibrated against the stock 32B, image tokens for the 8B, demo folder

Browse files
.gitattributes CHANGED
@@ -33,3 +33,9 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ demo/chess-32b-reference.mp4 filter=lfs diff=lfs merge=lfs -text
37
+ demo/chess-4b-mlp.mp4 filter=lfs diff=lfs merge=lfs -text
38
+ demo/chess-4b-ridge.mp4 filter=lfs diff=lfs merge=lfs -text
39
+ demo/chess-8b-mlp.mp4 filter=lfs diff=lfs merge=lfs -text
40
+ demo/chess-8b-ridge.mp4 filter=lfs diff=lfs merge=lfs -text
41
+ demo/chess-comparison.mp4 filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -16,11 +16,64 @@ library_name: comfyui
16
 
17
  **Projection matrices that let a small Qwen3-VL replace the Qwen3-VL-32B text encoder of MiniMax H3.**
18
 
19
- **15.7 GB → 4.5 GB of VRAM**, with no change to the diffusion model, the VAEs or the sampler.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  > ⚠️ **Proof of concept — working, but a proof of concept.** It runs and produces good video, and every number below was measured on real hardware. Built and tested on a single setup (Windows 11, NVIDIA, ComfyUI 0.31.0) with deliberately limited exploration.
22
 
23
- > **Arriving from a tutorial or an article?** Anything published before 11 August names files that have moved. `h3_qwen3vl_4b_tap24`, `h3_control_zero` and `h3_control_identity` are still here, one folder down in `obsolete/`, so nothing is lost. But take the current set instead: **`mmh3-4b-ClipProj-celeb-mlp`** for a Qwen3-VL-4B, **`mmh3-8b-ClipProj-celeb-mlp`** for an 8B. They are better on every measurement below, and they need node **0.1.4 or later**.
24
 
25
  These files are useless on their own. They require the custom node:
26
  **[github.com/nicolab28/ComfyUI-ClipProj](https://github.com/nicolab28/ComfyUI-ClipProj)**
@@ -35,19 +88,37 @@ It did, and the first results were good enough that keeping them on my own disk
35
 
36
  It is also why there are so many measurements on the model card. Before showing this to anyone I had to convince myself I was not fooling myself, and most of what I tried along the way turned out to be wrong. Those attempts are written down as well, in [MEASUREMENTS.md](https://github.com/nicolab28/ComfyUI-ClipProj/blob/main/MEASUREMENTS.md) and [CALIBRATION.md](https://github.com/nicolab28/ComfyUI-ClipProj/blob/main/CALIBRATION.md).
37
 
38
- ## Update to node 0.1.4, and re-download the `-mlp` matrices
 
 
 
 
 
 
 
 
 
 
 
39
 
40
- **The `-mlp` matrices are now fp16 and half the size.** The residual network was published in fp32 and the node forced fp32 on load regardless of the file, so storing it in half precision would have halved the download and saved nothing at all in VRAM. Node 0.1.4 keeps a residual in whatever precision it was saved in, converting its inputs and outputs around it instead. Measured: 240 MB on the card instead of 480 for the 4B, 288 instead of 576 for the 8B. The files here have been replaced under the same names — re-download them, and take 0.1.4 with them, because an older node will load them and cast them straight back up to fp32.
41
 
42
- Node 0.1.4 also frees the card **before** loading a replacement encoder rather than after, which matters if yours is tight enough that two encoders will not sit on it at once.
43
 
44
- ## Also in 0.1.3
45
 
46
- Two reasons, one of them silent.
47
 
48
- **The `-mlp` matrices carry a residual network, and an older node ignores it without saying so.** It reads the matrix, finds keys it does not know, drops them, and applies the linear part alone. Nothing fails, nothing warns, and you end up judging the plain matrix while believing you tested the residual. Node 0.1.3 reads them.
 
 
 
 
 
 
 
49
 
50
- **Everything is renamed.** The old `h3_qwen3vl_*` files have moved to `obsolete/` and the `.pt` copies are gone: opening a pickle executes code, which makes no sense for a file holding six tensors. If a workflow of yours names an old file, either point it at `obsolete/` or, better, switch to the new set.
51
 
52
  ## What this is
53
 
@@ -65,22 +136,22 @@ It works because every Qwen3-VL shares the **same tokenizer** (151936 tokens): a
65
 
66
  Put them in `ComfyUI/models/clip_projections/`.
67
 
68
- **Start with `mmh3-8b-ClipProj-celeb-mlp` if you have the VRAM, `mmh3-4b-ClipProj-celeb-mlp` otherwise.**
69
 
70
- | File | Encoder | Names covered | Residual | Test cosine |
71
  |---|---|---|---|---|
72
- | `mmh3-4b-ClipProj` | any Qwen3-VL-4B | no | no | 0.7169 |
73
- | `mmh3-4b-ClipProj-mlp` | any Qwen3-VL-4B | no | yes | 0.7944 |
74
- | `mmh3-4b-ClipProj-celeb` | any Qwen3-VL-4B | **yes** | no | 0.7095 |
75
- | `mmh3-4b-ClipProj-celeb-mlp` | any Qwen3-VL-4B | **yes** | yes | 0.7930 |
76
- | `mmh3-8b-ClipProj` | any Qwen3-VL-8B | no | no | 0.7528 |
77
- | `mmh3-8b-ClipProj-mlp` | any Qwen3-VL-8B | no | yes | 0.7970 |
78
- | `mmh3-8b-ClipProj-celeb` | any Qwen3-VL-8B | **yes** | no | 0.7466 |
79
- | `mmh3-8b-ClipProj-celeb-mlp` | any Qwen3-VL-8B | **yes** | yes | **0.8037** |
80
- | `mmh3-ClipProj-control-zero` | — | control, run it once | — | — |
81
- | `mmh3-ClipProj-control-identity` | | control, run it once | | |
82
-
83
- All eight are calibrated on the same general corpus and measured on the same held-out prompts, so the column is comparable across every row.
84
 
85
  Every matrix works on **any variant of its own size**: the measured cosine gap between a bf16-calibrated matrix applied to an abliterated fp8 encoder is 0.0023. You do not need the exact checkpoint a matrix was calibrated on. The 8B matrices need an 8B encoder though — 4096 input dimensions instead of 2560 — and the node checks the width and refuses a mismatch.
86
 
@@ -123,6 +194,10 @@ Two things measured while building it. Width beats depth: at equal parameter cou
123
 
124
  ## Measured results
125
 
 
 
 
 
126
  | | 4B | 8B |
127
  |---|---|---|
128
  | matrix, no names | 0.7169 | 0.7528 |
 
16
 
17
  **Projection matrices that let a small Qwen3-VL replace the Qwen3-VL-32B text encoder of MiniMax H3.**
18
 
19
+ **15.7 GB → 4.9 GB of VRAM**, with no change to the diffusion model, the VAEs or the sampler. The 32B is nvfp4, the small encoders int8; the projection itself costs 52 MB to 604 MB depending on the file.
20
+
21
+ <video controls src="https://huggingface.co/NicoLab28/ClipProj-MiniMax-H3/resolve/main/demo/chess-comparison.mp4"></video>
22
+
23
+ Five renders of one scene, in this order: **4B matrix-only, 8B matrix-only, the 32B reference, 8B residual, 4B residual.** The reference sits in the middle, so each half is read against it.
24
+
25
+ | conditioning | encoder | projection on card | total |
26
+ |---|---|---|---|
27
+ | Qwen3-VL-32B nvfp4 | 15.69 GB | — | **15.7 GB** |
28
+ | Qwen3-VL-8B int8 + `v3-mlp` | 10.01 GB | 604 MB | **10.6 GB** |
29
+ | Qwen3-VL-8B int8 + `v3` | 10.01 GB | 84 MB | **10.1 GB** |
30
+ | Qwen3-VL-4B int8 + `v3-mlp` | 4.83 GB | 503 MB | **5.3 GB** |
31
+ | Qwen3-VL-4B int8 + `v3` | 4.83 GB | 52 MB | **4.9 GB** |
32
+
33
+ Two things this table makes explicit, because both would otherwise flatter the result. **The quantisations differ**: the 32B is nvfp4, the students are int8 — part of the size gap is format, not parameter count. And **the residual is not free**: the node loads the matrix in float32 and keeps the residual in the dtype it was saved in, so a `-mlp` file costs roughly half a gigabyte on the card where the plain matrix costs a rounding error.
34
+
35
+ | | |
36
+ |---|---|
37
+ | diffusion model | `minimax_h3_fl2va_pruned_int8_convrot` |
38
+ | turbo LoRA | `minimax_h3_fl2v_turbo_8step_v1.0_comfyui_bf16`, strength 1.0 |
39
+ | sampler / scheduler | `res_multistep` / `simple` |
40
+ | steps | 8 |
41
+ | seed | 42 |
42
+ | resolution | 16:9 at 0.8 MP, upscaled 2× by RTX Video Super Resolution ULTRA |
43
+ | frames | 192 at 24 fps — 8.00 s |
44
+ | video VAE | `minimax_h3_video_vae_int8_convrot` |
45
+ | audio VAE | `minimax_h3_audio_vae_fp32` |
46
+
47
+ **Only the projection changes between the five.** Everything else is identical, and on one machine the pipeline is deterministic — running the same configuration twice gives byte-identical decoded video and audio, verified by MD5 — so every difference you see comes from the projection and nothing else.
48
+
49
+ ### You will not reproduce these files, and that is expected
50
+
51
+ Run the demo prompt with seed 42 on your own machine and you will get the same scene, not the same file. **The result depends on the model of GPU the encoder runs on.**
52
+
53
+ This came out of an unrelated test — checking that three loading modes gave the same output — and the cards happened to be at hand. Four of them is not a study, and none of this was the point of the exercise; it is written down because it would otherwise look like something is broken. Same prompt, same seed, same everything else:
54
+
55
+ | card | decoded video MD5 |
56
+ |---|---|
57
+ | RTX 4070 | `1daf9be3…` |
58
+ | RTX 3090 | `0a415964…` |
59
+ | RTX 3060 | `dcb2f965…` |
60
+ | RTX 4070 Ti SUPER | `b3b185e7…` |
61
+
62
+ Four cards, four results. **Two different RTX 3090s gave byte-identical output**, so it is the model that decides, not the individual card — and not the architecture either, since the 3060 and the 3090 are both Ampere and disagree.
63
+
64
+ The cause is small and the consequence is not. Encoding the same prompt on two cards gives conditioning that agrees to a **relative error of 7 × 10⁻⁷** — cosine 1.00000000, largest single-component difference 0.002. Different numbers of compute units mean different reduction orders, so floating-point additions do not happen in the same sequence. Eight denoising steps turn that into a different piece of furniture, or a wristwatch that is there on one card and absent on another. That watch is nowhere in the prompt, which is exactly why it is free to move.
65
+
66
+ So: on one machine, with one card, everything here is reproducible to the bit — that is what makes the five-way comparison above meaningful. Across machines, expect the same scene with different details. This is a property of the diffusion model and its sampler, not of the projections: the reference 32B behaves identically.
67
+
68
+ The full prompt is in [`demo/chess-prompt.txt`](demo/chess-prompt.txt), the settings above in machine-readable form in [`demo/generation-settings.json`](demo/generation-settings.json), and the five renders are in [`demo/`](demo) one by one if you want to step through them.
69
+
70
+ **What to look at.** Her knee. The prompt asks for it three times and ends on a sentence of its own — *"Her knee never stops bouncing."* It is a continuous involuntary motion with no narrative purpose, which makes it the clearest single sign that a projection carried what was written. Then look at the cat, the laundry and the furniture: named once, anchored nowhere, and they move from one render to the next. That part is expected, and it is the honest limit of the method — what the prompt states is carried, what it leaves open is refurnished by the model.
71
+
72
+ One thing none of the five gets right, the 32B included: she lifts a knight and does not put it back on the same square. Object permanence behind an occluding hand, on a grid of sixty-four identical squares, is a limit of the video model rather than of the conditioning.
73
 
74
  > ⚠️ **Proof of concept — working, but a proof of concept.** It runs and produces good video, and every number below was measured on real hardware. Built and tested on a single setup (Windows 11, NVIDIA, ComfyUI 0.31.0) with deliberately limited exploration.
75
 
76
+ > **Arriving from a tutorial or an article?** Anything published before this release names older files. Nothing has been deleted the previous sets are still here but take the current one: **`mmh3-4b-ClipProj-v3-mlp`** for a Qwen3-VL-4B, **`mmh3-8b-ClipProj-v3-mlp`** for an 8B. They need node **0.1.13 or later**, and an earlier node raises `KeyError: 'W'` on them rather than falling back quietly.
77
 
78
  These files are useless on their own. They require the custom node:
79
  **[github.com/nicolab28/ComfyUI-ClipProj](https://github.com/nicolab28/ComfyUI-ClipProj)**
 
88
 
89
  It is also why there are so many measurements on the model card. Before showing this to anyone I had to convince myself I was not fooling myself, and most of what I tried along the way turned out to be wrong. Those attempts are written down as well, in [MEASUREMENTS.md](https://github.com/nicolab28/ComfyUI-ClipProj/blob/main/MEASUREMENTS.md) and [CALIBRATION.md](https://github.com/nicolab28/ComfyUI-ClipProj/blob/main/CALIBRATION.md).
90
 
91
+ ## What changed in v3
92
+
93
+ **Calibrated against the stock encoder.** The previous matrices were fitted against a modified 32B. While testing them we found that naming one part of a body could rewrite the whole of it — build, height and face shifting together, none of it asked for. v3 targets `qwen3vl_32b_minimax_h3_nvfp4_awq`, the encoder a plain `Load CLIP` gives you, and we no longer observe the problem.
94
+
95
+ **The 8B now sees image tokens.** Until v3 the image corpus had only ever been encoded with a 4B student, so both 8B matrices projected vision tokens without having seen a single one — while the node accepts a reference image. Measured on 100 held-out images, on the raw conditioning the diffusion model actually receives:
96
+
97
+ | | vision tokens | text in the same sequences |
98
+ |---|---|---|
99
+ | 8B residual, before | 0.7692 | 0.9085 |
100
+ | 8B residual, after | 0.8578 | 0.9605 |
101
+ | 8B matrix, before | 0.7845 | 0.8926 |
102
+ | 8B matrix, after | 0.8457 | 0.9361 |
103
 
104
+ That costs 0.0027 of pure-text cosine on the residual and 0.0013 on the matrix. Both students now share the same corpus, so the 4B and the 8B are comparable to each other for the first time.
105
 
106
+ **Node 0.1.13 is required.** The `-v3-mlp` files carry no linear matrix at all the non-linear part does the whole job and an earlier node raises `KeyError: 'W'` when it opens one.
107
 
108
+ **And `ClipProjApply` now works with an int8 encoder.** Loading a quantised Qwen3-VL through ComfyUI's own `Load CLIP` and handing it to `ClipProjApply` used to fail inside the vision tower as soon as a reference image was present: `dequantize_int8_embedding` was called on a tensor the cast context had already dequantised to bf16, and the error named nothing useful. The position embedding now falls back to a plain lookup when that happens. A text-only prompt never triggered it, which is why it stayed hidden — it only appeared with an image, on the pageable path. The five demo renders above use that path.
109
 
110
+ **One number, measured the same way for everything.** The `cos_test` written inside each file is its training-time figure against its own campaign's target, and it does **not** compare across versions. The comparable number is stored separately as `cos_prompt_reference`: every projection encoded against the same stock 32B, on the same prompt.
111
 
112
+ | projection | vs 32B |
113
+ |---|---|
114
+ | `mmh3-8b-ClipProj-v3-mlp` | 0.9449 |
115
+ | v2 `mmh3-8b-ClipProj-celeb-mlp` | 0.9393 |
116
+ | `mmh3-4b-ClipProj-v3-mlp` | 0.9381 |
117
+ | v2 `mmh3-4b-ClipProj-celeb-mlp` | 0.9293 |
118
+ | `mmh3-8b-ClipProj-v3` | 0.9289 |
119
+ | `mmh3-4b-ClipProj-v3` | 0.9193 |
120
 
121
+ **Prefer the `-mlp` files.** The plain matrices are honest linear baselines and useful as such, but a linear map does not carry an attribute the prompt states once. Ask for crossed ankles and it will seat the subject cross-legged you can see it in the demo above, in the first two segments.
122
 
123
  ## What this is
124
 
 
136
 
137
  Put them in `ComfyUI/models/clip_projections/`.
138
 
139
+ **Start with `mmh3-8b-ClipProj-v3-mlp` if you have the VRAM, `mmh3-4b-ClipProj-v3-mlp` otherwise.** Both need node 0.1.13 or later.
140
 
141
+ | File | Encoder | Structure | Size | vs 32B |
142
  |---|---|---|---|---|
143
+ | `mmh3-4b-ClipProj-v3-mlp` | any Qwen3-VL-4B | non-linear | 503 MB | 0.9381 |
144
+ | `mmh3-8b-ClipProj-v3-mlp` | any Qwen3-VL-8B | non-linear | 604 MB | **0.9449** |
145
+ | `mmh3-4b-ClipProj-v3` | any Qwen3-VL-4B | matrix only | 26 MB | 0.9193 |
146
+ | `mmh3-8b-ClipProj-v3` | any Qwen3-VL-8B | matrix only | 42 MB | 0.9289 |
147
+ | `mmh3-ClipProj-control-zero` | | control, run it once | 52 MB | |
148
+ | `mmh3-ClipProj-control-identity` | | control, run it once | 52 MB | |
149
+
150
+ The v2 files — `mmh3-4b-ClipProj-celeb-mlp` and the seven beside it are kept and still work. They were calibrated against a modified 32B and against a corpus containing no image tokens, so prefer v3.
151
+
152
+ The last column is the one number measured identically for every row: same stock 32B, same prompt, cosine averaged token by token. It is also written inside each file as `cos_prompt_reference`.
153
+
154
+ The `-v3-mlp` files are larger than the v2 residuals 503 and 604 MB against 304 and 386 because the hidden width went from 16 384 to 32 768. That is the whole reason for the extra download.
155
 
156
  Every matrix works on **any variant of its own size**: the measured cosine gap between a bf16-calibrated matrix applied to an abliterated fp8 encoder is 0.0023. You do not need the exact checkpoint a matrix was calibrated on. The 8B matrices need an 8B encoder though — 4096 input dimensions instead of 2560 — and the node checks the width and refuses a mismatch.
157
 
 
194
 
195
  ## Measured results
196
 
197
+ > **Two scales of cosine appear on this page and they do not compare.** The table just below is the v2 training-time figure, measured on held-out prompts against *that* campaign's target. The 0.93–0.94 figures higher up are `cos_prompt_reference`: one prompt, every projection encoded against the same stock 32B. A number is only ever comparable to another measured the same way — mixing them is how a set of matrices can look like it improved when nothing was established.
198
+
199
+ The v2 campaign, on its own held-out set:
200
+
201
  | | 4B | 8B |
202
  |---|---|---|
203
  | matrix, no names | 0.7169 | 0.7528 |
RELEASE_NOTES_v3.md ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ClipProj v3
2
+
3
+ Four new matrices, calibrated against `qwen3vl_32b_minimax_h3_nvfp4_awq` — the
4
+ base MiniMax H3 encoder, the one a plain `Load CLIP` gives you.
5
+
6
+ Requires node **0.1.13** or later. The `-v3-mlp` files carry no linear matrix,
7
+ and an older node raises `KeyError: 'W'` on load.
8
+
9
+ ## What changed
10
+
11
+ **Body descriptions.** While testing v2 we found that naming one part of a
12
+ body could rewrite the whole of it — build, height and face shifting together,
13
+ none of it asked for. Those matrices were calibrated against a modified 32B.
14
+ v3 is calibrated against the stock encoder, and we no longer observe the
15
+ problem: a build and an attribute stated in the same prompt are honoured
16
+ independently.
17
+
18
+ **Everything is measurably closer to the 32B.** Measured on a single reference
19
+ prompt, every projection encoded against the same stock 32B, so the numbers are
20
+ comparable to each other — which the figures published with v1 and v2 were not,
21
+ having been measured against different targets and in different spaces. The
22
+ `cos_test` written inside each file is the training-time figure against that
23
+ campaign's own target and does **not** compare across versions; the comparable
24
+ number is stored separately as `cos_prompt_reference`.
25
+
26
+ The render pipeline was verified deterministic before measuring anything: the
27
+ same workflow run twice, thirteen days apart, produced two files with identical
28
+ SHA-256. Any difference below therefore comes from the projection and nothing
29
+ else.
30
+
31
+ | projection | mean cosine vs 32B |
32
+ |---|---|
33
+ | 8B v3-mlp | 0.9449 |
34
+ | 8B v2 `celeb-mlp` | 0.9393 |
35
+ | 4B v3-mlp | 0.9381 |
36
+ | 4B v2 `celeb-mlp` | 0.9293 |
37
+ | 8B v3 | 0.9289 |
38
+ | 4B v3 | 0.9193 |
39
+
40
+ **The 8B now sees image tokens.** Until v3 the image corpus had only ever been
41
+ encoded with a 4B student, so both 8B matrices projected vision tokens without
42
+ having seen a single one — while the node accepts a reference image. Measured on
43
+ 100 held-out images, on the raw conditioning the diffusion model actually
44
+ receives:
45
+
46
+ | | vision tokens | text in the same sequences |
47
+ |---|---|---|
48
+ | 8B residual, before | 0.7692 | 0.9085 |
49
+ | 8B residual, after | 0.8578 | 0.9605 |
50
+ | 8B matrix, before | 0.7845 | 0.8926 |
51
+ | 8B matrix, after | 0.8457 | 0.9361 |
52
+
53
+ That costs 0.0027 of pure-text cosine on the residual and 0.0013 on the matrix —
54
+ which is why the 8B figures above are slightly below what a text-only corpus
55
+ would have given.
56
+
57
+ **Prefer the `-mlp` files.** The plain matrices are honest linear baselines and
58
+ they are useful as such, but a linear map does not carry an attribute that the
59
+ prompt states once. Ask for crossed ankles and a plain matrix will seat the
60
+ subject cross-legged instead. The residual networks hold those.
61
+
62
+ ## What this does not fix, and will not
63
+
64
+ A projection cannot invent information the small encoder never had. Five hours
65
+ of training on a 3090, two more to encode the dataset, six and a half million
66
+ tokens — none of that changes what a 4B model wrote down in the first place.
67
+ The result is an approximation of the 32B's conditioning, not a copy of it.
68
+
69
+ In practice the line falls here: **what the prompt states, the projection
70
+ carries; what the prompt leaves open, the model fills from its own prior.**
71
+ Constrain a scene tightly and the projected renders track the 32B closely.
72
+ Leave the set dressing unstated — a cat somewhere, laundry on a line, furniture
73
+ — and it will be furnished differently. That is not a defect to be tuned away,
74
+ it is what a 20-degree angle between two conditioning vectors looks like on
75
+ screen.
76
+
77
+ ## Training data
78
+
79
+ The aim was to activate as much of the encoder's weight space as possible
80
+ rather than to cover one domain deeply. A matrix only learns to project the
81
+ directions it has actually seen used, so the corpus deliberately mixes
82
+ registers, languages and lengths.
83
+
84
+ | source | tokens |
85
+ |---|---|
86
+ | cinematic video prompts | 1 342 987 |
87
+ | native H3 format, 4 length draws | 3 169 879 |
88
+ | explicit register | 544 073 |
89
+ | Chinese | 532 302 |
90
+ | celebrity prompts, long form | 314 516 |
91
+ | filler sequences | 149 917 |
92
+ | celebrity prompts, short form | 99 668 |
93
+ | images, 3 blocks, 1 700 images | 349 244 |
94
+ | **total, both students** | **6 502 586** |
95
+
96
+ Both students now see the same corpus, images included — which makes the 4B and
97
+ the 8B comparable to each other for the first time.
98
+
99
+ 3 331 prompts for fitting, one in fifty held out for measurement. Tap 24 on
100
+ both students. Sequence lengths are drawn at random rather than truncated to a
101
+ fixed size, so a given word appears at many different positions instead of
102
+ always the same ones.
103
+
104
+ ## The demo folder
105
+
106
+ `demo/` holds five renders of the same scene. Same prompt, same seed 42, same
107
+ 8 steps, same turbo LoRA, same DiT, same VAE — only the projection changes. The
108
+ pipeline is reproducible bit for bit, verified by running it twice and comparing
109
+ the decoded video and audio streams, so every difference between these five
110
+ comes from the projection and nothing else.
111
+
112
+ | file | conditioning |
113
+ |---|---|
114
+ | `chess-32b-reference.mp4` | Qwen3-VL-32B, 15.7 GB |
115
+ | `chess-8b-mlp.mp4` | Qwen3-VL-8B + `v3-mlp`, 10.0 GB |
116
+ | `chess-4b-mlp.mp4` | Qwen3-VL-4B + `v3-mlp`, 4.8 GB |
117
+ | `chess-8b-ridge.mp4` | Qwen3-VL-8B + `v3`, matrix only |
118
+ | `chess-4b-ridge.mp4` | Qwen3-VL-4B + `v3`, matrix only |
119
+ | `chess-comparison.mp4` | the five in sequence, labelled |
120
+ | `chess-prompt.txt` | the prompt, verbatim |
121
+
122
+ The comparison runs matrix-only first, then the 32B, then the residuals — so the
123
+ reference sits in the middle and each half is read against it.
124
+
125
+ **You will not reproduce these files byte for byte, and that is normal.** Noticed
126
+ while testing something else, so take it as an observation rather than a study:
127
+ the result depends on the model of GPU the encoder runs on. Four cards, one
128
+ prompt, one seed, four different outputs — while two different RTX 3090s gave
129
+ byte-identical video. Not the architecture either: the 3060 and the 3090 are
130
+ both Ampere and disagree. Encoding the same prompt on two cards gives
131
+ conditioning that matches to a relative error of 7 × 10⁻⁷; eight denoising steps
132
+ turn that into a different piece of furniture. On one machine everything here is
133
+ reproducible to the bit, which is what makes the five-way comparison meaningful.
134
+
135
+ Watch her knee. The prompt asks for it three times, ending on a sentence of its
136
+ own: *"Her knee never stops bouncing."* It is the most redundant instruction in
137
+ the text, it is a continuous involuntary motion with no narrative purpose, and
138
+ it is the clearest single indicator that a projection carried what was written.
139
+ Then watch the cat, the laundry and the furniture, which are named once and
140
+ anchored nowhere — those move, and they are supposed to.
141
+
142
+ One thing none of the five gets right, including the 32B: she lifts a knight and
143
+ does not put it back on the same square. Object permanence through an occluding
144
+ hand on a grid of sixty-four identical squares is a limit of the video model, not
145
+ of the conditioning. It is listed here so nobody attributes it to the projection.
146
+
147
+ ## Files
148
+
149
+ | file | size | structure | encoder |
150
+ |---|---|---|---|
151
+ | `mmh3-4b-ClipProj-v3-mlp.safetensors` | 503 MB | residual only | Qwen3-VL-4B |
152
+ | `mmh3-8b-ClipProj-v3-mlp.safetensors` | 604 MB | residual only | Qwen3-VL-8B |
153
+ | `mmh3-4b-ClipProj-v3.safetensors` | 26 MB | matrix only | Qwen3-VL-4B |
154
+ | `mmh3-8b-ClipProj-v3.safetensors` | 42 MB | matrix only | Qwen3-VL-8B |
155
+
156
+ The residuals use a hidden width of 32 768 against 16 384 in v2, which is where
157
+ the extra download size comes from.
demo/chess-32b-reference.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:38406baa768a7979854ef4a42644b3487a05f9b9159e0e932688d06c17de431e
3
+ size 8946962
demo/chess-4b-mlp.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d9293133841cf511aedc5303f6118e85e8aa2cded59c63cd4092ddd13e7fc1b9
3
+ size 9211277
demo/chess-4b-ridge.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:621921784f34a945ff7358ba2c622202f24564eaaeee8e9a8e6827237a9bdf87
3
+ size 9505589
demo/chess-8b-mlp.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7d11c4ffb7388534909d20036b7b5be8b5444e9587e2302e0a1039cb25bb8a3f
3
+ size 8753222
demo/chess-8b-ridge.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2e37144afa55e56ada994290c81f6e2756ca6a15139e06feef246dcbac801ec5
3
+ size 8924076
demo/chess-comparison.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ada3f0a355a1f1f86a4587ff11fe94a9a403bd1872a84a27b11a480ae0a2ba3a
3
+ size 24874743
demo/chess-prompt.txt ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ subject_definitions:
2
+ S1 is a woman in her early thirties of striking natural beauty, with long dark hair worn loose and slightly undone by the wind, bare of make-up except for a trace of colour on the lips. She moves with unhurried grace, every gesture unselfconscious and fluid. She wears a white cotton sundress with thin straps, the hem riding high on her thigh as she sits, and no shoes. She has a low, warm voice.
3
+
4
+ summary:
5
+ Text to video with audio. One continuous shot, no cut. A woman playing white sits alone over a chess board on a sunlit terrace deep in thought, gathers her hair back off her face, her bare knee bouncing with impatience, then reaches out and touches one of her own pieces without committing to the move. She says one line out loud on camera.
6
+
7
+ retention_analysis:
8
+ N/A - no reference images, video or audio are supplied; everything is generated.
9
+
10
+ detailed_description:
11
+ Live action footage on a 50mm lens at f/2.8, shallow depth of field, blazing late morning Mediterranean sun from high on the left, brilliant white light, hard clean shadows, the sea a saturated blue behind her, vivid colour, fine natural grain, handheld camera with a small amount of drift, slowly pushing in on her across the shot. The target video lasts 8.00 seconds.
12
+ [Shot 1] The setting is a wide sunlit stone terrace above the sea, open to the sky, densely lived in: a long pergola overhead dripping with magenta bougainvillea throwing dappled shadow across the stone, terracotta pots of herbs and lemon trees crowding the low wall, a laden breakfast table pushed aside with coffee cups and a bowl of apricots and a scattered newspaper, striped cushions on a bench, a straw hat left on the wall, laundry snapping on a line at the far end, a cat stretched out asleep in the sun, whitewashed steps leading down out of frame towards the water, the open sea and a clear sky filling the whole background, no bare surface anywhere in frame. Medium shot of the woman sitting sideways in a cane chair pulled up to a low stone table, framed from mid-calf up and slightly from the side, one bare leg crossed over the other, the cotton hem high on her thigh, a wooden chess board on the table in front of her mid-game, she is playing white and the white pieces are on her side of the board nearest the camera, with exactly two captured black pawns set down beside the board on her right. Her face is turned three quarters towards the camera and her mouth is clearly visible throughout the shot, even while her eyes stay down on the board. Action: she sits absolutely still for a moment with her chin resting on the back of her left hand, her eyes moving over the board and nowhere else, her crossed knee bouncing lightly and continuously throughout the whole shot. The woman (S1) says out loud: <d>[English] No. Not that one. Not yet.</d> She speaks the words aloud on camera and her lips move clearly and naturally in sync with them, her jaw and mouth visibly forming the words. This is not a voiceover and not an internal thought; the sound comes from her mouth in the shot. Then she lifts her right hand, pushes her fingers back through her wind-blown hair from her forehead to the nape of her neck, gathers it loosely off her face and lets it fall again over her left shoulder, the movement slow and graceful and entirely absorbed. Her knee never stops bouncing. She reaches out with her right hand and takes hold of her own white knight, lifts it barely off its square, holds it suspended for a beat, and sets it back down exactly where it was. She never looks at the lens. Her eyes stay on the board from the first frame to the last.
13
+
14
+ overall_soundscape:
15
+ Diegetic sound only. Cicadas in the heat, a steady sea breeze moving the bougainvillea overhead and snapping the laundry on the line, the sea washing on rocks far below, gulls, the creak of the cane chair as she shifts, the faint knock of the wooden knight lifting and setting back down on the board, her voice close to the microphone as she speaks.
16
+
17
+ non_diegetic_music:
18
+ N/A - no score, no soundtrack, no musical arrangement at any point.
demo/generation-settings.json ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "scene": "chess terrace",
3
+ "prompt_file": "chess-prompt.txt",
4
+ "note": "All five renders share every setting below. Only the projection changes. The pipeline is deterministic: running the same configuration twice produces identical decoded video and audio streams, verified by MD5.",
5
+ "settings": {
6
+ "aspect_ratio": "16:9 (Widescreen)",
7
+ "megapixels": 0.8,
8
+ "vae": [
9
+ "minimax_h3_audio_vae_fp32.safetensors",
10
+ "minimax_h3_video_vae_int8_convrot.safetensors"
11
+ ],
12
+ "sampler": "res_multistep",
13
+ "steps": 8,
14
+ "scheduler": "simple",
15
+ "diffusion_model": "minimax_h3_fl2va_pruned_int8_convrot.safetensors",
16
+ "seed": 42,
17
+ "fps": 24.0,
18
+ "turbo_lora": "minimax_h3_fl2v_turbo_8step_v1.0_comfyui_bf16.safetensors",
19
+ "turbo_lora_strength": 1.0,
20
+ "upscale": "RTX Video Super Resolution 2.0x ULTRA",
21
+ "frames": 192,
22
+ "duration_seconds": 8.0,
23
+ "output_resolution": "2432x1344 after upscale"
24
+ },
25
+ "renders": [
26
+ {
27
+ "file": "chess-4b-ridge.mp4",
28
+ "text_encoder": "Qwen3-VL-4B",
29
+ "projection": "mmh3-4b-ClipProj-v3.safetensors"
30
+ },
31
+ {
32
+ "file": "chess-8b-ridge.mp4",
33
+ "text_encoder": "Qwen3-VL-8B",
34
+ "projection": "mmh3-8b-ClipProj-v3.safetensors"
35
+ },
36
+ {
37
+ "file": "chess-32b-reference.mp4",
38
+ "text_encoder": "Qwen3-VL-32B",
39
+ "projection": null
40
+ },
41
+ {
42
+ "file": "chess-8b-mlp.mp4",
43
+ "text_encoder": "Qwen3-VL-8B",
44
+ "projection": "mmh3-8b-ClipProj-v3-mlp.safetensors"
45
+ },
46
+ {
47
+ "file": "chess-4b-mlp.mp4",
48
+ "text_encoder": "Qwen3-VL-4B",
49
+ "projection": "mmh3-4b-ClipProj-v3-mlp.safetensors"
50
+ }
51
+ ],
52
+ "comparison": {
53
+ "file": "chess-comparison.mp4",
54
+ "order": [
55
+ "chess-4b-ridge.mp4",
56
+ "chess-8b-ridge.mp4",
57
+ "chess-32b-reference.mp4",
58
+ "chess-8b-mlp.mp4",
59
+ "chess-4b-mlp.mp4"
60
+ ],
61
+ "note": "Matrix-only first, then the 32B reference, then the residuals."
62
+ }
63
+ }
mmh3-4b-ClipProj-v3-mlp.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:feef06ef3b9aede3b1f3331b71eebbc873e21a867d73bcf40ea2c0b007270693
3
+ size 503434368
mmh3-4b-ClipProj-v3.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:06f147b31e12836974dd85322049b7235cb573c3943ba31b44fd2956db079c92
3
+ size 26256176
mmh3-8b-ClipProj-v3-mlp.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9304d6002db92eb1ac58dac917864b3f8b96bf0d65fd889e6f20de18413a091c
3
+ size 604103792
mmh3-8b-ClipProj-v3.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7bd5dab8605017b375a4d14e99e4bb0cea4953c4d68de05c104a13ba13808902
3
+ size 41990952