File size: 8,580 Bytes
b37a2f5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
---
license: other
license_name: flux-non-commercial-license-v2.1
license_link: https://bfl.ai/licensing
base_model:
  - wikeeyang/Flux2-Klein-9B-True-V3
  - black-forest-labs/FLUX.2-klein-9B
base_model_relation: quantized
library_name: diffusers
pipeline_tag: text-to-image
tags:
  - flux
  - flux2
  - klein
  - fp8
  - quantized
  - optimum-quanto
language:
  - en
---

# Flux2-Klein-9B-True-V3-fp8

An FP8 build of [`wikeeyang/Flux2-Klein-9B-True-V3`](https://huggingface.co/wikeeyang/Flux2-Klein-9B-True-V3), saved in the [optimum-quanto](https://github.com/huggingface/optimum-quanto) `QuantizedDiffusersModel` layout so it loads directly into a `diffusers` `Flux2KleinPipeline` with no conversion step.

**This is a format and precision conversion only.** Nothing was retrained, distilled or merged. The weights are wikeeyang's True-V3.

> ### ⚠️ Licence — read before use
>
> These weights derive from [`black-forest-labs/FLUX.2-klein-9B`](https://huggingface.co/black-forest-labs/FLUX.2-klein-9B), released under the **FLUX Non-Commercial License v2.1**. Commercial or revenue-generating deployment requires a separate licence from Black Forest Labs — see [bfl.ai/licensing](https://bfl.ai/licensing).
>
> Only FLUX.2-klein-**4B** is Apache-2.0. The 9B models are not. The upstream True-V3 repository is tagged `apache-2.0`; that tag does not reflect the terms of the base model, and a third party cannot relicense weights they do not own. This repository is labelled to match the actual upstream terms.

## Why this exists

True-V3 ships ComfyUI single-file formats only — `bf16`, `fp8mixed`, `int8mixedrow`, GGUF, `nvfp4`, `int4_convrot`. None of them is a quanto checkpoint: `QuantizedDiffusersModel.from_pretrained()` expects quanto's own directory layout (`_data`/`_scale` tensors plus a quanto config), and quanto cannot read ComfyUI's scaled-FP8 tensor layout.

So a diffusers/quanto serving stack could not use True-V3 without converting it first. This repository is that conversion, published so nobody has to redo it.

## Usage

```python
import torch
from diffusers import Flux2KleinPipeline, Flux2Transformer2DModel
from optimum.quanto import QuantizedDiffusersModel

# quanto's Marlin FP8 kernel rejects a non-contiguous activation, which the
# Flux2 attention path produces. Apply before loading.
from optimum.quanto.nn.qlinear import QLinear
_orig = QLinear.forward
QLinear.forward = lambda self, x: _orig(self, x.contiguous())


class QuantizedFlux2KleinTransformer(QuantizedDiffusersModel):
    base_class = Flux2Transformer2DModel


pipe = Flux2KleinPipeline.from_pretrained(
    "black-forest-labs/FLUX.2-klein-9B",
    transformer=None,
    torch_dtype=torch.bfloat16,
).to("cuda")

pipe.transformer = QuantizedFlux2KleinTransformer.from_pretrained(
    "ModelsLab/Flux2-Klein-9B-True-V3-fp8"
).to("cuda")

image = pipe(
    prompt="a vintage storefront window with a hand-painted gold sign that reads "
           '"KLEIN COFFEE ROASTERS EST 1974", rainy street reflection',
    height=1024, width=1024,
    num_inference_steps=4,
    generator=torch.Generator("cuda").manual_seed(1234),
).images[0]
image.save("out.png")
```

### Tighter VRAM

The transformer is 9.08 GB. The bf16 text encoder is another ~16 GB, which will not fit alongside it on a 24 GB card. Quantise it the same way:

```python
from optimum.quanto import QuantizedModelForCausalLM, qfloat8
from transformers import AutoModelForCausalLM

te = AutoModelForCausalLM.from_pretrained(
    "black-forest-labs/FLUX.2-klein-9B", subfolder="text_encoder",
    torch_dtype=torch.bfloat16)
pipe.text_encoder = QuantizedModelForCausalLM.quantize(
    te, weights=qfloat8).to("cuda")
```

FP8 transformer + FP8 text encoder is ~18 GB resident. Measured peak allocation on an RTX 3090 at 1024×1024, 4 steps, with both resident: **20.66 GB** — so it fits a 24 GB card, with roughly 3 GB spare.

## Measured against the stock FP8 checkpoint

![stock klein-9B FP8 vs True-V3 FP8, same prompt and seed](comparison.jpg)

*Same prompt, same seed, same 4 steps, same quantiser. Top row is the stock FP8 checkpoint, bottom row is this one. Note the misspelled "COFFFEE" in the stock render.*

Both checkpoints were run through an identical harness: same architecture, same quantiser, same 4 steps, same guidance, same seeds, and prompt embeddings encoded once and cached so both saw bit-identical conditioning. 12 prompts × 3 seeds = **36 timed renders each**, 1024×1024, on an RTX 3090.

| | stock klein-9B FP8 | True-V3 FP8 | Δ |
|---|---|---|---|
| Aesthetic (LAION v2) | 5.996 | **6.167** | +0.171 |
| CLIP adherence (×100) | 28.98 | **29.65** | +0.67 |
| Reference retention¹ | 56.6 | **60.6** | +4.0 |
| Median latency / 1024² | 6.29 s | 6.31 s | +0.2% |
| On-disk size | 9.08 GB | 9.08 GB | — |

¹ CLIP image–image cosine ×100 between each edit and its reference images, over a two-reference editing run (four scenes × two seeds), scored on the render set shared by every variant tested.

The latency difference is inside run-to-run noise — across eight variants benchmarked the same way, the whole spread was 0.06 s. Same architecture, same step count, same FP8 kernels, so this is a quality change at fixed cost rather than a trade.

Qualitatively, the clearest wins over stock are **rendered text** (the sign prompt above is misspelled "COFFFEE" by the stock checkpoint and correct here), **hands**, and **product/material detail**. Colour is slightly less saturated and more filmic.

**Caveat on the metrics:** the LAION aesthetic predictor rewards contrast and saturation, so it is not a neutral judge of photographic realism — a crunchier model can score well while rendering worse text. Treat the table as a ranking aid and look at your own prompts.

## Verification

This checkpoint was checked by regenerating four benchmark prompts at a fixed seed and diffing them
against the renders produced by the original bf16 True-V3 weights in the earlier benchmark run:

| prompt | max abs diff | mean abs diff | PSNR |
|---|---|---|---|
| text | 0 | 0.0000 | ∞ |
| portrait | 0 | 0.0000 | ∞ |
| hands | 0 | 0.0000 | ∞ |
| product | 0 | 0.0000 | ∞ |

**Bit-identical** on all four. The conversion changes the container, not the output.

## How this was built

Reproducible in about a minute of compute once the 18 GB source file is local (63 s measured: load, quantise, save):

```python
import torch
from diffusers import Flux2Transformer2DModel
from optimum.quanto import QuantizedDiffusersModel, qfloat8
from huggingface_hub import hf_hub_download


class QuantizedFlux2KleinTransformer(QuantizedDiffusersModel):
    base_class = Flux2Transformer2DModel


path = hf_hub_download("wikeeyang/Flux2-Klein-9B-True-V3",
                       "Flux2-Klein-9B-True-V3-bf16.safetensors")

tr = Flux2Transformer2DModel.from_single_file(
    path,
    config="black-forest-labs/FLUX.2-klein-9B", subfolder="transformer",
    torch_dtype=torch.bfloat16,
)
QuantizedFlux2KleinTransformer.quantize(tr, weights=qfloat8).save_pretrained("out")
```

## Notes and caveats

- **Ampere (RTX 3090 / A100):** these cards have no native FP8 tensor cores. quanto routes through its Marlin FP8 GEMM, which does run on `sm_80`/`sm_86` — so FP8 here buys memory, and the speed is comparable to the equivalent bf16 path rather than dramatically faster. Ada and Hopper get more from it.
- **The contiguity patch in the usage snippet is required**, not optional. Without it the Marlin kernel raises `RuntimeError: A is not contiguous` on the first forward pass.
- **LoRAs must be fused before quantisation.** Calling `load_lora_weights()` on an already-quantised transformer does not work. Load the bf16 transformer, `fuse_lora()`, then quantise.
- **Only the transformer is replaced.** Text encoder, VAE, scheduler and tokenizer come from the base `FLUX.2-klein-9B` repo unchanged.
- **`guidance_scale` is ignored.** Klein is step-distilled, and `diffusers` emits *"Guidance scale 4.0 is ignored for step-wise distilled models"* and drops the value. Passing it does nothing; do not expect it to behave as a tuning knob.

## Credits

- [**wikeeyang**](https://huggingface.co/wikeeyang) — the True-V3 finetune. All of the quality improvement is theirs; this repository only changes the container.
- [**Black Forest Labs**](https://huggingface.co/black-forest-labs) — FLUX.2-klein-9B.
- [**optimum-quanto**](https://github.com/huggingface/optimum-quanto) — the quantiser and the checkpoint format.

Converted and published by [ModelsLab](https://huggingface.co/ModelsLab).