Mixed-precision quantized UNet (GPTQ-applied). 152 of 192 Linear layers quantized to INT4 (45) / INT8 (107); the rest stay fp16. Fake-quantized: values rounded to int grid, stored as bf16.
Standard stabilityai/sd-turbo components, unmodified
lora_adapter.pt
(Optional) QLoRA recovery adapter trained on top of the quantized UNet. Improves LPIPS by ~8 % when applied. See "Advanced: QLoRA recovery" below.
mp_quant_metadata.json
Per-layer bit-width assignment + GPTQ hyper-parameters for full reproducibility
Quick start
python
1from diffusers import StableDiffusionPipeline
2import torch
34pipe = StableDiffusionPipeline.from_pretrained(5"ChenHe727/EdgeDiffusion",6 torch_dtype=torch.bfloat16,# required: INT4 layers use bf16 dtype7)8pipe = pipe.to("cuda")910image = pipe(11"a photo of a tabby cat sitting on a wooden chair, sharp focus",12 num_inference_steps=2,# 2-step is the sweet spot for SD-Turbo derivatives13 guidance_scale=0.0,# SD-Turbo doesn't use CFG14).images[0]1516image.save("output.png")
Why 2 inference steps?
SD-Turbo is fundamentally trained with adversarial diffusion distillation for 1-step generation. Empirically, 2 steps gives the best quality/speed trade-off for our compressed model: 28 % faster than 4 steps with marginally better LPIPS.
Results
Benchmark on RTX 5070 (Blackwell), 512 × 512, 2-step inference:
Variant
Params
Latency
VRAM
LPIPS vs original SD-Turbo
LPIPS vs fp16 baseline
stabilityai/sd-turbo (original)
860 M
0.146 s
3.05 GB
0
0.278
fp16 baseline (pruned + distilled)
642 M
0.142 s
2.64 GB
0.278
0
this repo (mp_quant PTQ)
642 M
0.145 s
2.64 GB
0.277
0.062
with LoRA adapter loaded
642 M + 9 MB
0.171 s
2.65 GB
0.278
0.057
Key takeaway: mixed-precision quantization adds essentially zero perceptual cost on top of the pruned + distilled baseline (LPIPS 0.062 vs fp16). The dominant quality cost in the pipeline is the pruning stage; quantization is "free".
Advanced: QLoRA recovery adapter
The included lora_adapter.pt was trained for 500 steps with step-wise teacher-student distillation to recover residual PTQ quality loss. It reduces the LPIPS gap from 0.062 to 0.057 (~8 % improvement).
python
1import torch
2from peft import LoraConfig, get_peft_model
3from diffusers import StableDiffusionPipeline
4from huggingface_hub import hf_hub_download
5import json
67# Load base pipeline8pipe = StableDiffusionPipeline.from_pretrained(9"ChenHe727/EdgeDiffusion", torch_dtype=torch.bfloat16,10).to("cuda")1112# Discover which layers were quantized (LoRA targets these)13meta_path = hf_hub_download("ChenHe727/EdgeDiffusion","mp_quant_metadata.json")14withopen(meta_path)as f:15 meta = json.load(f)16target_fqns =[fqn for fqn, bit in meta["quantization"]["assignment"].items()if bit !="fp16"]1718# Re-attach LoRA structure and load adapter weights19lora_state = torch.load(hf_hub_download("ChenHe727/EdgeDiffusion","lora_adapter.pt"),20 weights_only=False, map_location="cuda")21sample_key =next(k for k in lora_state if"lora_A"in k)22rank = lora_state[sample_key].shape[0]2324pipe.unet = get_peft_model(pipe.unet, LoraConfig(25 r=rank, lora_alpha=rank *2, target_modules=target_fqns,26 lora_dropout=0.0, bias="none",27))28own = pipe.unet.state_dict()29for k, v in lora_state.items():30if k in own:31 own[k].copy_(v.to(own[k].device, dtype=own[k].dtype))32pipe.unet.eval()3334# Generate as usual35image = pipe("a cat", num_inference_steps=2, guidance_scale=0.0).images[0]
Pipeline overview
The model in this repo is the output of a three-stage compression pipeline applied to stabilityai/sd-turbo:
stabilityai/sd-turbo (860 M)
↓ structural pruning + step-wise distillation
ChenHe727/EdgeDiffusion_distilled_feat_attn (642 M, fp16)
↓ sensitivity-aware mixed-precision GPTQ (this repo's UNet)
↓ QLoRA recovery training (this repo's lora_adapter.pt)
ChenHe727/EdgeDiffusion (this repo)
Full design rationale, ablations, and reproducibility instructions: see the GitHub repo.
Limitations
Conv2d layers are not quantized in v1 — only nn.Linear (attention projections, FFN). Conv2d holds ~70 % of UNet parameters; full quantization is planned for v2.
Fake-quant storage: weights are rounded to INT4/INT8 grids but stored as bf16 (2 bytes/value). Real packed INT4/INT8 storage would shrink the file from 1.22 GB to ~900 MB but requires a separate packing step.
LPIPS vs original SD-Turbo ≈ 0.28 mostly comes from the upstream pruning + distillation stage. The quantization stage itself adds only 0.005-0.062.
2-step inference is the recommended default. 1-step works (faster) but quality drops noticeably; 4-step is slower and not better.