File size: 7,016 Bytes
0c17c2b | 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 | # FLUX.2 Klein 9B KV Image Generation & Multi-Reference Edit Server
This directory contains the self-contained image generation and editing server integrating **`FLUX.2-klein-9b-kv-Nunchaku`** with this **`FLUX.2-klein-text-encoder-NVFP4`** text encoder.
It enables fast, 4-step distilled text-to-image generation and multi-reference image editing (with KV caching) on single consumer GPUs (e.g., 16 GB RTX 5060 Ti / RTX 4080 / RTX 5090) without running out of memory.
---
## π§ͺ Quantization Details & Strategy
The text encoder was quantized using `llmcompressor` / `compressed-tensors` via [`quantize_flux2_klein_text_encoder_nvfp4.py`](file:///auto/home/amano/olegk/Nikola/src/nvfp4/quantize_flux2_klein_text_encoder_nvfp4.py) based on the recipe in [`recipe.yaml`](../recipe.yaml).
### High-Fidelity Representation Preservation Strategy
FLUX.2 Klein conditions its flow transformer on intermediate representations extracted from specific layers of the Qwen3 text encoder (specifically layer indices **9, 18, and 27**). Blanket quantization across all layers degrades prompt adherence and text grounding. To prevent this, the following selective quantization scheme was applied:
1. **Unquantized Self-Attention Projections (`q_proj`, `k_proj`, `v_proj`, `o_proj`)**:
- Kept in full **bfloat16** across all 36 layers.
- Preserves semantic query-key routing and relational representations without quantization noise.
2. **Unquantized Root & Near-Tap Layers**:
- Layer 0 (root) and layers **8, 9, 17, 18, 26, 27** (the critical tap layers and their immediate predecessors) are preserved in full **bfloat16**.
- Ensures the hidden state outputs at indices 9, 18, and 27 have near-perfect representation fidelity (cosine similarity > 0.999 vs unquantized reference).
3. **Compressed Intermediate MLP Blocks**:
- The MLP blocks (`gate_proj`, `up_proj`, `down_proj`) of intermediate layers (1β7, 10β16, 19β25, 28β35) are compressed to **NVFP4 (W4A4)** using tensor-group scaling (`format: nvfp4-pack-quantized`).
- Yields ~4x compression of MLP weights, reducing memory footprint and load times significantly.
4. **Preserved Head & Embeddings**:
- `lm_head` and `embed_tokens` are kept unquantized in **bfloat16**.
---
## π¦ Required Packages in Python Environment
The server runs on Python 3.11 or 3.12 with CUDA 12.8 / 13.0 (e.g. Blackwell sm_120 / Thor sm_110):
| Package | Minimum Version | Purpose |
| :--- | :---: | :--- |
| `torch` | `2.11.0+` | PyTorch with Blackwell/sm_120 CUDA compute support |
| `diffusers` | `0.38.0+` | Built with native `Flux2KleinKVPipeline` support |
| `transformers` | `5.3.0+` | Qwen3 causal language model architecture support |
| `vllm` | `0.20.0+` | Provides precompiled CUTLASS NVFP4 GEMM kernels |
| `nunchaku` | `1.2.1+` | SVDQuant FP4/INT4 4-bit transformer execution |
| `fastapi`, `uvicorn` | Latest | OpenAI-compatible REST server infrastructure |
| `pillow` | Latest | Image processing and aspect-ratio padding |
> [!IMPORTANT]
> When installing packages in environments with custom CUDA builds, use `--no-deps` to avoid overwriting PyTorch or flash-attention wheels tuned for your compute capability.
---
## β‘ Architecture & Sequential GPU Memory Management
On a 16 GB VRAM GPU, loading both the NVFP4 text encoder (~10.1 GB) and the Nunchaku transformer (~5.8 GB) simultaneously would total ~15.9 GB, exceeding available VRAM and causing an out-of-memory crash.
`Flux2KleinBackend.py` solves this using **Sequential GPU Stage Execution**:
```
[Idle: 0.01 GB VRAM]
β
βΌ
Stage 1: Load Text Encoder to CUDA ββ> Encode Prompt & IDs ββ> Offload to CPU [0.01 GB VRAM]
β
βΌ (if reference images passed)
Stage 2: Load VAE to CUDA βββββββββββ> Encode Image Latents ββ> Offload to CPU [0.01 GB VRAM]
β
βΌ
Stage 3: Load Transformer to CUDA βββ> 4-Step Denoise (KV) ββ> Offload to CPU [0.01 GB VRAM]
β (Step 0: Extract KV)
β (Steps 1-3: Cached KV)
βΌ
Stage 4: Load VAE to CUDA βββββββββββ> Decode Latents to PIL β> Offload to CPU [0.01 GB VRAM]
β
βΌ
[Idle: 0.01 GB VRAM] (GPU completely freed for other tasks!)
```
### Performance & Memory Metrics (RTX 5060 Ti 16 GB)
- **Text-to-Image (1024x1024, 4 steps)**: ~9.5s total (~0.5s/step), **10.43 GB** peak VRAM.
- **1-Image Edit (1024x1024, 4 steps)**: ~12.5s total, **10.46 GB** peak VRAM.
- **2-Image Multi-Reference Edit (1024x1024, 4 steps)**: ~15.0s total, **13.18 GB** peak VRAM.
- **Idle Memory Footprint**: **0.010 GB** (10 MB).
- **Concurrency**: All requests are serialized with an `asyncio.Lock` queue, preventing concurrent memory spikes.
---
## π Running the Server
### 1. Launch via Helper Script
```bash
# Set paths (or edit defaults inside the script)
export MODEL_PATH="/path/to/FLUX.2-klein-9b-kv-Nunchaku"
export NVFP4_TEXT_ENCODER_PATH="/path/to/FLUX.2-klein-text-encoder-NVFP4"
./imagegen_flux2_klein.sh 4501
```
### 2. Launch Manually via Python
```bash
export VLLM_NVFP4_GEMM_BACKEND=cutlass
python ImageEditServer.py \
--port 4501 \
--model /path/to/FLUX.2-klein-9b-kv-Nunchaku \
--optimized-model /path/to/FLUX.2-klein-9b-kv-Nunchaku/svdq-fp4_r32-FLUX.2-klein-9b-kv-Nunchaku.safetensors \
--nvfp4-text-encoder /path/to/FLUX.2-klein-text-encoder-NVFP4 \
--backend flux2-klein \
--steps 4 \
--guidance-scale 1.0
```
---
## π‘ API Usage Examples
### 1. Text-to-Image Generation (`POST /v1/images/generations`)
```bash
curl -X POST http://localhost:4501/v1/images/generations \
-H "Content-Type: application/json" \
-d '{
"prompt": "A cute cheerful anime girl with a ponytail in a simple village dress smiling, anime style",
"size": "1024x1024",
"num_inference_steps": 4,
"seed": 42
}' | jq -r '.data[0].b64_json' | base64 -d > generated.png
```
### 2. Single-Image Reference Edit (`POST /v1/images/edits`)
```bash
curl -X POST http://localhost:4501/v1/images/edits \
-F "image=@generated.png" \
-F "prompt=The anime girl wearing stylish round glasses and a bright red ribbon in her hair, anime style" \
-F "num_inference_steps=4" \
-F "seed": 42 | jq -r '.data[0].b64_json' | base64 -d > edited_1img.png
```
### 3. Multi-Reference 2-Image Edit (`POST /v1/images/edits`)
Pass multiple `-F "image=@..."` form fields:
```bash
curl -X POST http://localhost:4501/v1/images/edits \
-F "image=@girl.png" \
-F "image=@sofa.png" \
-F "prompt=The anime girl from image 1 is sitting comfortably on the sofa from image 2 in the living room, anime style" \
-F "num_inference_steps=4" \
-F "seed": 42 | jq -r '.data[0].b64_json' | base64 -d > edited_2imgs.png
```
### 4. Memory Introspection (`GET /v1/memory_stats`)
```bash
curl http://localhost:4501/v1/memory_stats | jq .
```
Expected output when idle: `"allocated_gb": 0.01`, `"reserved_gb": 0.02`.
|