Qwen_Image-2.1-MXFP4
Built with Qwen. This portable package contains the balanced MXFP4 weights converted by EliovpAI from Qwen-Image-2.1. The weight tensors are unchanged from our previously prepared balanced checkpoint. It supports text-to-image generation, single-image editing and transparent RGBA output through the supplied Python API and command line launcher.
The tensor payload is 8.674 GiB. All components remain resident on one GPU. The runtime reconstructs each packed weight in BF16 for the original framework operation. Activations remain BF16. This is weight storage compression, not native FP4 matrix multiplication. There is no custom native library or model compiler to build or load. Quark is not required for inference.
The supplied loader is necessary: this custom packed checkpoint cannot be loaded
with an unmodified DiffusionPipeline.from_pretrained call.
Compatibility and tested scope
The intended target is an AMD GPU supported by the installed ROCm PyTorch stack, with BF16 support and enough free VRAM for the complete request. The loader does not impose an architecture whitelist. GPU family alone does not establish that a particular driver, wheel, operating system or memory capacity will work.
Hardware qualification for this portable package uses one MI355X, architecture
gfx950, with Torch 2.10.0+rocm7.1 and HIP 7.1.25424. Other GPUs are not
qualified by that result. Consult AMD's
ROCm compatibility documentation
for the prerequisites of the chosen stack.
| Request profile | Supplied limits |
|---|---|
| Text to image | 1024 × 1024 or 2048 × 2048 |
| RGBA generation | 1024 × 1024 or 2048 × 2048 |
| Editing | One input image, at most 4,194,304 input pixels; 1024 × 1024 output |
| Sampling | Batch one, 40 steps, true guidance 1.0, prefix KV reuse enabled |
| Prompt | 1 to 512 characters |
| Concurrency | One active request per worker and one cooperating worker per selected GPU |
| Graphs | Eager complete pipeline; whole-pipeline graph capture is unsupported |
The API rejects unsupported sizes, batches, guidance and step counts before GPU execution. This bounded profile does not establish video generation, arbitrary aspect ratios, multi-image editing or multiple simultaneous workers.
Exact model and arithmetic
Original model revision: 790c92633540aa0cb11d9abf19eb46d861714758.
Diffusers revision: 7263f3317f6b392d62f41e9d75ed9d7e21fc5a5c.
Checkpoint identities, including the tokenizer, scheduler and component configs,
are pinned in checkpoint.lock.json. Startup verifies their SHA-256 hashes.
result.json describes the tensor layout and lists every converted layer.
| Component | Original BF16 inference payload | Balanced payload |
|---|---|---|
| Diffusion transformer | 13.253 GiB | 3.706 GiB |
| Text and vision encoder | 16.330 GiB | 4.339 GiB |
| VAE | 0.629 GiB | 0.629 GiB |
| Total | 30.212 GiB | 8.674 GiB |
The balanced checkpoint packs 224 primary diffusion matrices and 372 text/vision matrices, embedding tables and patch-convolution weights. Small diffusion input/output/conditioning matrices, the VAE, normalization parameters and biases remain BF16. The word MXFP4 does not mean that every tensor has been converted to four bits. This package contains the balanced profile only.
Storage uses low-nibble-first E2M1 values with one E8M0 scale per 32 input columns. Convolution weights are flattened by output channel and zero-padded to a multiple of 32 columns; the decoder crops that padding and restores the original shape. Embedding lookup reconstructs only requested rows. Loading streams tensors to avoid a second complete expanded model in host memory.
The existing conversion used Quark weight-only rounding, without activation
calibration, GPTQ, learned correction or retraining. Preparing this portable
consumer did not requantize, repack or alter any stored weight tensor. The archived
conversion reproduction checked all 1,881 stored tensors bit for bit. Descriptive
format labels in 23 shard headers were normalized for this standalone package.
All tensor names, shapes, dtypes, offsets and data bytes were verified unchanged.
measurements/metadata-verification.json maps the original benchmark manifest to
the final checkpoint manifest; the final checkpoint also passed a fresh CLI run.
Install and run
Use Python 3.12 in an isolated environment. Start with a ROCm PyTorch stack that
supports your GPU. The full tested dependency closures are recorded in
requirements-mi355.lock.txt and environment.lock.json. The tested MI355X setup can be reproduced as follows:
python3 -m venv .venv
. .venv/bin/activate
python -m pip install torch==2.10.0+rocm7.1 torchvision==0.25.0+rocm7.1 \
--index-url https://download.pytorch.org/whl/rocm7.1
python -m pip install -r requirements-mi355.lock.txt \
--extra-index-url https://download.pytorch.org/whl/rocm7.1
python -m pip check
Use the pinned Diffusers commit, not an older distribution that lacks
QwenImage21Pipeline. requirements.txt pins the primary image dependencies for
an existing compatible stack; use the full selected lock for exact reproduction.
The adapter uses eager operations and disables compilation. Auxiliary packages
listed by the upstream Torch wheel remain subject to that wheel's requirements.
From the unpacked package directory:
python tools/verify_package.py --package .
./launch.sh --model-dir . generate \
--prompt 'A neon shop sign that reads "QWEN IMAGE 2.1", rainy night, reflections on wet pavement' \
--size 2048 --seed 42 --output ../outputs/neon.png
./launch.sh --model-dir . generate \
--mode rgba --prompt 'A cute cartoon dragon sticker.' \
--size 2048 --seed 52 --output ../outputs/dragon.png
./launch.sh --model-dir . generate \
--mode edit --image ../inputs/teapot.png \
--prompt 'Change the blue teapot to bright red. Keep the table and composition.' \
--size 1024 --seed 53 --output ../outputs/red-teapot.png
The package root contains result.json, checkpoint.lock.json, text_encoder/,
transformer/, vae/, processor/ and scheduler/. Set --model-dir to that
root. QWEN_IMAGE21_PYTHON may select a different Python executable for the shell
launcher. Existing image files are not overwritten. Each generated PNG has a
JSON sidecar recording the exact settings, timing, memory and checkpoint identity.
Keep outputs outside the immutable release directory when verifying its allowlist.
RGBA mode adds the upstream transparency prompt wording. Ordinary prompts also return RGBA PNGs but can have nearly opaque alpha. Keep PNG format to retain transparency; an RGBA file mode alone does not prove a transparent background.
Examples: 2048 generation, 2048 RGBA, edit input, edited image.
Local API
./launch.sh --model-dir . serve --port 8192
curl http://127.0.0.1:8192/v1/images/generations \
-H 'Content-Type: application/json' \
-d '{"model":"qwen-image-2.1-mxfp4","prompt":"A blue ceramic teapot on a wooden table","size":"2048x2048","seed":42,"n":1}'
GET /health reports readiness, checkpoint identity, load time and hardware.
GET /v1/models lists task limits. Generation returns a base64 PNG in
data[0].b64_json, plus complete-request measurements in metrics.
For POST /v1/images/edits, send JSON with prompt, one base64-encoded image in
image_b64, size: "1024x1024" and seed. This endpoint uses JSON rather than
multipart form data. It does not fetch URLs or open client-supplied file paths.
The body limit is 32 MiB and a concurrent GPU request receives HTTP 409.
Python usage:
from qwen_image21.runtime import ImageEngine
engine = ImageEngine('/path/to/checkpoint')
image, png, metrics = engine.generate('A blue ceramic teapot', width=1024, height=1024)
image.save('/path/to/teapot.png')
Memory and latency
All model components stay on the GPU, without CPU model offload or VAE tiling.
Single-frame VAE calls discard unused temporal caches and use the verified
convolution configuration MIOPEN_FIND_MODE=FAST, MIOPEN_DEBUG_CONV_GEMM=0.
Condition-image prefix caches are released before final decoding and between
requests. Each operation's expanded weight is temporary.
The optional global --memory-budget-gib flag limits the framework allocator.
It does not reserve VRAM, cover every library allocation, or guarantee that a
request fits. There is no universal 30 GiB admission rule. Required memory depends
on the image profile, runtime algorithms, allocator history and other GPU users.
A small request following a large one may retain the larger allocator reservation.
MI355X, the exact locked balanced checkpoint, batch one, 40 steps and guidance 1.0:
| Request | Full HTTP request | Sampled device peak |
|---|---|---|
| 2048 text to image, first | 22.83 s | 32.31 GiB |
| 2048 text to image, warm repeat 1 | 21.44 s | 32.05 GiB |
| 2048 text to image, warm repeat 2 | 21.58 s | 32.05 GiB |
| 1024 text to image after the large requests | 6.19 s | 32.05 GiB |
| 2048 RGBA | 21.18 s | 32.41 GiB |
| 1024 RGBA after the large request | 6.01 s | 32.41 GiB |
| 1024 single-image edit | 7.17 s | 32.44 GiB |
A separate fresh-process 1024 text-to-image CLI request completed through PNG in
7.57 seconds at 18.97 GiB sampled device peak and 16.37 GiB framework allocation.
Its record is measurements/mi355-fresh1024.json. This establishes a tested small
profile, not a universal memory guarantee for every prompt and device.
No memory cap was applied. These are full-suite device peaks with allocator
history, not separate minimum-VRAM estimates for the smaller profiles.
Startup to API readiness took 11.02 seconds, including file verification.
The exact per-request allocation, reservation, startup and identity records are
in measurements/mi355-api.json.
Times include the full 40-step request and PNG encoding; API measurements also include local HTTP/base64 transfer. Startup, including verification and loading, is reported separately. First requests use existing filesystem and kernel caches; these are process-cold measurements rather than clean-install measurements. Whole-device usage is sampled every 20 ms and can miss brief transients. Framework allocated and reserved high-water marks are also recorded. Device peaks are observations, not guaranteed minimum capacity or performance promises.
Quality and reproducibility
The retained eight-prompt quality screen uses 2048² images, 40 steps, guidance 1.0
and one fixed seed per prompt. It covers typography, portrait, product, wildlife,
spatial relations, illustration, landscape and poster generation. BF16 and balanced
outputs, settings and hashes are included in evaluation/images/.
| Metric | BF16 | Balanced MXFP4 |
|---|---|---|
| Mean CLIPScore | 0.90313 | 0.86839 |
| Mean LPIPS versus paired BF16 | 0 | 0.19177 |
CLIPScore uses CLIP ViT-B/32, the A photo depicts prefix and
2.5 × max(cosine(image, text), 0) in float32 on CPU. LPIPS uses AlexNet v0.1,
RGB images resized to 512² with bicubic interpolation and inputs in [-1,1].
This metric resize does not alter the generated resolution. Model hashes are
pinned in evaluation/metric-manifest.json.
Balanced mean CLIPScore is about 3.85% below BF16 in this small sample. These numbers describe a quality tradeoff, not unchanged quality, statistical equivalence or a human-preference guarantee. LPIPS also responds to composition changes. No FID/KID estimate is made from eight examples.
The new portable loader regenerated all eight archived balanced quality images
byte for byte on MI355X. Running the pinned CPU evaluator on those new outputs
reproduced every archived per-image score and the table above exactly.
See measurements/mi355-quality-generation.json and measurements/mi355-quality.json.
The MI355X hardware suite passed all tasks. Its three identical 2048 prompts produced identical PNGs. A separate instrumented seven-request sequence verified bitwise-identical prompt embeddings and final denoising latents for A, A, RGBA, A, edit, A, A. The BF16 VAE can produce small pixel differences even on immediate repeats in this stack. Consequently the final PNG is not promised to be bitwise identical across every request order or hardware/software combination.
The API screen records those differences and requires RGBA mean absolute error
at most 0.25 and maximum error at most 8 on the 0-255 scale for A-B-A. The separate
isolation test requires exact conditioning and denoising tensors. These image
limits are a repeatability check, not a general quality-equivalence threshold.
The final API sequence had image MAE 0.0415 and maximum difference 3.
See measurements/mi355-isolation.json for the final tensor-level results.
Reproduce validation with a fresh output directory:
python -m unittest discover -s tests -v
timeout --signal=TERM --kill-after=15s 900s python tools/validate_api.py \
--model-dir . --output ../validation/api
python tools/check_isolation.py --model-dir . --output ../validation/isolation
# Regenerate the eight paired prompts through this exact loader.
timeout --signal=TERM --kill-after=15s 900s python tools/quality_generate.py \
--model-dir . --output ../validation/balanced
# CPU metrics, kept separate from GPU timings.
python -m pip install -r requirements-evaluation.lock.txt \
--extra-index-url https://download.pytorch.org/whl/rocm7.1
python tools/prepare_metrics.py --output ../metric-cache
python tools/evaluate.py --reference evaluation/images/bf16 \
--candidate ../validation/balanced --clip-dir ../metric-cache/clip \
--torch-hub-dir ../metric-cache/torch-hub --output ../validation/quality
The API harness verifies startup, rejected settings, repeated 2048 generation,
RGBA at both sizes, editing, and A-B-A request isolation. Each process owns a
cooperative GPU lock; QWEN_IMAGE21_GPU_LOCK can select a shared physical-device
lock where multiple device-index mappings are used. The library accepts one
checkpoint per engine. Restart the process to change checkpoints.
License and attribution
The unchanged upstream Qwen Research License is included in LICENSE, with the required attribution in NOTICE. Quantization does not replace that license. See THIRD_PARTY_NOTICES.md for adapter, framework and evaluation attribution. All model rights remain subject to the upstream agreement. No additional model-use permission is implied here.
Model tree for EliovpAI/Qwen_Image-2.1-MXFP4
Base model
Qwen/Qwen-Image-2.1