Instructions to use ProCreations/Image-2.1-Calibrated-NVFP4 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use ProCreations/Image-2.1-Calibrated-NVFP4 with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("ProCreations/Image-2.1-Calibrated-NVFP4", dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- Draw Things
- DiffusionBee
Release calibrated Image2.1 NVFP4 transformer with dynamic scaling and BF16 rank correction, native SM120 runtime, quality evidence and real-time demo
1961af5 verified Download source/probe_dynamic.py from ProCreations/Image-2.1-Calibrated-NVFP4: direct link, hf CLI and curl.
- Browser
- Download file 3.48 kB
-
https://huggingface.co/ProCreations/Image-2.1-Calibrated-NVFP4/resolve/main/source/probe_dynamic.py
- Command line
-
hf download hf://ProCreations/Image-2.1-Calibrated-NVFP4/source/probe_dynamic.py
-
curl -L -o probe_dynamic.py https://huggingface.co/ProCreations/Image-2.1-Calibrated-NVFP4/resolve/main/source/probe_dynamic.py
3.48 kB
| """Calibrate from original BF16 and measure actual native W4A4 + BF16 correction.""" | |
| import os,json,time,math | |
| from pathlib import Path | |
| import torch | |
| from safetensors import safe_open | |
| from safetensors.torch import load_file | |
| from flashinfer import nvfp4_quantize,mm_fp4 | |
| from flashinfer.gemm import nvfp4_quantize_smooth,mm_nvfp4_svdquant | |
| BASE=Path(os.environ['IMAGE21_BASE'])/'transformer' | |
| OLD=Path(os.environ['IMAGE21_WORK_DIR']) | |
| INDEX=json.loads((BASE/'diffusion_pytorch_model.safetensors.index.json').read_text())['weight_map'] | |
| def weight(name): | |
| with safe_open(BASE/INDEX[name+'.weight'],framework='pt',device='cuda') as f:return f.get_tensor(name+'.weight') | |
| def split(x): | |
| g=torch.arange(len(x)//4,device=x.device) | |
| return x[g*4+g%4].contiguous(),x[g*4+(g+1)%4].contiguous() | |
| def error(y,ref):return ((y.float()-ref.float()).square().mean()/ref.float().square().mean()).sqrt().item() | |
| def prepare(w,x,smoothing,rank,method="svd",mse=False): | |
| amax=x.float().abs().amax(0).clamp_min(1e-5);wmax=w.float().abs().amax(0).clamp_min(1e-5) | |
| smooth=torch.ones_like(amax) if smoothing==0 else (amax**smoothing/wmax**(1-smoothing)).clamp(.01,100) | |
| smooth/=smooth.log().mean().exp() | |
| pre=(1/smooth).to(torch.bfloat16) | |
| ws=w.float()/pre.float()[None,:] | |
| if rank: | |
| torch.manual_seed(1234) | |
| if method=='svd': | |
| u,s,v=torch.svd_lowrank(ws,q=rank+32,niter=3) | |
| up=u[:,:rank]*s[:rank];down=v[:,:rank] | |
| elif method=='output': | |
| u,s,v=torch.svd_lowrank(ws@(x.float()*pre.float()).T,q=rank+32,niter=3) | |
| up=u[:,:rank];down=ws.T@up | |
| else: | |
| u,s,v=torch.svd_lowrank(x.float()*pre.float(),q=rank+32,niter=3) | |
| down=v[:,:rank];up=ws@down | |
| residual=ws-up@down.T | |
| else:residual=ws | |
| gx=(2688/((x.float()*pre.float()).abs().max()*1.2)).reshape(1) | |
| gw=(2688/residual.abs().max()).reshape(1) | |
| if mse: | |
| from weight_quant import quantize_weight | |
| h=(x.float()*pre.float()).square().mean(0) | |
| h=h+.01*h.mean() | |
| wq,wsc=quantize_weight(residual,gw,h) | |
| else:wq,wsc=nvfp4_quantize(residual.to(torch.bfloat16),gw,backend='cute-dsl') | |
| alpha=(1/(gx*gw)).reshape(1) | |
| result={'weight':wq.view(torch.uint8),'sf':wsc.view(torch.uint8).reshape(-1),'pre':pre,'gx':gx,'alpha':alpha} | |
| if rank:result.update(down=(pre.float()[:,None]*down).to(torch.bfloat16).contiguous(),up=(up/alpha).to(torch.bfloat16).contiguous()) | |
| return result | |
| def run(x,p): | |
| from dynamic_scale import scale | |
| gx,alpha,up=scale(x,p['pre'],p['gx'],p['alpha'],p['up']) if 'up' in p else (p['gx'],p['alpha'],None) | |
| q,sf=nvfp4_quantize_smooth(x,p['pre'],gx,backend='cute-dsl') | |
| if 'down' in p: | |
| d=x@p['down'] | |
| return mm_nvfp4_svdquant(q,p['weight'],sf,p['sf'],alpha,d,up,backend='cute-dsl') | |
| return mm_fp4(q,p['weight'].T,sf,p['sf'],p['alpha'],backend='b12x') | |
| def main(): | |
| rows=[] | |
| for block,leaf in [(0,'attn.to_q'),(0,'img_mlp.out'),(8,'attn.to_v'),(16,'img_mlp.proj'),(16,'img_mlp.gate_layer'),(24,'attn.to_out.0'),(31,'attn.to_k')]: | |
| name=f'transformer_blocks.{block}.{leaf}';w=weight(name) | |
| x=load_file(str(OLD/'calibration'/f'{name}.safetensors'))['activations'].cuda();fit,check=split(x) | |
| rf=fit@w.T;rc=check@w.T | |
| for smooth in [0.,.5,1.]: | |
| for rank in [0,128]: | |
| start=time.time();p=prepare(w,fit,smooth,rank);y=run(fit,p);yc=run(check,p) | |
| row={'name':name,'smooth':smooth,'rank':rank,'fit_nrmse':error(y,rf),'check_nrmse':error(yc,rc),'seconds':time.time()-start};rows.append(row);print(json.dumps(row),flush=True) | |
| Path('probe.json').write_text(json.dumps(rows,indent=2)) | |
| if __name__=='__main__':main() | |