kruatech commited on
Commit
6a10500
·
verified ·
1 Parent(s): 0046389

Upload folder using huggingface_hub

Browse files
README.md CHANGED
@@ -19,7 +19,7 @@ tags:
19
 
20
  **Image and music models converted for native MLX inference on Apple silicon**
21
 
22
- ![MLX](https://img.shields.io/badge/MLX-0.32-1f6feb) ![platform](https://img.shields.io/badge/platform-Apple%20silicon-black) ![precision](https://img.shields.io/badge/precision-bf16%20and%204--bit-6f42c1) [![licence](https://img.shields.io/badge/licence-Apache--2.0%20and%20MIT-brightgreen)](https://huggingface.co/kruatech/studio-mlx/blob/main/LICENSES.md)
23
 
24
  </div>
25
 
@@ -34,60 +34,31 @@ Pick one line. The image models need the shared encoder, the music model does no
34
  ```bash
35
  pip install "huggingface_hub[hf_xet]"
36
 
37
- # Z-Image-Turbo - 6.14 GiB
38
- hf download kruatech/studio-mlx --local-dir bundles \
39
- --include "shared/qwen3-4b-text-encoder-q4/*" "z-image-turbo-q4/*"
40
-
41
- # FLUX.2 [klein] 4B - 4.79 GiB
42
- hf download kruatech/studio-mlx --local-dir bundles \
43
- --include "shared/qwen3-4b-text-encoder-q4/*" "flux2-klein-4b-q4/*"
44
-
45
- # ACE-Step 1.5 Turbo - 9.38 GiB
46
- hf download kruatech/studio-mlx --local-dir bundles --include "ace-step-1.5-turbo/*"
47
-
48
  ```
49
 
50
  The `-q4` folders sit at the top level rather than inside the bf16 ones, so an include pattern fetches one precision without dragging in the other.
51
 
52
  ## What is inside
53
 
54
- | folder | what it does | precision | size | licence |
55
- |----------------------------------------------------------------------|--------------------------------------------------------|-----------|-----------|------------|
56
- | [`shared/qwen3-4b-text-encoder`](shared/qwen3-4b-text-encoder) | Shared text encoder for both image models | `bf16` | 7.51 GiB | Apache-2.0 |
57
- | [`shared/qwen3-4b-text-encoder-q4`](shared/qwen3-4b-text-encoder-q4) | Shared text encoder for both image models | `4-bit` | 2.59 GiB | Apache-2.0 |
58
- | [`z-image-turbo`](z-image-turbo) | Text to image, 6B, 8 steps | `bf16` | 11.62 GiB | Apache-2.0 |
59
- | [`z-image-turbo-q4`](z-image-turbo-q4) | Text to image, 6B, 8 steps | `4-bit` | 3.55 GiB | Apache-2.0 |
60
- | [`flux2-klein-4b`](flux2-klein-4b) | Text to image and multi-reference editing, 4B, 4 steps | `bf16` | 7.39 GiB | Apache-2.0 |
61
- | [`flux2-klein-4b-q4`](flux2-klein-4b-q4) | Text to image and multi-reference editing, 4B, 4 steps | `4-bit` | 2.20 GiB | Apache-2.0 |
62
- | [`ace-step-1.5-turbo`](ace-step-1.5-turbo) | Text to music, 48 kHz stereo | `bf16` | 9.38 GiB | MIT |
63
 
64
  ## bf16 or 4-bit
65
 
66
  **Use the 4-bit folders.** Quantization is MLX affine: 4 bits per weight with a bf16 scale and bias for every group of 64, about 4.5 bits per weight and 28-30% of the bf16 size. Normalizations, modulations and the encoder's embedding table stay in bf16, and matrix multiplication still runs in bf16 - the gain is memory and load time, not integer arithmetic.
67
 
68
- | component | bf16 | 4-bit | ratio |
69
- |---------------------|-----------|----------|-------|
70
- | Z-Image DiT | 11.46 GiB | 3.40 GiB | 30% |
71
- | klein DiT | 7.22 GiB | 2.03 GiB | 28% |
72
- | shared text encoder | 7.51 GiB | 2.58 GiB | 34% |
73
-
74
- The encoder ratio is higher on purpose: its 151936 x 2560 embedding table stays in bf16, because quantizing it moved the encoder output by 3e-01 in testing.
75
-
76
- What that buys, on one klein run at 1024 px with 4 steps:
77
-
78
- | phase | bf16 | 4-bit |
79
- |-----------------------|-------|-------|
80
- | read the text encoder | 40 s | 22 s |
81
- | read the transformer | 58 s | 19 s |
82
- | 4 diffusion steps | 35 s | 32 s |
83
- | total | 170 s | 73 s |
84
-
85
- The steps themselves barely change: multiplication still happens in bf16, the kernel unpacks each group of 64 weights on the fly. The saving is disk reading, which is why it depends on your storage - these figures come from an external volume at about 0.2 GiB/s.
86
 
87
  Output quality was indistinguishable: the same prompt and seed gave two clean images that differ the way two seeds differ. The bf16 folders exist as the accuracy reference a port can be checked against.
88
 
89
- `ace-step-1.5-turbo` has no 4-bit variant. It is published as converted upstream, and its own card reports that its DiT has to run in float32 - bf16 already fails accuracy at `rel_max 1.45e-01`, so quantizing further is not on the table.
90
-
91
  ## Bundle layout
92
 
93
  ```
@@ -184,15 +155,11 @@ Stages run one at a time and release their weights, so peak memory is set by the
184
 
185
  Derivatives of three upstream models under two licences. Each folder carries the licence of its upstream model.
186
 
187
- | folder | upstream | licence |
188
- |-----------------------------------|-----------------------------------------------------------------------------------------------|-----------------------------------------------------------|
189
- | `shared/qwen3-4b-text-encoder` | [black-forest-labs/FLUX.2-klein-4B](https://huggingface.co/black-forest-labs/FLUX.2-klein-4B) | [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) |
190
- | `shared/qwen3-4b-text-encoder-q4` | [black-forest-labs/FLUX.2-klein-4B](https://huggingface.co/black-forest-labs/FLUX.2-klein-4B) | [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) |
191
- | `z-image-turbo` | [Tongyi-MAI/Z-Image-Turbo](https://huggingface.co/Tongyi-MAI/Z-Image-Turbo) | [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) |
192
- | `z-image-turbo-q4` | [Tongyi-MAI/Z-Image-Turbo](https://huggingface.co/Tongyi-MAI/Z-Image-Turbo) | [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) |
193
- | `flux2-klein-4b` | [black-forest-labs/FLUX.2-klein-4B](https://huggingface.co/black-forest-labs/FLUX.2-klein-4B) | [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) |
194
- | `flux2-klein-4b-q4` | [black-forest-labs/FLUX.2-klein-4B](https://huggingface.co/black-forest-labs/FLUX.2-klein-4B) | [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) |
195
- | `ace-step-1.5-turbo` | [ACE-Step/Ace-Step1.5](https://huggingface.co/ACE-Step/Ace-Step1.5) | [MIT](https://opensource.org/license/mit) |
196
 
197
  The shared text encoder is redistributed from inside the two image repositories, both Apache-2.0. Conversion tooling and these cards: MIT.
198
 
 
19
 
20
  **Image and music models converted for native MLX inference on Apple silicon**
21
 
22
+ ![MLX](https://img.shields.io/badge/MLX-0.32-1f6feb) ![platform](https://img.shields.io/badge/platform-Apple%20silicon-black) ![precision](https://img.shields.io/badge/precision-bf16%20and%204--bit-6f42c1) [![licence](https://img.shields.io/badge/licence-Apache--2.0%20and%20MIT-brightgreen)](LICENSES.md)
23
 
24
  </div>
25
 
 
34
  ```bash
35
  pip install "huggingface_hub[hf_xet]"
36
 
 
 
 
 
 
 
 
 
 
 
 
37
  ```
38
 
39
  The `-q4` folders sit at the top level rather than inside the bf16 ones, so an include pattern fetches one precision without dragging in the other.
40
 
41
  ## What is inside
42
 
43
+ | folder | what it does | precision | size | licence |
44
+ |----------------------------------------------------------------------|------------------------------------------|-----------|----------|------------|
45
+ | [`qwen3-tts-12hz-1.7b-base`](qwen3-tts-12hz-1.7b-base) | Voice cloning from a reference recording | `bf16` | 4.23 GiB | Apache-2.0 |
46
+ | [`qwen3-tts-12hz-1.7b-customvoice`](qwen3-tts-12hz-1.7b-customvoice) | Nine preset voices, ten languages | `bf16` | 4.21 GiB | Apache-2.0 |
47
+ | [`qwen3-tts-12hz-1.7b-voicedesign`](qwen3-tts-12hz-1.7b-voicedesign) | Describe a voice in words and get it | `bf16` | 4.21 GiB | Apache-2.0 |
 
 
 
 
48
 
49
  ## bf16 or 4-bit
50
 
51
  **Use the 4-bit folders.** Quantization is MLX affine: 4 bits per weight with a bf16 scale and bias for every group of 64, about 4.5 bits per weight and 28-30% of the bf16 size. Normalizations, modulations and the encoder's embedding table stay in bf16, and matrix multiplication still runs in bf16 - the gain is memory and load time, not integer arithmetic.
52
 
53
+ | | bf16 | 4-bit |
54
+ |-----------------------------------|-----------|----------|
55
+ | Z-Image DiT | 11.46 GiB | 3.40 GiB |
56
+ | klein DiT | 7.22 GiB | 2.03 GiB |
57
+ | shared encoder | 7.51 GiB | 2.58 GiB |
58
+ | klein 512 px, 4 steps, wall clock | 170 s | 73 s |
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
  Output quality was indistinguishable: the same prompt and seed gave two clean images that differ the way two seeds differ. The bf16 folders exist as the accuracy reference a port can be checked against.
61
 
 
 
62
  ## Bundle layout
63
 
64
  ```
 
155
 
156
  Derivatives of three upstream models under two licences. Each folder carries the licence of its upstream model.
157
 
158
+ | folder | upstream | licence |
159
+ |-----------------------------------|-----------------------------------------------------------------------------------------------------|-----------------------------------------------------------|
160
+ | `qwen3-tts-12hz-1.7b-base` | [Qwen/Qwen3-TTS-12Hz-1.7B-Base](https://huggingface.co/Qwen/Qwen3-TTS-12Hz-1.7B-Base) | [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) |
161
+ | `qwen3-tts-12hz-1.7b-customvoice` | [Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice](https://huggingface.co/Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice) | [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) |
162
+ | `qwen3-tts-12hz-1.7b-voicedesign` | [Qwen/Qwen3-TTS-12Hz-1.7B-VoiceDesign](https://huggingface.co/Qwen/Qwen3-TTS-12Hz-1.7B-VoiceDesign) | [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) |
 
 
 
 
163
 
164
  The shared text encoder is redistributed from inside the two image repositories, both Apache-2.0. Conversion tooling and these cards: MIT.
165
 
qwen3-tts-12hz-1.7b-base/README.md ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ library_name: mlx
4
+ tags:
5
+ - mlx
6
+ - mlx-swift
7
+ - apple-silicon
8
+ base_model:
9
+ - Qwen/Qwen3-TTS-12Hz-1.7B-Base
10
+ ---
11
+
12
+ <div align="center">
13
+
14
+ # Qwen3-TTS 12 Hz 1.7B Base for MLX, bf16
15
+
16
+ **Voice cloning from a reference recording**
17
+
18
+ ![precision](https://img.shields.io/badge/precision-bf16-6f42c1) ![size](https://img.shields.io/badge/size-4.23%20GiB-1f6feb) [![licence](https://img.shields.io/badge/licence-Apache--2.0-brightgreen)](https://www.apache.org/licenses/LICENSE-2.0) [![part of](https://img.shields.io/badge/part%20of-studio--mlx-black)](https://huggingface.co/kruatech/studio-mlx)
19
+
20
+ </div>
21
+
22
+ Text to speech at 24 kHz. A talker generates 16 codec codes per frame at 12 Hz - the first sampled directly, the remaining 15 filled in by a code predictor - and a codec turns those codes into a waveform.
23
+
24
+ Converted from [Qwen/Qwen3-TTS-12Hz-1.7B-Base](https://huggingface.co/Qwen/Qwen3-TTS-12Hz-1.7B-Base) for native MLX inference on Apple silicon.
25
+
26
+ ## Download
27
+
28
+ Self-contained. It does not use the shared Qwen3-4B encoder that the image models share; its own text stack and codec are inside this folder.
29
+
30
+ ```bash
31
+ pip install "huggingface_hub[hf_xet]"
32
+
33
+ hf download kruatech/studio-mlx --local-dir bundles \
34
+ --include "qwen3-tts-12hz-1.7b-base/*"
35
+ ```
36
+
37
+ ## Files
38
+
39
+ | component | class | precision | tensors | size |
40
+ |------------------|----------------------------------|-----------|---------|----------|
41
+ | model | Qwen3TTSForConditionalGeneration | keep | 480 | 3.59 GiB |
42
+ | speech_tokenizer | Qwen3TTSTokenizerV2Model | keep | 496 | 651 MiB |
43
+
44
+ Folder total: **4.23 GiB**. `manifest.json` carries SHA-256, byte sizes and tensor counts for every file.
45
+
46
+ ## How this model works in MLX
47
+
48
+ - Adds an ECAPA-TDNN speaker encoder (38 convolutions) on top of the talker, which the other two variants do not have. Clone a voice by supplying a reference recording and its transcript.
49
+ - `model` holds the talker and the code predictor; `speech_tokenizer` holds the codec, whose 52 Conv1d and 6 ConvTranspose1d weights are permuted to MLX layout.
50
+ - No 4-bit variant yet: quantization needs an MLX implementation of these modules, which does not exist at the time of writing.
51
+
52
+ <details>
53
+ <summary><b>Tensor layout</b></summary>
54
+
55
+ MLX convolutions expect channels last, PyTorch expects channels first, so convolution weights are permuted during conversion. Everything else keeps its upstream shape.
56
+
57
+ | kind | PyTorch | MLX |
58
+ |-----------------------------|---------------------|---------------------|
59
+ | `Conv2d.weight` | `(out, in, kH, kW)` | `(out, kH, kW, in)` |
60
+ | `Linear`, norms, embeddings | `(out, in)` | unchanged |
61
+
62
+ No `weight_norm` and no 3-D convolutions appear in these models, so no folding was needed. Parameter names match the upstream checkpoints, so weights load without remapping.
63
+
64
+ </details>
65
+
66
+ <details>
67
+ <summary><b>Verification numbers</b></summary>
68
+
69
+ Every module was compared against the upstream reference on fixed inputs in float32. The metric is `rel_max = max|a-b| / max|a|`.
70
+
71
+ | module | rel_max | reference |
72
+ |-------------------------------------------------|-----------|---------------------------------|
73
+ | Qwen3 encoder, hidden_states[-2] | 2.7e-07 | transformers |
74
+ | Qwen3 encoder, layers 9/18/27 with padding mask | 6.0e-07 | transformers |
75
+ | Z-Image DiT | 4.2e-06 | diffusers |
76
+ | Flux2 DiT | 3.7e-07 | diffusers |
77
+ | VAE decoder | 1.3e-05 | diffusers |
78
+ | VAE encoder | 5.3e-06 | diffusers |
79
+ | VAE tiled decode | 5.1e-06 | diffusers tiled_decode |
80
+ | sigma schedule, static shift | 3.2e-08 | FlowMatchEulerDiscreteScheduler |
81
+ | sigma schedule, exponential dynamic shift | 7.7e-08 | FlowMatchEulerDiscreteScheduler |
82
+ | position ids, latent packing, patchify | bit-exact | pipeline helpers |
83
+
84
+ A Swift MLX implementation was then checked against the Python one:
85
+
86
+ | module | rel_max | note |
87
+ |------------------------------|-----------------|------------------------------------------------|
88
+ | tokenization, both pipelines | bit-exact | same ids |
89
+ | Qwen3 encoder, bf16 | 2.2e-04 | Z-Image branch |
90
+ | Qwen3 encoder, bf16 | 4.6e-03 | klein branch, 512 tokens with mask |
91
+ | Qwen3 encoder, 4-bit | 3.0e-04 | proves the quantized layout is rebuilt exactly |
92
+ | Z-Image DiT, float32 | 3.7e-07 | 8 layers |
93
+ | Flux2 DiT, float32 | 1.0e-06 | 2 double + 2 single blocks |
94
+ | VAE decode / tiled / encode | 1e-05 or better | both VAE classes |
95
+ | VAE BatchNorm statistics | 0 | exact |
96
+
97
+ The bf16 figures for a full-depth DiT are larger - 2e-02 for both models - and that is rounding order, not a defect: at float32 the same code agrees to 1e-06, and the deviation grows with depth from a bf16-level 1e-04 per block. Comparing two bf16 implementations below 1e-02 is not meaningful for a 30-block network.
98
+
99
+ </details>
100
+
101
+ <details>
102
+ <summary><b>Performance on an M1 Max</b></summary>
103
+
104
+ Mac Studio, Apple M1 Max, 10-core CPU, 24-core GPU, 32 GB, macOS 26.5. Release build, 4-bit weights, batch 1, warm page cache.
105
+
106
+ | run | per step | VAE decode |
107
+ |--------------------------------------|----------|------------|
108
+ | Z-Image, 512 px, 4 steps | 2.6 s | 1.5 s |
109
+ | klein, 512 px, 4 steps | 2.3 s | 0.9 s |
110
+ | klein, 1024 px, 4 steps | 8.0 s | 0.2 s |
111
+ | klein, 512 px + one 512 px reference | 4.1 s | 0.9 s |
112
+
113
+ Reading the weights dominates a cold run and depends entirely on the storage: on an external volume delivering about 0.2 GiB/s, the 4-bit encoder took 22-32 s and a 4-bit DiT 18-35 s, against 60-107 s for the same weights in bf16. On internal storage expect these to be several times shorter. Compute is unaffected: the numbers above are steady state.
114
+
115
+ Stages run one at a time and release their weights, so peak memory is set by the largest single component rather than their sum. The 4-bit DiT is 2.03 GiB for klein and 3.40 GiB for Z-Image; activations at 1024 px add to that, and no separate peak measurement was made.
116
+
117
+ </details>
118
+
119
+ <details>
120
+ <summary><b>Limitations</b></summary>
121
+
122
+ - Seeds are not compatible with the upstream pipelines, which use `torch.Generator`. The same prompt gives comparable images, never the same file.
123
+ - Both models are distilled: CFG is not applied, and step counts are low by design (8 for Z-Image, 4 for klein).
124
+ - Z-Image's Omni mode is not covered: it needs a SigLIP encoder that is not part of this bundle.
125
+ - Tiled VAE decode is available for high resolutions and gives a result that differs slightly from a single pass, exactly as it does in diffusers.
126
+ - Batches larger than one are not implemented.
127
+
128
+ </details>
129
+
130
+ ## Licence
131
+
132
+ [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0), the licence of the upstream model. Conversion tooling and this card: MIT. The upstream card states usage restrictions that redistribution does not repeal.
qwen3-tts-12hz-1.7b-customvoice/README.md ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ library_name: mlx
4
+ tags:
5
+ - mlx
6
+ - mlx-swift
7
+ - apple-silicon
8
+ base_model:
9
+ - Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice
10
+ ---
11
+
12
+ <div align="center">
13
+
14
+ # Qwen3-TTS 12 Hz 1.7B Customvoice for MLX, bf16
15
+
16
+ **Nine preset voices, ten languages**
17
+
18
+ ![precision](https://img.shields.io/badge/precision-bf16-6f42c1) ![size](https://img.shields.io/badge/size-4.21%20GiB-1f6feb) [![licence](https://img.shields.io/badge/licence-Apache--2.0-brightgreen)](https://www.apache.org/licenses/LICENSE-2.0) [![part of](https://img.shields.io/badge/part%20of-studio--mlx-black)](https://huggingface.co/kruatech/studio-mlx)
19
+
20
+ </div>
21
+
22
+ Text to speech at 24 kHz. A talker generates 16 codec codes per frame at 12 Hz - the first sampled directly, the remaining 15 filled in by a code predictor - and a codec turns those codes into a waveform.
23
+
24
+ Converted from [Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice](https://huggingface.co/Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice) for native MLX inference on Apple silicon.
25
+
26
+ ## Download
27
+
28
+ Self-contained. It does not use the shared Qwen3-4B encoder that the image models share; its own text stack and codec are inside this folder.
29
+
30
+ ```bash
31
+ pip install "huggingface_hub[hf_xet]"
32
+
33
+ hf download kruatech/studio-mlx --local-dir bundles \
34
+ --include "qwen3-tts-12hz-1.7b-customvoice/*"
35
+ ```
36
+
37
+ ## Files
38
+
39
+ | component | class | precision | tensors | size |
40
+ |------------------|----------------------------------|-----------|---------|----------|
41
+ | model | Qwen3TTSForConditionalGeneration | keep | 404 | 3.57 GiB |
42
+ | speech_tokenizer | Qwen3TTSTokenizerV2Model | keep | 496 | 651 MiB |
43
+
44
+ Folder total: **4.21 GiB**. `manifest.json` carries SHA-256, byte sizes and tensor counts for every file.
45
+
46
+ ## How this model works in MLX
47
+
48
+ - Pick a speaker by name and optionally steer delivery with an instruction in plain language. The speaker and language tables live in `config/model.json`.
49
+ - `model` holds the talker and the code predictor; `speech_tokenizer` holds the codec, whose 52 Conv1d and 6 ConvTranspose1d weights are permuted to MLX layout.
50
+ - No 4-bit variant yet: quantization needs an MLX implementation of these modules, which does not exist at the time of writing.
51
+
52
+ <details>
53
+ <summary><b>Tensor layout</b></summary>
54
+
55
+ MLX convolutions expect channels last, PyTorch expects channels first, so convolution weights are permuted during conversion. Everything else keeps its upstream shape.
56
+
57
+ | kind | PyTorch | MLX |
58
+ |-----------------------------|---------------------|---------------------|
59
+ | `Conv2d.weight` | `(out, in, kH, kW)` | `(out, kH, kW, in)` |
60
+ | `Linear`, norms, embeddings | `(out, in)` | unchanged |
61
+
62
+ No `weight_norm` and no 3-D convolutions appear in these models, so no folding was needed. Parameter names match the upstream checkpoints, so weights load without remapping.
63
+
64
+ </details>
65
+
66
+ <details>
67
+ <summary><b>Verification numbers</b></summary>
68
+
69
+ Every module was compared against the upstream reference on fixed inputs in float32. The metric is `rel_max = max|a-b| / max|a|`.
70
+
71
+ | module | rel_max | reference |
72
+ |-------------------------------------------------|-----------|---------------------------------|
73
+ | Qwen3 encoder, hidden_states[-2] | 2.7e-07 | transformers |
74
+ | Qwen3 encoder, layers 9/18/27 with padding mask | 6.0e-07 | transformers |
75
+ | Z-Image DiT | 4.2e-06 | diffusers |
76
+ | Flux2 DiT | 3.7e-07 | diffusers |
77
+ | VAE decoder | 1.3e-05 | diffusers |
78
+ | VAE encoder | 5.3e-06 | diffusers |
79
+ | VAE tiled decode | 5.1e-06 | diffusers tiled_decode |
80
+ | sigma schedule, static shift | 3.2e-08 | FlowMatchEulerDiscreteScheduler |
81
+ | sigma schedule, exponential dynamic shift | 7.7e-08 | FlowMatchEulerDiscreteScheduler |
82
+ | position ids, latent packing, patchify | bit-exact | pipeline helpers |
83
+
84
+ A Swift MLX implementation was then checked against the Python one:
85
+
86
+ | module | rel_max | note |
87
+ |------------------------------|-----------------|------------------------------------------------|
88
+ | tokenization, both pipelines | bit-exact | same ids |
89
+ | Qwen3 encoder, bf16 | 2.2e-04 | Z-Image branch |
90
+ | Qwen3 encoder, bf16 | 4.6e-03 | klein branch, 512 tokens with mask |
91
+ | Qwen3 encoder, 4-bit | 3.0e-04 | proves the quantized layout is rebuilt exactly |
92
+ | Z-Image DiT, float32 | 3.7e-07 | 8 layers |
93
+ | Flux2 DiT, float32 | 1.0e-06 | 2 double + 2 single blocks |
94
+ | VAE decode / tiled / encode | 1e-05 or better | both VAE classes |
95
+ | VAE BatchNorm statistics | 0 | exact |
96
+
97
+ The bf16 figures for a full-depth DiT are larger - 2e-02 for both models - and that is rounding order, not a defect: at float32 the same code agrees to 1e-06, and the deviation grows with depth from a bf16-level 1e-04 per block. Comparing two bf16 implementations below 1e-02 is not meaningful for a 30-block network.
98
+
99
+ </details>
100
+
101
+ <details>
102
+ <summary><b>Performance on an M1 Max</b></summary>
103
+
104
+ Mac Studio, Apple M1 Max, 10-core CPU, 24-core GPU, 32 GB, macOS 26.5. Release build, 4-bit weights, batch 1, warm page cache.
105
+
106
+ | run | per step | VAE decode |
107
+ |--------------------------------------|----------|------------|
108
+ | Z-Image, 512 px, 4 steps | 2.6 s | 1.5 s |
109
+ | klein, 512 px, 4 steps | 2.3 s | 0.9 s |
110
+ | klein, 1024 px, 4 steps | 8.0 s | 0.2 s |
111
+ | klein, 512 px + one 512 px reference | 4.1 s | 0.9 s |
112
+
113
+ Reading the weights dominates a cold run and depends entirely on the storage: on an external volume delivering about 0.2 GiB/s, the 4-bit encoder took 22-32 s and a 4-bit DiT 18-35 s, against 60-107 s for the same weights in bf16. On internal storage expect these to be several times shorter. Compute is unaffected: the numbers above are steady state.
114
+
115
+ Stages run one at a time and release their weights, so peak memory is set by the largest single component rather than their sum. The 4-bit DiT is 2.03 GiB for klein and 3.40 GiB for Z-Image; activations at 1024 px add to that, and no separate peak measurement was made.
116
+
117
+ </details>
118
+
119
+ <details>
120
+ <summary><b>Limitations</b></summary>
121
+
122
+ - Seeds are not compatible with the upstream pipelines, which use `torch.Generator`. The same prompt gives comparable images, never the same file.
123
+ - Both models are distilled: CFG is not applied, and step counts are low by design (8 for Z-Image, 4 for klein).
124
+ - Z-Image's Omni mode is not covered: it needs a SigLIP encoder that is not part of this bundle.
125
+ - Tiled VAE decode is available for high resolutions and gives a result that differs slightly from a single pass, exactly as it does in diffusers.
126
+ - Batches larger than one are not implemented.
127
+
128
+ </details>
129
+
130
+ ## Licence
131
+
132
+ [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0), the licence of the upstream model. Conversion tooling and this card: MIT. The upstream card states usage restrictions that redistribution does not repeal.
qwen3-tts-12hz-1.7b-voicedesign/README.md ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ library_name: mlx
4
+ tags:
5
+ - mlx
6
+ - mlx-swift
7
+ - apple-silicon
8
+ base_model:
9
+ - Qwen/Qwen3-TTS-12Hz-1.7B-VoiceDesign
10
+ ---
11
+
12
+ <div align="center">
13
+
14
+ # Qwen3-TTS 12 Hz 1.7B Voicedesign for MLX, bf16
15
+
16
+ **Describe a voice in words and get it**
17
+
18
+ ![precision](https://img.shields.io/badge/precision-bf16-6f42c1) ![size](https://img.shields.io/badge/size-4.21%20GiB-1f6feb) [![licence](https://img.shields.io/badge/licence-Apache--2.0-brightgreen)](https://www.apache.org/licenses/LICENSE-2.0) [![part of](https://img.shields.io/badge/part%20of-studio--mlx-black)](https://huggingface.co/kruatech/studio-mlx)
19
+
20
+ </div>
21
+
22
+ Text to speech at 24 kHz. A talker generates 16 codec codes per frame at 12 Hz - the first sampled directly, the remaining 15 filled in by a code predictor - and a codec turns those codes into a waveform.
23
+
24
+ Converted from [Qwen/Qwen3-TTS-12Hz-1.7B-VoiceDesign](https://huggingface.co/Qwen/Qwen3-TTS-12Hz-1.7B-VoiceDesign) for native MLX inference on Apple silicon.
25
+
26
+ ## Download
27
+
28
+ Self-contained. It does not use the shared Qwen3-4B encoder that the image models share; its own text stack and codec are inside this folder.
29
+
30
+ ```bash
31
+ pip install "huggingface_hub[hf_xet]"
32
+
33
+ hf download kruatech/studio-mlx --local-dir bundles \
34
+ --include "qwen3-tts-12hz-1.7b-voicedesign/*"
35
+ ```
36
+
37
+ ## Files
38
+
39
+ | component | class | precision | tensors | size |
40
+ |------------------|----------------------------------|-----------|---------|----------|
41
+ | model | Qwen3TTSForConditionalGeneration | keep | 404 | 3.57 GiB |
42
+ | speech_tokenizer | Qwen3TTSTokenizerV2Model | keep | 496 | 651 MiB |
43
+
44
+ Folder total: **4.21 GiB**. `manifest.json` carries SHA-256, byte sizes and tensor counts for every file.
45
+
46
+ ## How this model works in MLX
47
+
48
+ - No preset speakers: the voice itself is described in natural language.
49
+ - `model` holds the talker and the code predictor; `speech_tokenizer` holds the codec, whose 52 Conv1d and 6 ConvTranspose1d weights are permuted to MLX layout.
50
+ - No 4-bit variant yet: quantization needs an MLX implementation of these modules, which does not exist at the time of writing.
51
+
52
+ <details>
53
+ <summary><b>Tensor layout</b></summary>
54
+
55
+ MLX convolutions expect channels last, PyTorch expects channels first, so convolution weights are permuted during conversion. Everything else keeps its upstream shape.
56
+
57
+ | kind | PyTorch | MLX |
58
+ |-----------------------------|---------------------|---------------------|
59
+ | `Conv2d.weight` | `(out, in, kH, kW)` | `(out, kH, kW, in)` |
60
+ | `Linear`, norms, embeddings | `(out, in)` | unchanged |
61
+
62
+ No `weight_norm` and no 3-D convolutions appear in these models, so no folding was needed. Parameter names match the upstream checkpoints, so weights load without remapping.
63
+
64
+ </details>
65
+
66
+ <details>
67
+ <summary><b>Verification numbers</b></summary>
68
+
69
+ Every module was compared against the upstream reference on fixed inputs in float32. The metric is `rel_max = max|a-b| / max|a|`.
70
+
71
+ | module | rel_max | reference |
72
+ |-------------------------------------------------|-----------|---------------------------------|
73
+ | Qwen3 encoder, hidden_states[-2] | 2.7e-07 | transformers |
74
+ | Qwen3 encoder, layers 9/18/27 with padding mask | 6.0e-07 | transformers |
75
+ | Z-Image DiT | 4.2e-06 | diffusers |
76
+ | Flux2 DiT | 3.7e-07 | diffusers |
77
+ | VAE decoder | 1.3e-05 | diffusers |
78
+ | VAE encoder | 5.3e-06 | diffusers |
79
+ | VAE tiled decode | 5.1e-06 | diffusers tiled_decode |
80
+ | sigma schedule, static shift | 3.2e-08 | FlowMatchEulerDiscreteScheduler |
81
+ | sigma schedule, exponential dynamic shift | 7.7e-08 | FlowMatchEulerDiscreteScheduler |
82
+ | position ids, latent packing, patchify | bit-exact | pipeline helpers |
83
+
84
+ A Swift MLX implementation was then checked against the Python one:
85
+
86
+ | module | rel_max | note |
87
+ |------------------------------|-----------------|------------------------------------------------|
88
+ | tokenization, both pipelines | bit-exact | same ids |
89
+ | Qwen3 encoder, bf16 | 2.2e-04 | Z-Image branch |
90
+ | Qwen3 encoder, bf16 | 4.6e-03 | klein branch, 512 tokens with mask |
91
+ | Qwen3 encoder, 4-bit | 3.0e-04 | proves the quantized layout is rebuilt exactly |
92
+ | Z-Image DiT, float32 | 3.7e-07 | 8 layers |
93
+ | Flux2 DiT, float32 | 1.0e-06 | 2 double + 2 single blocks |
94
+ | VAE decode / tiled / encode | 1e-05 or better | both VAE classes |
95
+ | VAE BatchNorm statistics | 0 | exact |
96
+
97
+ The bf16 figures for a full-depth DiT are larger - 2e-02 for both models - and that is rounding order, not a defect: at float32 the same code agrees to 1e-06, and the deviation grows with depth from a bf16-level 1e-04 per block. Comparing two bf16 implementations below 1e-02 is not meaningful for a 30-block network.
98
+
99
+ </details>
100
+
101
+ <details>
102
+ <summary><b>Performance on an M1 Max</b></summary>
103
+
104
+ Mac Studio, Apple M1 Max, 10-core CPU, 24-core GPU, 32 GB, macOS 26.5. Release build, 4-bit weights, batch 1, warm page cache.
105
+
106
+ | run | per step | VAE decode |
107
+ |--------------------------------------|----------|------------|
108
+ | Z-Image, 512 px, 4 steps | 2.6 s | 1.5 s |
109
+ | klein, 512 px, 4 steps | 2.3 s | 0.9 s |
110
+ | klein, 1024 px, 4 steps | 8.0 s | 0.2 s |
111
+ | klein, 512 px + one 512 px reference | 4.1 s | 0.9 s |
112
+
113
+ Reading the weights dominates a cold run and depends entirely on the storage: on an external volume delivering about 0.2 GiB/s, the 4-bit encoder took 22-32 s and a 4-bit DiT 18-35 s, against 60-107 s for the same weights in bf16. On internal storage expect these to be several times shorter. Compute is unaffected: the numbers above are steady state.
114
+
115
+ Stages run one at a time and release their weights, so peak memory is set by the largest single component rather than their sum. The 4-bit DiT is 2.03 GiB for klein and 3.40 GiB for Z-Image; activations at 1024 px add to that, and no separate peak measurement was made.
116
+
117
+ </details>
118
+
119
+ <details>
120
+ <summary><b>Limitations</b></summary>
121
+
122
+ - Seeds are not compatible with the upstream pipelines, which use `torch.Generator`. The same prompt gives comparable images, never the same file.
123
+ - Both models are distilled: CFG is not applied, and step counts are low by design (8 for Z-Image, 4 for klein).
124
+ - Z-Image's Omni mode is not covered: it needs a SigLIP encoder that is not part of this bundle.
125
+ - Tiled VAE decode is available for high resolutions and gives a result that differs slightly from a single pass, exactly as it does in diffusers.
126
+ - Batches larger than one are not implemented.
127
+
128
+ </details>
129
+
130
+ ## Licence
131
+
132
+ [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0), the licence of the upstream model. Conversion tooling and this card: MIT. The upstream card states usage restrictions that redistribution does not repeal.