Add files using upload-large-folder tool
Browse files- FL2VA/SHA256SUMS +1 -0
- FL2VA/minimax-h3-fl2va-int8-lean-convrot-dynamic-k16-separate-qkv-quality21.safetensors +3 -0
- README.md +115 -33
- Ref2VA/SHA256SUMS +1 -0
- Ref2VA/minimax-h3-ref2va-int8-lean-convrot-dynamic-k16-separate-qkv-quality21.safetensors +3 -0
- patches/comfyui-minimax-h3-dynamic-time-separate-qkv.patch +220 -0
- reports/loadtest_dynamic_separate_fl2va.json +31 -0
- reports/loadtest_dynamic_separate_ref2va.json +31 -0
- reports/validation_dynamic_separate_fl2va.json +32 -0
- reports/validation_dynamic_separate_ref2va.json +32 -0
FL2VA/SHA256SUMS
CHANGED
|
@@ -1 +1,2 @@
|
|
| 1 |
ad0cf8b50ba92068b5a4477527d5da73412a22db99cded35b07195504e78cf6a minimax-h3-fl2va-int8-lean-convrot-table-k16-g4097-quality21.safetensors
|
|
|
|
|
|
| 1 |
ad0cf8b50ba92068b5a4477527d5da73412a22db99cded35b07195504e78cf6a minimax-h3-fl2va-int8-lean-convrot-table-k16-g4097-quality21.safetensors
|
| 2 |
+
495b9d565b17194196646ef39b0c04ac7dde4a8df695b103b3fcbe04b5da00a6 minimax-h3-fl2va-int8-lean-convrot-dynamic-k16-separate-qkv-quality21.safetensors
|
FL2VA/minimax-h3-fl2va-int8-lean-convrot-dynamic-k16-separate-qkv-quality21.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:495b9d565b17194196646ef39b0c04ac7dde4a8df695b103b3fcbe04b5da00a6
|
| 3 |
+
size 22547374816
|
README.md
CHANGED
|
@@ -24,10 +24,14 @@ of [`MiniMaxAI/MiniMax-H3`](https://huggingface.co/MiniMaxAI/MiniMax-H3).
|
|
| 24 |
The released checkpoints cover both the **FL2VA** and **Ref2VA** diffusion
|
| 25 |
transformers. Each is designed as a first 24 GiB GPU candidate while preserving
|
| 26 |
more of the original model than the corresponding official pruned INT8
|
| 27 |
-
checkpoint.
|
|
|
|
|
|
|
| 28 |
|
| 29 |
-
These are not official MiniMax or ComfyOrg checkpoints.
|
| 30 |
-
MiniMax-H3 `adaln_t_table` format and load in an unmodified
|
|
|
|
|
|
|
| 31 |
|
| 32 |
## Included files
|
| 33 |
|
|
@@ -35,10 +39,15 @@ MiniMax-H3 `adaln_t_table` format and load in an unmodified ComfyUI.
|
|
| 35 |
|---|---|
|
| 36 |
| [`FL2VA/minimax-h3-fl2va-int8-lean-convrot-table-k16-g4097-quality21.safetensors`](FL2VA/minimax-h3-fl2va-int8-lean-convrot-table-k16-g4097-quality21.safetensors) | Stock-compatible FL2VA diffusion transformer, 20.940 GiB |
|
| 37 |
| [`Ref2VA/minimax-h3-ref2va-int8-lean-convrot-table-k16-g4097-quality21.safetensors`](Ref2VA/minimax-h3-ref2va-int8-lean-convrot-table-k16-g4097-quality21.safetensors) | Stock-compatible Ref2VA diffusion transformer, 20.940 GiB |
|
|
|
|
|
|
|
| 38 |
| [`vae/minimax_h3_video_vae_fp16.safetensors`](vae/minimax_h3_video_vae_fp16.safetensors) | Stock ComfyUI MiniMax-H3 visual VAE, FP16, 4.850 GiB |
|
| 39 |
| [`vae/minimax_h3_audio_vae_fp32.safetensors`](vae/minimax_h3_audio_vae_fp32.safetensors) | Stock ComfyUI MiniMax-H3 stereo audio VAE, FP32, 0.564 GiB |
|
|
|
|
| 40 |
| [`reports/layer_policy.json`](reports/layer_policy.json) / [`layer_policy_ref2va.json`](reports/layer_policy_ref2va.json) | Exact per-layer precision policies |
|
| 41 |
| [`reports/validation.json`](reports/validation.json) / [`validation_ref2va.json`](reports/validation_ref2va.json) | Structural, numerical, and CPU-load results |
|
|
|
|
|
|
|
| 42 |
| [`reports/mm_quant_profile_fl2va.json`](reports/mm_quant_profile_fl2va.json) | Row-sampled reconstruction profile for all 200 main matrices |
|
| 43 |
| [`reports/mm_quant_profile_ref2va.json`](reports/mm_quant_profile_ref2va.json) | Matching Ref2VA reconstruction profile |
|
| 44 |
|
|
@@ -48,14 +57,12 @@ Diffusers scheduler, tokenizer, processor, or remote-code Python bundles as
|
|
| 48 |
separate downloads: a standard workflow needs one selected diffusion model,
|
| 49 |
the two included VAEs, and a compatible MiniMax-H3 text encoder.
|
| 50 |
|
| 51 |
-
##
|
| 52 |
|
| 53 |
The original FL2VA and Ref2VA Diffusers shards each contain 50 transformer
|
| 54 |
-
blocks with separate Q, K, and V tensors.
|
| 55 |
-
|
| 56 |
-
MiniMax-H3 support:
|
| 57 |
|
| 58 |
-
- packs attention rows as global `cat(Q, K, V)`;
|
| 59 |
- swaps the two `fc1` halves from `[value, gate]` to `[gate, value]`;
|
| 60 |
- stores 170 selected main attention/MLP matrices as INT8 ConvRot with group
|
| 61 |
size 256 and per-row FP32 scales;
|
|
@@ -63,14 +70,52 @@ MiniMax-H3 support:
|
|
| 63 |
- keeps the two-block token refiner, norms, Q/K norms, and condition projection
|
| 64 |
at their source precision;
|
| 65 |
- keeps patch projections and video/audio output heads in source FP32;
|
| 66 |
-
- evaluates the original FP32 time embedder offline and stores its smooth
|
| 67 |
-
one-dimensional output curve as a dense FP32 table with 4,097 rows and 16
|
| 68 |
-
coordinates;
|
| 69 |
- replaces only the redundant input width of the 51 large AdaLN projections,
|
| 70 |
while keeping every block and final AdaLN projection separate in FP32.
|
| 71 |
|
| 72 |
-
|
| 73 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
## Precision policy
|
| 76 |
|
|
@@ -92,7 +137,7 @@ considerably cheaper to retain in BF16 than QKV or `fc1`. The exact
|
|
| 92 |
machine-readable lists are in [`reports/layer_policy.json`](reports/layer_policy.json)
|
| 93 |
and [`reports/layer_policy_ref2va.json`](reports/layer_policy_ref2va.json).
|
| 94 |
|
| 95 |
-
##
|
| 96 |
|
| 97 |
The time embedder maps the current diffusion timestep/noise level to a
|
| 98 |
2,688-dimensional signal. Every transformer block turns this signal into its
|
|
@@ -126,12 +171,14 @@ generation quality.
|
|
| 126 |
|---|---:|---:|---|
|
| 127 |
| Official regular INT8 ConvRot | about 31.70 GiB | 200 INT8 | full AdaLN weights |
|
| 128 |
| Official pruned INT8 ConvRot | 19.530 GiB | 200 INT8 | FP16 rank-8 table |
|
| 129 |
-
| Each `quality21` release | 20.940 GiB | 170 INT8 + 30 BF16 | FP32 rank-16 table, 4,097 rows |
|
|
|
|
| 130 |
|
| 131 |
-
Each file is about 1.410 GiB (7.22%) larger than its official pruned INT8
|
| 132 |
-
counterpart. The
|
| 133 |
-
|
| 134 |
-
|
|
|
|
| 135 |
|
| 136 |
The full 200-matrix sampled reconstruction proxies produced:
|
| 137 |
|
|
@@ -149,21 +196,31 @@ benchmark.
|
|
| 149 |
## ComfyUI installation
|
| 150 |
|
| 151 |
1. Use a current ComfyUI checkout with MiniMax-H3 and native INT8 ConvRot
|
| 152 |
-
support.
|
| 153 |
-
commit `14b05228cef127ce529bc0c08660770d4af3e9a8`.
|
| 154 |
-
2.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
`ComfyUI/models/diffusion_models/`. FL2VA is for text/image first-last-frame
|
| 156 |
workflows; Ref2VA is for reference image/video/audio workflows.
|
| 157 |
-
|
| 158 |
-
|
| 159 |
matching MiniMax-H3 FL2VA or Ref2VA workflow. Supply a compatible text
|
| 160 |
encoder separately.
|
| 161 |
|
| 162 |
```text
|
| 163 |
ComfyUI/models/
|
| 164 |
|-- diffusion_models/
|
| 165 |
-
|
|
| 166 |
-
| `-- minimax-h3-ref2va-int8-lean-convrot-table-k16-g4097-quality21.safetensors
|
| 167 |
|-- vae/
|
| 168 |
| |-- minimax_h3_video_vae_fp16.safetensors
|
| 169 |
| `-- minimax_h3_audio_vae_fp32.safetensors
|
|
@@ -171,7 +228,8 @@ ComfyUI/models/
|
|
| 171 |
`-- <MiniMax-H3 text encoder from its separate repository>
|
| 172 |
```
|
| 173 |
|
| 174 |
-
No custom node
|
|
|
|
| 175 |
|
| 176 |
## Validation status
|
| 177 |
|
|
@@ -193,6 +251,25 @@ Completed:
|
|
| 193 |
- full CPU load through a clean, unmodified ComfyUI as
|
| 194 |
`ModelPatcher -> MiniMaxH3Model`, table mode `(4097, 16)`, for both diffusion
|
| 195 |
checkpoints;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
- both companion VAE files fully loaded on CPU through the same clean ComfyUI:
|
| 197 |
`MiniMaxH3VideoVAE` in FP16 with native internal tiling, and
|
| 198 |
`MiniMaxH3AudioVAE` in FP32 with 32 kHz stereo output;
|
|
@@ -209,17 +286,19 @@ Not completed at release time:
|
|
| 209 |
- end-to-end FL2VA or Ref2VA generation;
|
| 210 |
- same-seed visual/audio A/B against BF16 and official pruned variants.
|
| 211 |
|
| 212 |
-
Therefore, 20.940 GiB
|
| 213 |
-
|
| 214 |
-
|
|
|
|
| 215 |
|
| 216 |
## Conversion provenance
|
| 217 |
|
| 218 |
The converter reads the original top-level `transformer/` or `transformer_ref/`
|
| 219 |
Diffusers shards from MiniMaxAI directly; it does not create a 61+ GiB merged
|
| 220 |
BF16 intermediate. The nested task-specific transformer folders were not used
|
| 221 |
-
because their already-packed QKV is head-major
|
| 222 |
-
global `cat(Q,K,V)` packing
|
|
|
|
| 223 |
|
| 224 |
The official FL2VA and Ref2VA pruned files,
|
| 225 |
[`minimax_h3_fl2va_pruned_int8_convrot.safetensors`](https://huggingface.co/Comfy-Org/MiniMax-H3/blob/main/diffusion_models/minimax_h3_fl2va_pruned_int8_convrot.safetensors)
|
|
@@ -245,7 +324,10 @@ This release is intended for research and local ComfyUI inference. It is a
|
|
| 245 |
new, structurally validated operating point, not a completed quality benchmark.
|
| 246 |
Both variants were profiled separately. Their independently selected
|
| 247 |
quality21 BF16 islands are identical, but this does not establish perceptual
|
| 248 |
-
quality without end-to-end A/B generation.
|
|
|
|
|
|
|
|
|
|
| 249 |
|
| 250 |
## License
|
| 251 |
|
|
|
|
| 24 |
The released checkpoints cover both the **FL2VA** and **Ref2VA** diffusion
|
| 25 |
transformers. Each is designed as a first 24 GiB GPU candidate while preserving
|
| 26 |
more of the original model than the corresponding official pruned INT8
|
| 27 |
+
checkpoint. Two layouts are provided: a stock-compatible dense time-table
|
| 28 |
+
edition and a patched edition with the original FP32 time MLP and physically
|
| 29 |
+
separate Q, K, and V modules.
|
| 30 |
|
| 31 |
+
These are not official MiniMax or ComfyOrg checkpoints. The `table-*` files use
|
| 32 |
+
the standard MiniMax-H3 `adaln_t_table` format and load in an unmodified
|
| 33 |
+
ComfyUI. The `dynamic-*-separate-qkv-*` files require the small core patch
|
| 34 |
+
included in [`patches/`](patches/).
|
| 35 |
|
| 36 |
## Included files
|
| 37 |
|
|
|
|
| 39 |
|---|---|
|
| 40 |
| [`FL2VA/minimax-h3-fl2va-int8-lean-convrot-table-k16-g4097-quality21.safetensors`](FL2VA/minimax-h3-fl2va-int8-lean-convrot-table-k16-g4097-quality21.safetensors) | Stock-compatible FL2VA diffusion transformer, 20.940 GiB |
|
| 41 |
| [`Ref2VA/minimax-h3-ref2va-int8-lean-convrot-table-k16-g4097-quality21.safetensors`](Ref2VA/minimax-h3-ref2va-int8-lean-convrot-table-k16-g4097-quality21.safetensors) | Stock-compatible Ref2VA diffusion transformer, 20.940 GiB |
|
| 42 |
+
| [`FL2VA/minimax-h3-fl2va-int8-lean-convrot-dynamic-k16-separate-qkv-quality21.safetensors`](FL2VA/minimax-h3-fl2va-int8-lean-convrot-dynamic-k16-separate-qkv-quality21.safetensors) | Patched FL2VA: original FP32 time MLP and separate Q/K/V, 20.999 GiB |
|
| 43 |
+
| [`Ref2VA/minimax-h3-ref2va-int8-lean-convrot-dynamic-k16-separate-qkv-quality21.safetensors`](Ref2VA/minimax-h3-ref2va-int8-lean-convrot-dynamic-k16-separate-qkv-quality21.safetensors) | Patched Ref2VA: original FP32 time MLP and separate Q/K/V, 20.999 GiB |
|
| 44 |
| [`vae/minimax_h3_video_vae_fp16.safetensors`](vae/minimax_h3_video_vae_fp16.safetensors) | Stock ComfyUI MiniMax-H3 visual VAE, FP16, 4.850 GiB |
|
| 45 |
| [`vae/minimax_h3_audio_vae_fp32.safetensors`](vae/minimax_h3_audio_vae_fp32.safetensors) | Stock ComfyUI MiniMax-H3 stereo audio VAE, FP32, 0.564 GiB |
|
| 46 |
+
| [`patches/comfyui-minimax-h3-dynamic-time-separate-qkv.patch`](patches/comfyui-minimax-h3-dynamic-time-separate-qkv.patch) | Required ComfyUI core patch for the two dynamic/separate files |
|
| 47 |
| [`reports/layer_policy.json`](reports/layer_policy.json) / [`layer_policy_ref2va.json`](reports/layer_policy_ref2va.json) | Exact per-layer precision policies |
|
| 48 |
| [`reports/validation.json`](reports/validation.json) / [`validation_ref2va.json`](reports/validation_ref2va.json) | Structural, numerical, and CPU-load results |
|
| 49 |
+
| [`reports/validation_dynamic_separate_fl2va.json`](reports/validation_dynamic_separate_fl2va.json) / [`validation_dynamic_separate_ref2va.json`](reports/validation_dynamic_separate_ref2va.json) | Bitwise split and dynamic-time validation for the patched files |
|
| 50 |
+
| [`reports/loadtest_dynamic_separate_fl2va.json`](reports/loadtest_dynamic_separate_fl2va.json) / [`loadtest_dynamic_separate_ref2va.json`](reports/loadtest_dynamic_separate_ref2va.json) | Full patched-Comfy CPU loader results |
|
| 51 |
| [`reports/mm_quant_profile_fl2va.json`](reports/mm_quant_profile_fl2va.json) | Row-sampled reconstruction profile for all 200 main matrices |
|
| 52 |
| [`reports/mm_quant_profile_ref2va.json`](reports/mm_quant_profile_ref2va.json) | Matching Ref2VA reconstruction profile |
|
| 53 |
|
|
|
|
| 57 |
separate downloads: a standard workflow needs one selected diffusion model,
|
| 58 |
the two included VAEs, and a compatible MiniMax-H3 text encoder.
|
| 59 |
|
| 60 |
+
## Shared quantization policy
|
| 61 |
|
| 62 |
The original FL2VA and Ref2VA Diffusers shards each contain 50 transformer
|
| 63 |
+
blocks with separate Q, K, and V tensors. Both release layouts use the same
|
| 64 |
+
quality21 precision policy:
|
|
|
|
| 65 |
|
|
|
|
| 66 |
- swaps the two `fc1` halves from `[value, gate]` to `[gate, value]`;
|
| 67 |
- stores 170 selected main attention/MLP matrices as INT8 ConvRot with group
|
| 68 |
size 256 and per-row FP32 scales;
|
|
|
|
| 70 |
- keeps the two-block token refiner, norms, Q/K norms, and condition projection
|
| 71 |
at their source precision;
|
| 72 |
- keeps patch projections and video/audio output heads in source FP32;
|
|
|
|
|
|
|
|
|
|
| 73 |
- replaces only the redundant input width of the 51 large AdaLN projections,
|
| 74 |
while keeping every block and final AdaLN projection separate in FP32.
|
| 75 |
|
| 76 |
+
The stock-compatible files additionally pack attention rows as global
|
| 77 |
+
`cat(Q,K,V)` and evaluate the original FP32 time embedder offline into a dense
|
| 78 |
+
FP32 table with 4,097 rows and 16 coordinates. Each stock-compatible checkpoint
|
| 79 |
+
contains 872 tensor entries: 170 INT8 weights, 170 quantization configuration
|
| 80 |
+
blobs, 250 BF16 entries, and 282 FP32 entries.
|
| 81 |
+
|
| 82 |
+
## Patched dynamic-time, separate-QKV edition
|
| 83 |
+
|
| 84 |
+
The two `dynamic-k16-separate-qkv` files retain the original four FP32 time-MLP
|
| 85 |
+
tensors:
|
| 86 |
+
|
| 87 |
+
```text
|
| 88 |
+
256 -> 5,376 -> 2,688
|
| 89 |
+
full_t = SiLU(original_time_embedder(t))
|
| 90 |
+
coords = (full_t - mean) @ basis[2,688 x 16]
|
| 91 |
+
AdaLN_i(t) = separate_linear_i(coords)
|
| 92 |
+
```
|
| 93 |
+
|
| 94 |
+
The 16-dimensional dynamic basis compresses only the shared input subspace of
|
| 95 |
+
the 51 very wide AdaLN projections. It does not replace the time MLP with an
|
| 96 |
+
interpolation table, and it does not merge the 51 per-block/final AdaLN layers.
|
| 97 |
+
|
| 98 |
+
Q, K, and V are also separate modules all the way through loading and forward:
|
| 99 |
+
|
| 100 |
+
- 50 main blocks contain independent INT8 ConvRot `q_proj`, `k_proj`, and
|
| 101 |
+
`v_proj` weights, scales, and descriptors;
|
| 102 |
+
- both token-refiner blocks contain independent BF16 Q, K, and V projections;
|
| 103 |
+
- the checkpoint contains no `qkv_proj` key;
|
| 104 |
+
- the patch performs three projection calls rather than concatenating the
|
| 105 |
+
tensors at load time.
|
| 106 |
+
|
| 107 |
+
This changes storage structure, not the quantization values. All 156 resulting
|
| 108 |
+
Q/K/V projection tensors are bit-identical to the corresponding contiguous
|
| 109 |
+
thirds of the validated fused release. Per-row scales were already independent,
|
| 110 |
+
so splitting QKV alone is not claimed as a quality improvement. It may be
|
| 111 |
+
slower than fused QKV because the current ConvRot path can rotate the same input
|
| 112 |
+
three times and launches three projections. GPU speed and peak VRAM have not
|
| 113 |
+
yet been measured.
|
| 114 |
+
|
| 115 |
+
Each patched checkpoint contains 1,181 tensor entries and 270 INT8 ConvRot
|
| 116 |
+
modules. The total quantized QKV payload is unchanged; the approximately
|
| 117 |
+
0.059 GiB size increase comes primarily from restoring the original FP32 time
|
| 118 |
+
MLP.
|
| 119 |
|
| 120 |
## Precision policy
|
| 121 |
|
|
|
|
| 137 |
machine-readable lists are in [`reports/layer_policy.json`](reports/layer_policy.json)
|
| 138 |
and [`reports/layer_policy_ref2va.json`](reports/layer_policy_ref2va.json).
|
| 139 |
|
| 140 |
+
## Stock-compatible time conditioning
|
| 141 |
|
| 142 |
The time embedder maps the current diffusion timestep/noise level to a
|
| 143 |
2,688-dimensional signal. Every transformer block turns this signal into its
|
|
|
|
| 171 |
|---|---:|---:|---|
|
| 172 |
| Official regular INT8 ConvRot | about 31.70 GiB | 200 INT8 | full AdaLN weights |
|
| 173 |
| Official pruned INT8 ConvRot | 19.530 GiB | 200 INT8 | FP16 rank-8 table |
|
| 174 |
+
| Each stock `table-k16 quality21` release | 20.940 GiB | 170 INT8 + 30 BF16 | FP32 rank-16 table, 4,097 rows |
|
| 175 |
+
| Each patched `dynamic-k16 separate-QKV quality21` release | 20.999 GiB | Same 170 semantic main matrices; 270 physical INT8 modules | Original FP32 time MLP + FP32 dynamic rank-16 basis |
|
| 176 |
|
| 177 |
+
Each stock file is about 1.410 GiB (7.22%) larger than its official pruned INT8
|
| 178 |
+
counterpart. The patched edition adds about 0.059 GiB over the stock quality21
|
| 179 |
+
file by restoring the original FP32 time MLP. The remaining added space is
|
| 180 |
+
intentionally spent on a higher-precision time representation and a BF16 island
|
| 181 |
+
for the most sensitive main matrices.
|
| 182 |
|
| 183 |
The full 200-matrix sampled reconstruction proxies produced:
|
| 184 |
|
|
|
|
| 196 |
## ComfyUI installation
|
| 197 |
|
| 198 |
1. Use a current ComfyUI checkout with MiniMax-H3 and native INT8 ConvRot
|
| 199 |
+
support. The stock table files were CPU-load-tested on a clean, unmodified
|
| 200 |
+
ComfyUI commit `14b05228cef127ce529bc0c08660770d4af3e9a8`.
|
| 201 |
+
2. If using a `dynamic-k16-separate-qkv` file, apply the included patch from the
|
| 202 |
+
root of that ComfyUI checkout. This is not required for the `table-k16` files:
|
| 203 |
+
|
| 204 |
+
```powershell
|
| 205 |
+
git apply --check C:\path\to\comfyui-minimax-h3-dynamic-time-separate-qkv.patch
|
| 206 |
+
git apply C:\path\to\comfyui-minimax-h3-dynamic-time-separate-qkv.patch
|
| 207 |
+
```
|
| 208 |
+
|
| 209 |
+
The patch was verified with `git apply --check` against commit `14b05228`.
|
| 210 |
+
It updates MiniMax-H3 model detection/runtime and makes partial-offload
|
| 211 |
+
bookkeeping tolerate packed INT8 layers without a regular `.weight` field.
|
| 212 |
+
3. Place the FL2VA and/or Ref2VA safetensors file in
|
| 213 |
`ComfyUI/models/diffusion_models/`. FL2VA is for text/image first-last-frame
|
| 214 |
workflows; Ref2VA is for reference image/video/audio workflows.
|
| 215 |
+
4. Place both files from `vae/` in `ComfyUI/models/vae/`.
|
| 216 |
+
5. Load one diffusion checkpoint and both VAEs with the standard loaders in the
|
| 217 |
matching MiniMax-H3 FL2VA or Ref2VA workflow. Supply a compatible text
|
| 218 |
encoder separately.
|
| 219 |
|
| 220 |
```text
|
| 221 |
ComfyUI/models/
|
| 222 |
|-- diffusion_models/
|
| 223 |
+
| `-- <one FL2VA or Ref2VA table/dynamic-separate checkpoint>
|
|
|
|
| 224 |
|-- vae/
|
| 225 |
| |-- minimax_h3_video_vae_fp16.safetensors
|
| 226 |
| `-- minimax_h3_audio_vae_fp32.safetensors
|
|
|
|
| 228 |
`-- <MiniMax-H3 text encoder from its separate repository>
|
| 229 |
```
|
| 230 |
|
| 231 |
+
No custom node is required. Only the two explicitly named dynamic/separate
|
| 232 |
+
checkpoints require the included core patch.
|
| 233 |
|
| 234 |
## Validation status
|
| 235 |
|
|
|
|
| 251 |
- full CPU load through a clean, unmodified ComfyUI as
|
| 252 |
`ModelPatcher -> MiniMaxH3Model`, table mode `(4097, 16)`, for both diffusion
|
| 253 |
checkpoints;
|
| 254 |
+
- patched editions: 1,181 keys, 270 INT8 ConvRot descriptors at group size 256,
|
| 255 |
+
zero fused-QKV keys, and all 156 Q/K/V projection tensors bitwise equal to
|
| 256 |
+
the corresponding thirds of the stock quality21 checkpoints;
|
| 257 |
+
- patched editions: all 719 common non-QKV tensors bitwise equal to their stock
|
| 258 |
+
quality21 counterparts and all four FP32 time-MLP tensors bitwise equal to
|
| 259 |
+
the selected raw HF variant;
|
| 260 |
+
- patched FL2VA dynamic coordinates reproduce the released 4,097-row table
|
| 261 |
+
bit-for-bit; the independently reconstructed Ref2VA dynamic path differs by
|
| 262 |
+
`2.89e-7` relative (`1.31e-6` maximum absolute coordinate error);
|
| 263 |
+
- patched AdaLN output relative errors on 19 timesteps are `2.85e-7` to
|
| 264 |
+
`3.32e-7` for FL2VA and `2.96e-7` to `3.31e-7` for Ref2VA across blocks
|
| 265 |
+
0, 24, 49, and the final layer;
|
| 266 |
+
- both patched files fully load on CPU through the patched ComfyUI as
|
| 267 |
+
`MiniMaxH3Model`, with dynamic basis enabled, table mode disabled, and
|
| 268 |
+
independent Q/K/V modules in both the main stack and token refiner;
|
| 269 |
+
- patched SHA-256: FL2VA
|
| 270 |
+
`495b9d565b17194196646ef39b0c04ac7dde4a8df695b103b3fcbe04b5da00a6`;
|
| 271 |
+
Ref2VA
|
| 272 |
+
`82d16de12bee9d006c83c80b8843f2bb7e05cd25320724113ca7d128c6be6a04`;
|
| 273 |
- both companion VAE files fully loaded on CPU through the same clean ComfyUI:
|
| 274 |
`MiniMaxH3VideoVAE` in FP16 with native internal tiling, and
|
| 275 |
`MiniMaxH3AudioVAE` in FP32 with 32 kHz stereo output;
|
|
|
|
| 286 |
- end-to-end FL2VA or Ref2VA generation;
|
| 287 |
- same-seed visual/audio A/B against BF16 and official pruned variants.
|
| 288 |
|
| 289 |
+
Therefore, 20.940 GiB (stock table) or 20.999 GiB (patched dynamic/separate) is
|
| 290 |
+
the on-disk tensor payload, not a promise that every workflow will remain under
|
| 291 |
+
24 GiB. Activations, runtime buffers, resolution, frame count, batch size, and
|
| 292 |
+
offloading policy determine peak VRAM.
|
| 293 |
|
| 294 |
## Conversion provenance
|
| 295 |
|
| 296 |
The converter reads the original top-level `transformer/` or `transformer_ref/`
|
| 297 |
Diffusers shards from MiniMaxAI directly; it does not create a 61+ GiB merged
|
| 298 |
BF16 intermediate. The nested task-specific transformer folders were not used
|
| 299 |
+
because their already-packed QKV is head-major. The stock-compatible edition
|
| 300 |
+
uses global `cat(Q,K,V)` packing expected by unmodified ComfyUI; the patched
|
| 301 |
+
edition retains the top-level source's separate Q, K, and V ordering.
|
| 302 |
|
| 303 |
The official FL2VA and Ref2VA pruned files,
|
| 304 |
[`minimax_h3_fl2va_pruned_int8_convrot.safetensors`](https://huggingface.co/Comfy-Org/MiniMax-H3/blob/main/diffusion_models/minimax_h3_fl2va_pruned_int8_convrot.safetensors)
|
|
|
|
| 324 |
new, structurally validated operating point, not a completed quality benchmark.
|
| 325 |
Both variants were profiled separately. Their independently selected
|
| 326 |
quality21 BF16 islands are identical, but this does not establish perceptual
|
| 327 |
+
quality without end-to-end A/B generation. The separate-QKV runtime also trades
|
| 328 |
+
the fused projection for three independent ConvRot calls, so it should be
|
| 329 |
+
treated as an experimental quality/architecture variant until GPU speed and
|
| 330 |
+
VRAM are measured.
|
| 331 |
|
| 332 |
## License
|
| 333 |
|
Ref2VA/SHA256SUMS
CHANGED
|
@@ -1 +1,2 @@
|
|
| 1 |
f4b2a8b4a53446f5d696a39deb693b09ac7ce37013793a8b19340644e38b154f minimax-h3-ref2va-int8-lean-convrot-table-k16-g4097-quality21.safetensors
|
|
|
|
|
|
| 1 |
f4b2a8b4a53446f5d696a39deb693b09ac7ce37013793a8b19340644e38b154f minimax-h3-ref2va-int8-lean-convrot-table-k16-g4097-quality21.safetensors
|
| 2 |
+
82d16de12bee9d006c83c80b8843f2bb7e05cd25320724113ca7d128c6be6a04 minimax-h3-ref2va-int8-lean-convrot-dynamic-k16-separate-qkv-quality21.safetensors
|
Ref2VA/minimax-h3-ref2va-int8-lean-convrot-dynamic-k16-separate-qkv-quality21.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:82d16de12bee9d006c83c80b8843f2bb7e05cd25320724113ca7d128c6be6a04
|
| 3 |
+
size 22547376168
|
patches/comfyui-minimax-h3-dynamic-time-separate-qkv.patch
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
diff --git a/comfy/ldm/minimax/model.py b/comfy/ldm/minimax/model.py
|
| 2 |
+
--- a/comfy/ldm/minimax/model.py
|
| 3 |
+
+++ b/comfy/ldm/minimax/model.py
|
| 4 |
+
@@ -143,19 +143,29 @@ def rope_rotation_table(angles, dtype):
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class Attention(nn.Module):
|
| 8 |
+
- def __init__(self, hidden, heads, head_dim, eps, dtype=None, device=None, operations=None):
|
| 9 |
+
+ def __init__(self, hidden, heads, head_dim, eps, separate_qkv=False,
|
| 10 |
+
+ dtype=None, device=None, operations=None):
|
| 11 |
+
super().__init__()
|
| 12 |
+
self.heads = heads
|
| 13 |
+
self.head_dim = head_dim
|
| 14 |
+
+ self.separate_qkv = separate_qkv
|
| 15 |
+
inner = heads * head_dim
|
| 16 |
+
- self.qkv_proj = operations.Linear(hidden, inner * 3, bias=False, dtype=dtype, device=device)
|
| 17 |
+
+ if separate_qkv:
|
| 18 |
+
+ self.q_proj = operations.Linear(hidden, inner, bias=False, dtype=dtype, device=device)
|
| 19 |
+
+ self.k_proj = operations.Linear(hidden, inner, bias=False, dtype=dtype, device=device)
|
| 20 |
+
+ self.v_proj = operations.Linear(hidden, inner, bias=False, dtype=dtype, device=device)
|
| 21 |
+
+ else:
|
| 22 |
+
+ self.qkv_proj = operations.Linear(hidden, inner * 3, bias=False, dtype=dtype, device=device)
|
| 23 |
+
self.q_norm = operations.RMSNorm(head_dim, eps=eps, dtype=dtype, device=device)
|
| 24 |
+
self.k_norm = operations.RMSNorm(head_dim, eps=eps, dtype=dtype, device=device)
|
| 25 |
+
self.out_proj = operations.Linear(inner, hidden, bias=False, dtype=dtype, device=device)
|
| 26 |
+
|
| 27 |
+
def forward(self, x, rope_freqs=None, transformer_options={}):
|
| 28 |
+
s = x.shape[0]
|
| 29 |
+
- q, k, v = self.qkv_proj(x).split(self.heads * self.head_dim, dim=-1)
|
| 30 |
+
+ if self.separate_qkv:
|
| 31 |
+
+ q, k, v = self.q_proj(x), self.k_proj(x), self.v_proj(x)
|
| 32 |
+
+ else:
|
| 33 |
+
+ q, k, v = self.qkv_proj(x).split(self.heads * self.head_dim, dim=-1)
|
| 34 |
+
v = v.view(s, self.heads, self.head_dim)
|
| 35 |
+
if rope_freqs is not None:
|
| 36 |
+
# fused per-head RMSNorm + partial split-half rope, in place on the qkv buffer
|
| 37 |
+
@@ -224,11 +234,13 @@ def _mod_gate(x, gate, other, segments):
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class RefinerBlock(nn.Module):
|
| 41 |
+
- def __init__(self, hidden, heads, head_dim, ffn, eps, qk_eps, dtype=None, device=None, operations=None):
|
| 42 |
+
+ def __init__(self, hidden, heads, head_dim, ffn, eps, qk_eps, separate_qkv=False,
|
| 43 |
+
+ dtype=None, device=None, operations=None):
|
| 44 |
+
super().__init__()
|
| 45 |
+
self.norm1 = operations.RMSNorm(hidden, eps=eps, dtype=dtype, device=device)
|
| 46 |
+
self.norm2 = operations.RMSNorm(hidden, eps=eps, dtype=dtype, device=device)
|
| 47 |
+
- self.attn = Attention(hidden, heads, head_dim, qk_eps, dtype=dtype, device=device, operations=operations)
|
| 48 |
+
+ self.attn = Attention(hidden, heads, head_dim, qk_eps, separate_qkv=separate_qkv,
|
| 49 |
+
+ dtype=dtype, device=device, operations=operations)
|
| 50 |
+
self.mlp = MLP(hidden, ffn, dtype=dtype, device=device, operations=operations)
|
| 51 |
+
|
| 52 |
+
def forward(self, x, transformer_options={}):
|
| 53 |
+
@@ -239,10 +251,11 @@ class RefinerBlock(nn.Module):
|
| 54 |
+
|
| 55 |
+
class TokenRefiner(nn.Module):
|
| 56 |
+
def __init__(self, num_layers, hidden, heads, head_dim, ffn, eps, qk_eps, final_eps,
|
| 57 |
+
- dtype=None, device=None, operations=None):
|
| 58 |
+
+ separate_qkv=False, dtype=None, device=None, operations=None):
|
| 59 |
+
super().__init__()
|
| 60 |
+
self.blocks = nn.ModuleList([
|
| 61 |
+
- RefinerBlock(hidden, heads, head_dim, ffn, eps, qk_eps, dtype=dtype, device=device, operations=operations)
|
| 62 |
+
+ RefinerBlock(hidden, heads, head_dim, ffn, eps, qk_eps, separate_qkv=separate_qkv,
|
| 63 |
+
+ dtype=dtype, device=device, operations=operations)
|
| 64 |
+
for _ in range(num_layers)])
|
| 65 |
+
self.final_norm = operations.RMSNorm(hidden, eps=final_eps, dtype=dtype, device=device)
|
| 66 |
+
|
| 67 |
+
@@ -254,11 +267,13 @@ class TokenRefiner(nn.Module):
|
| 68 |
+
|
| 69 |
+
class DiTBlock(nn.Module):
|
| 70 |
+
def __init__(self, hidden, heads, head_dim, ffn, t_dim, eps, qk_eps,
|
| 71 |
+
- apply_silu=True, adaln_dtype=None, dtype=None, device=None, operations=None):
|
| 72 |
+
+ apply_silu=True, adaln_dtype=None, separate_qkv=False,
|
| 73 |
+
+ dtype=None, device=None, operations=None):
|
| 74 |
+
super().__init__()
|
| 75 |
+
self.norm1 = operations.RMSNorm(hidden, eps=eps, dtype=dtype, device=device)
|
| 76 |
+
self.norm2 = operations.RMSNorm(hidden, eps=eps, dtype=dtype, device=device)
|
| 77 |
+
- self.attn = Attention(hidden, heads, head_dim, qk_eps, dtype=dtype, device=device, operations=operations)
|
| 78 |
+
+ self.attn = Attention(hidden, heads, head_dim, qk_eps, separate_qkv=separate_qkv,
|
| 79 |
+
+ dtype=dtype, device=device, operations=operations)
|
| 80 |
+
self.mlp = MLP(hidden, ffn, dtype=dtype, device=device, operations=operations)
|
| 81 |
+
self.adaln_proj = AdalnProj(t_dim, hidden, 6, 3, apply_silu=apply_silu,
|
| 82 |
+
dtype=adaln_dtype if adaln_dtype is not None else dtype,
|
| 83 |
+
@@ -416,7 +431,7 @@ class MiniMaxH3Model(nn.Module):
|
| 84 |
+
timestep_input_dim=256, time_embed_hidden_size=5376, time_embed_dim=2688,
|
| 85 |
+
rope_inv_freq_len=16, norm_eps=1e-5, qk_norm_eps=1e-5, final_norm_eps=1e-5,
|
| 86 |
+
sigma_shift_video=12.0, sigma_shift_audio=3.0,
|
| 87 |
+
- adaln_curve_grid=None,
|
| 88 |
+
+ adaln_curve_grid=None, adaln_curve_basis_dim=None, separate_qkv=False,
|
| 89 |
+
image_model=None, dtype=None, device=None, operations=None, **kwargs):
|
| 90 |
+
super().__init__()
|
| 91 |
+
self.dtype = dtype
|
| 92 |
+
@@ -426,30 +441,43 @@ class MiniMaxH3Model(nn.Module):
|
| 93 |
+
self.audio_latents_dim = audio_latents_dim
|
| 94 |
+
self.sigma_shift_video = sigma_shift_video
|
| 95 |
+
self.sigma_shift_audio = sigma_shift_audio
|
| 96 |
+
- self.use_adaln_curves = adaln_curve_grid is not None
|
| 97 |
+
- # curve-form checkpoints replace the time embedder and full-width adaln weights with a small shared basis of the time-embedding curve
|
| 98 |
+
+ self.separate_qkv = separate_qkv
|
| 99 |
+
+ if adaln_curve_grid is not None and adaln_curve_basis_dim is not None:
|
| 100 |
+
+ raise ValueError("adaln table and dynamic basis modes are mutually exclusive")
|
| 101 |
+
+ self.use_adaln_table = adaln_curve_grid is not None
|
| 102 |
+
+ self.use_adaln_basis = adaln_curve_basis_dim is not None
|
| 103 |
+
+ self.use_adaln_curves = self.use_adaln_table or self.use_adaln_basis
|
| 104 |
+
+ # Curve checkpoints replace full-width adaLN weights with projections
|
| 105 |
+
+ # over a small shared basis. Table mode also replaces the time MLP;
|
| 106 |
+
+ # dynamic-basis mode retains it and projects its exact fp32 output.
|
| 107 |
+
curve = {"apply_silu": not self.use_adaln_curves,
|
| 108 |
+
"adaln_dtype": torch.float32 if self.use_adaln_curves else dtype}
|
| 109 |
+
+ adaln_dim = adaln_curve_basis_dim if self.use_adaln_basis else time_embed_dim
|
| 110 |
+
video_patch_dim = latents_dim * self.patch_size[0] * self.patch_size[1] * self.patch_size[2]
|
| 111 |
+
|
| 112 |
+
self.video_patch_proj = operations.Linear(video_patch_dim, hidden_size, bias=True, dtype=torch.float32, device=device)
|
| 113 |
+
self.audio_patch_proj = operations.Linear(audio_latents_dim, hidden_size, bias=True, dtype=torch.float32, device=device)
|
| 114 |
+
self.condition_proj = operations.Linear(text_dim, hidden_size, bias=True, dtype=dtype, device=device)
|
| 115 |
+
- if self.use_adaln_curves:
|
| 116 |
+
+ if self.use_adaln_table:
|
| 117 |
+
self.register_buffer("adaln_t_table", torch.empty(adaln_curve_grid, time_embed_dim, dtype=torch.float32))
|
| 118 |
+
else:
|
| 119 |
+
self.time_embedder = TimeEmbedder(timestep_input_dim, time_embed_hidden_size, time_embed_dim,
|
| 120 |
+
dtype=torch.float32, device=device, operations=operations)
|
| 121 |
+
+ if self.use_adaln_basis:
|
| 122 |
+
+ self.register_buffer("adaln_curve_basis", torch.empty(time_embed_dim, adaln_curve_basis_dim, dtype=torch.float32))
|
| 123 |
+
+ self.register_buffer("adaln_curve_mean", torch.empty(time_embed_dim, dtype=torch.float32))
|
| 124 |
+
self.rope = nn.Module()
|
| 125 |
+
self.rope.register_buffer("inv_freq", torch.empty(rope_inv_freq_len, dtype=torch.float32))
|
| 126 |
+
self.token_refiner = TokenRefiner(token_refiner_num_layers, hidden_size, num_attention_heads,
|
| 127 |
+
attention_head_dim, ffn_hidden_size, norm_eps, qk_norm_eps,
|
| 128 |
+
- final_norm_eps, dtype=dtype, device=device, operations=operations)
|
| 129 |
+
+ final_norm_eps, separate_qkv=separate_qkv,
|
| 130 |
+
+ dtype=dtype, device=device, operations=operations)
|
| 131 |
+
self.blocks = nn.ModuleList([
|
| 132 |
+
DiTBlock(hidden_size, num_attention_heads, attention_head_dim, ffn_hidden_size,
|
| 133 |
+
- time_embed_dim, norm_eps, qk_norm_eps, **curve, dtype=dtype, device=device, operations=operations)
|
| 134 |
+
+ adaln_dim, norm_eps, qk_norm_eps, separate_qkv=separate_qkv,
|
| 135 |
+
+ **curve, dtype=dtype, device=device, operations=operations)
|
| 136 |
+
for _ in range(num_layers)])
|
| 137 |
+
- self.final_layer = FinalLayer(hidden_size, time_embed_dim, video_patch_dim, audio_latents_dim,
|
| 138 |
+
+ self.final_layer = FinalLayer(hidden_size, adaln_dim, video_patch_dim, audio_latents_dim,
|
| 139 |
+
final_norm_eps, **curve, dtype=dtype, device=device, operations=operations)
|
| 140 |
+
|
| 141 |
+
def preprocess_text_embeds(self, text_states):
|
| 142 |
+
@@ -599,12 +627,19 @@ class MiniMaxH3Model(nn.Module):
|
| 143 |
+
aoff += n
|
| 144 |
+
|
| 145 |
+
t_vals = torch.tensor(unique_t, dtype=torch.float32, device=device)
|
| 146 |
+
- if self.use_adaln_curves:
|
| 147 |
+
+ if self.use_adaln_table:
|
| 148 |
+
# adaln projections consume interpolated coordinates of the time-embedding curve
|
| 149 |
+
table = comfy.model_management.cast_to(self.adaln_t_table, device=device)
|
| 150 |
+
pos = t_vals.clamp(0.0, 1.0) * (table.shape[0] - 1) # t in [0,1] -> fractional grid index, out-of-range t clamps to the curve ends
|
| 151 |
+
i0 = pos.floor().long().clamp(max=table.shape[0] - 2) # lower grid row, max-clamp keeps t=1.0 on the last interval instead of reading past the table
|
| 152 |
+
t_emb = torch.lerp(table[i0], table[i0 + 1], (pos - i0).unsqueeze(1)) # blend the two rows by the fractional part
|
| 153 |
+
+ elif self.use_adaln_basis:
|
| 154 |
+
+ # Preserve the original fp32 time MLP and remove only the redundant
|
| 155 |
+
+ # directions that its smooth one-dimensional output curve never uses.
|
| 156 |
+
+ full_t_emb = nn.functional.silu(self.time_embedder(t_vals))
|
| 157 |
+
+ basis = comfy.model_management.cast_to(self.adaln_curve_basis, device=device)
|
| 158 |
+
+ mean = comfy.model_management.cast_to(self.adaln_curve_mean, device=device)
|
| 159 |
+
+ t_emb = (full_t_emb - mean) @ basis
|
| 160 |
+
else:
|
| 161 |
+
t_emb = self.time_embedder(t_vals).to(dtype)
|
| 162 |
+
|
| 163 |
+
diff --git a/comfy/model_detection.py b/comfy/model_detection.py
|
| 164 |
+
--- a/comfy/model_detection.py
|
| 165 |
+
+++ b/comfy/model_detection.py
|
| 166 |
+
@@ -368,11 +368,23 @@ def detect_unet_config(state_dict, key_prefix, metadata=None):
|
| 167 |
+
dit_config["latents_dim"] = state_dict['{}final_layer.video_out.weight'.format(key_prefix)].shape[0] // 4 # patch 1x2x2
|
| 168 |
+
dit_config["audio_latents_dim"] = state_dict['{}final_layer.audio_out.weight'.format(key_prefix)].shape[0]
|
| 169 |
+
dit_config["attention_head_dim"] = state_dict['{}blocks.0.attn.q_norm.weight'.format(key_prefix)].shape[0]
|
| 170 |
+
- qkv = state_dict['{}blocks.0.attn.qkv_proj.weight'.format(key_prefix)]
|
| 171 |
+
- dit_config["num_attention_heads"] = qkv.shape[0] // (3 * dit_config["attention_head_dim"])
|
| 172 |
+
+ qkv_key = '{}blocks.0.attn.qkv_proj.weight'.format(key_prefix)
|
| 173 |
+
+ separate_keys = tuple('{}blocks.0.attn.{}_proj.weight'.format(key_prefix, name) for name in ('q', 'k', 'v'))
|
| 174 |
+
+ if qkv_key in state_dict_keys:
|
| 175 |
+
+ qkv = state_dict[qkv_key]
|
| 176 |
+
+ dit_config["num_attention_heads"] = qkv.shape[0] // (3 * dit_config["attention_head_dim"])
|
| 177 |
+
+ elif all(k in state_dict_keys for k in separate_keys):
|
| 178 |
+
+ q, k, v = (state_dict[name] for name in separate_keys)
|
| 179 |
+
+ if q.shape != k.shape or q.shape != v.shape:
|
| 180 |
+
+ raise ValueError("MiniMax H3 separate Q/K/V projection shapes do not match")
|
| 181 |
+
+ dit_config["num_attention_heads"] = q.shape[0] // dit_config["attention_head_dim"]
|
| 182 |
+
+ dit_config["separate_qkv"] = True
|
| 183 |
+
+ else:
|
| 184 |
+
+ raise ValueError("MiniMax H3 checkpoint has neither fused nor separate Q/K/V projections")
|
| 185 |
+
dit_config["ffn_hidden_size"] = state_dict['{}blocks.0.mlp.fc1.weight'.format(key_prefix)].shape[0] // 2
|
| 186 |
+
dit_config["text_dim"] = state_dict['{}condition_proj.weight'.format(key_prefix)].shape[1]
|
| 187 |
+
table_key = '{}adaln_t_table'.format(key_prefix)
|
| 188 |
+
+ basis_key = '{}adaln_curve_basis'.format(key_prefix)
|
| 189 |
+
if table_key in state_dict_keys:
|
| 190 |
+
# adaln shipped over a precomputed curve basis: the adaln linears span a small shared basis of the time-embedding curve (no time embedder)
|
| 191 |
+
table = state_dict[table_key].shape # [grid, k]
|
| 192 |
+
@@ -383,6 +395,11 @@ def detect_unet_config(state_dict, key_prefix, metadata=None):
|
| 193 |
+
dit_config["timestep_input_dim"] = te.shape[1]
|
| 194 |
+
dit_config["time_embed_hidden_size"] = te.shape[0]
|
| 195 |
+
dit_config["time_embed_dim"] = state_dict['{}time_embedder.proj_out.weight'.format(key_prefix)].shape[0]
|
| 196 |
+
+ if basis_key in state_dict_keys:
|
| 197 |
+
+ basis = state_dict[basis_key].shape # [full time dim, compact adaLN dim]
|
| 198 |
+
+ if basis[0] != dit_config["time_embed_dim"]:
|
| 199 |
+
+ raise ValueError("MiniMax H3 adaLN basis input does not match time embedder output")
|
| 200 |
+
+ dit_config["adaln_curve_basis_dim"] = basis[1]
|
| 201 |
+
dit_config["rope_inv_freq_len"] = state_dict['{}rope.inv_freq'.format(key_prefix)].shape[0]
|
| 202 |
+
if metadata is not None and "config" in metadata:
|
| 203 |
+
dit_config.update(json.loads(metadata["config"]).get("transformer", {}))
|
| 204 |
+
diff --git a/comfy/model_patcher.py b/comfy/model_patcher.py
|
| 205 |
+
--- a/comfy/model_patcher.py
|
| 206 |
+
+++ b/comfy/model_patcher.py
|
| 207 |
+
@@ -231,8 +231,11 @@ def get_key_weight(model, key):
|
| 208 |
+
except AttributeError:
|
| 209 |
+
pass
|
| 210 |
+
|
| 211 |
+
- weight = getattr(op, op_keys[1])
|
| 212 |
+
- if convert_func is not None:
|
| 213 |
+
+ # Quantized comfy_kitchen layers (int8/fp4) may not expose a regular
|
| 214 |
+
+ # .weight attribute. Returning None lets the existing caller skip
|
| 215 |
+
+ # those packed weights during partial-offload bookkeeping.
|
| 216 |
+
+ weight = getattr(op, op_keys[1], None)
|
| 217 |
+
+ if weight is not None and convert_func is not None:
|
| 218 |
+
weight = comfy.utils.get_attr(model, key)
|
| 219 |
+
|
| 220 |
+
return weight, set_func, convert_func
|
reports/loadtest_dynamic_separate_fl2va.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"checkpoint": "F:\\AI\\ComfyUI_windows_portable\\ComfyUI\\models\\diffusion_models\\minimax-h3\\minimax-h3-fl2va-int8-lean-convrot-dynamic-k16-separate-qkv-quality21.safetensors",
|
| 3 |
+
"bytes": 22547374816,
|
| 4 |
+
"load_device": "cpu",
|
| 5 |
+
"elapsed_seconds": 8.17470669746399,
|
| 6 |
+
"checks": {
|
| 7 |
+
"model_class": "MiniMaxH3Model",
|
| 8 |
+
"separate_qkv": true,
|
| 9 |
+
"dynamic_basis": true,
|
| 10 |
+
"table_mode": false,
|
| 11 |
+
"basis_shape": [
|
| 12 |
+
2688,
|
| 13 |
+
16
|
| 14 |
+
],
|
| 15 |
+
"has_main_q_proj": true,
|
| 16 |
+
"has_main_k_proj": true,
|
| 17 |
+
"has_main_v_proj": true,
|
| 18 |
+
"has_main_qkv_proj": false,
|
| 19 |
+
"has_refiner_q_proj": true,
|
| 20 |
+
"has_refiner_qkv_proj": false,
|
| 21 |
+
"time_proj_in_shape": [
|
| 22 |
+
5376,
|
| 23 |
+
256
|
| 24 |
+
],
|
| 25 |
+
"time_proj_out_shape": [
|
| 26 |
+
2688,
|
| 27 |
+
5376
|
| 28 |
+
]
|
| 29 |
+
},
|
| 30 |
+
"status": "pass"
|
| 31 |
+
}
|
reports/loadtest_dynamic_separate_ref2va.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"checkpoint": "F:\\AI\\ComfyUI_windows_portable\\ComfyUI\\models\\diffusion_models\\minimax-h3\\minimax-h3-ref2va-int8-lean-convrot-dynamic-k16-separate-qkv-quality21.safetensors",
|
| 3 |
+
"bytes": 22547376168,
|
| 4 |
+
"load_device": "cpu",
|
| 5 |
+
"elapsed_seconds": 8.223532438278198,
|
| 6 |
+
"checks": {
|
| 7 |
+
"model_class": "MiniMaxH3Model",
|
| 8 |
+
"separate_qkv": true,
|
| 9 |
+
"dynamic_basis": true,
|
| 10 |
+
"table_mode": false,
|
| 11 |
+
"basis_shape": [
|
| 12 |
+
2688,
|
| 13 |
+
16
|
| 14 |
+
],
|
| 15 |
+
"has_main_q_proj": true,
|
| 16 |
+
"has_main_k_proj": true,
|
| 17 |
+
"has_main_v_proj": true,
|
| 18 |
+
"has_main_qkv_proj": false,
|
| 19 |
+
"has_refiner_q_proj": true,
|
| 20 |
+
"has_refiner_qkv_proj": false,
|
| 21 |
+
"time_proj_in_shape": [
|
| 22 |
+
5376,
|
| 23 |
+
256
|
| 24 |
+
],
|
| 25 |
+
"time_proj_out_shape": [
|
| 26 |
+
2688,
|
| 27 |
+
5376
|
| 28 |
+
]
|
| 29 |
+
},
|
| 30 |
+
"status": "pass"
|
| 31 |
+
}
|
reports/validation_dynamic_separate_fl2va.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"variant": "fl2va",
|
| 3 |
+
"checkpoint": "F:\\AI\\ComfyUI_windows_portable\\ComfyUI\\models\\diffusion_models\\minimax-h3\\minimax-h3-fl2va-int8-lean-convrot-dynamic-k16-separate-qkv-quality21.safetensors",
|
| 4 |
+
"table_source": "F:\\AI\\ComfyUI_windows_portable\\ComfyUI\\models\\diffusion_models\\minimax-h3\\minimax-h3-fl2va-int8-lean-convrot-table-k16-g4097-quality21.safetensors",
|
| 5 |
+
"bytes": 22547374816,
|
| 6 |
+
"gib": 20.99887916445732,
|
| 7 |
+
"keys": 1181,
|
| 8 |
+
"quantized_linears": 270,
|
| 9 |
+
"quant_groups": {
|
| 10 |
+
"256": 270
|
| 11 |
+
},
|
| 12 |
+
"fused_qkv_keys": 0,
|
| 13 |
+
"separate_qkv_projection_weights": 156,
|
| 14 |
+
"qkv_split_tensors_bitwise": 156,
|
| 15 |
+
"common_tensors_bitwise": 719,
|
| 16 |
+
"bf16_main": 30,
|
| 17 |
+
"time_embedder_bitwise_raw_hf": true,
|
| 18 |
+
"basis_shape": [
|
| 19 |
+
2688,
|
| 20 |
+
16
|
| 21 |
+
],
|
| 22 |
+
"basis_orthogonality_max_abs": 5.364418029785156e-07,
|
| 23 |
+
"dynamic_vs_released_table_relative": 0.0,
|
| 24 |
+
"dynamic_vs_released_table_max_abs": 0.0,
|
| 25 |
+
"adaln_curve_samples": 19,
|
| 26 |
+
"adaln_relative_errors": {
|
| 27 |
+
"block_0": 3.2893751722440356e-07,
|
| 28 |
+
"block_24": 3.324676356442069e-07,
|
| 29 |
+
"block_49": 3.217328412574716e-07,
|
| 30 |
+
"final": 2.851764975275728e-07
|
| 31 |
+
}
|
| 32 |
+
}
|
reports/validation_dynamic_separate_ref2va.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"variant": "ref2va",
|
| 3 |
+
"checkpoint": "F:\\AI\\ComfyUI_windows_portable\\ComfyUI\\models\\diffusion_models\\minimax-h3\\minimax-h3-ref2va-int8-lean-convrot-dynamic-k16-separate-qkv-quality21.safetensors",
|
| 4 |
+
"table_source": "F:\\AI\\ComfyUI_windows_portable\\ComfyUI\\models\\diffusion_models\\minimax-h3\\minimax-h3-ref2va-int8-lean-convrot-table-k16-g4097-quality21.safetensors",
|
| 5 |
+
"bytes": 22547376168,
|
| 6 |
+
"gib": 20.998880423605442,
|
| 7 |
+
"keys": 1181,
|
| 8 |
+
"quantized_linears": 270,
|
| 9 |
+
"quant_groups": {
|
| 10 |
+
"256": 270
|
| 11 |
+
},
|
| 12 |
+
"fused_qkv_keys": 0,
|
| 13 |
+
"separate_qkv_projection_weights": 156,
|
| 14 |
+
"qkv_split_tensors_bitwise": 156,
|
| 15 |
+
"common_tensors_bitwise": 719,
|
| 16 |
+
"bf16_main": 30,
|
| 17 |
+
"time_embedder_bitwise_raw_hf": true,
|
| 18 |
+
"basis_shape": [
|
| 19 |
+
2688,
|
| 20 |
+
16
|
| 21 |
+
],
|
| 22 |
+
"basis_orthogonality_max_abs": 2.384185791015625e-07,
|
| 23 |
+
"dynamic_vs_released_table_relative": 2.8884949188068276e-07,
|
| 24 |
+
"dynamic_vs_released_table_max_abs": 1.30385160446167e-06,
|
| 25 |
+
"adaln_curve_samples": 19,
|
| 26 |
+
"adaln_relative_errors": {
|
| 27 |
+
"block_0": 3.29813104826826e-07,
|
| 28 |
+
"block_24": 3.3073698091357073e-07,
|
| 29 |
+
"block_49": 3.2451748666062485e-07,
|
| 30 |
+
"final": 2.9567746651082416e-07
|
| 31 |
+
}
|
| 32 |
+
}
|