--- license: apache-2.0 pipeline_tag: text-to-image library_name: diffusers tags: - small - dit - flow-matching - custom_code datasets: - QLNI/FLUX-Reason-6M-flux2-latents language: - en ---

Surjo-Image-Preview

Text-To-Image • 108M Parameters • Looped Latent DiT, Flow Matching

## Samples
car vase
strawberries sea
--- ## Model ### About the model A tiny looped diffusion transformer (DiT) for 256px text-to-image, trained from scratch with rectified flow matching. - Pipeline: text-to-image - Parameter Count: 108.3M - Encoder: frozen `LiquidAI/LFM2.5-Encoder-230M` (1024-dim, 128 tokens) - VAE: FLUX.2 small-decoder mirror (`SurjoLabs/FLUX.2-small-decoder-vae`) - Image resolution: 256² - Latents: post-BN packed `(128, 16, 16)`, 256 tokens - Weights shipped are the EMA artifact (best-equivalent) ### Model config - `DIM`: 512 - `DEPTH`: 18 (7 pre + 4 looped ×2 + 7 post, effective 22) - `HEADS`: 8 × 64, QK-RMSNorm - `POS`: 2D-RoPE - `MLP`: SwiGLU - `TEXT_DIM`: 1024 (RMSNorm + projector) - `REPA_DIM`: 384 (DINOv2-S, HASTE schedule, off after 70%) - `LOOP_ITERS`: 2 - Custom architecture, loading needs `trust_remote_code=True` --- ## Training ### Dataset ~5.89M rows of prebuilt FLUX.2 latents + first-caption-per-row (`QLNI/FLUX-Reason-6M-flux2-latents`), 10 epochs (58.9M samples). ### Recipe AdamW (0.9/0.99, no decay, clip 1.0), LR 2e-4 to ~2e-5 cosine (5k warmup; decay horizon overshot by the warmup length, so it lands at ~2.35e-5 instead of the 2e-5 floor, cosmetic, disclosed), fp32 masters / bf16 compute, EMA 0.9999. 57,510 steps, global batch 1024, final val NLL **0.30116**. --- ## How to run the model ```bash pip install "transformers==5.13.1" "diffusers==0.39.0" accelerate safetensors pillow ``` ```python import torch from diffusers import AutoModel, AutoencoderKLFlux2 from transformers import AutoTokenizer, AutoModelForMaskedLM REPO = "SurjoLabs/Surjo-Image-Preview" device = "cuda" dtype = torch.bfloat16 if torch.cuda.get_device_capability()[0] >= 8 else torch.float16 model = AutoModel.from_pretrained(REPO, trust_remote_code=True, torch_dtype=dtype).to(device).eval() tok = AutoTokenizer.from_pretrained("LiquidAI/LFM2.5-Encoder-230M", trust_remote_code=True) tenc = AutoModelForMaskedLM.from_pretrained( "LiquidAI/LFM2.5-Encoder-230M", trust_remote_code=True, dtype=dtype).to(device).eval().lfm2 vae = AutoencoderKLFlux2.from_pretrained( "SurjoLabs/FLUX.2-small-decoder-vae", subfolder="vae", torch_dtype=dtype).to(device).eval() mean, var, eps = (vae.bn.running_mean.float(), vae.bn.running_var.float(), float(vae.config.batch_norm_eps)) prompt = "a cozy cabin in a snowy forest at dusk, warm light in the windows" ids = tok([prompt], padding="max_length", truncation=True, max_length=128, return_tensors="pt") ctx = tenc(**{k: v.to(device) for k, v in ids.items()}).last_hidden_state.to(dtype) uids = tok([""], padding="max_length", truncation=True, max_length=128, return_tensors="pt") uctx = tenc(**{k: v.to(device) for k, v in uids.items()}).last_hidden_state.to(dtype) cmask = ids["attention_mask"].to(device=device, dtype=dtype) umask = uids["attention_mask"].to(device=device, dtype=dtype) amp = torch.bfloat16 if torch.cuda.get_device_capability()[0] >= 8 else torch.float16 steps, cfg_scale, dt = 50, 3.0, 1.0 / 50 gen = torch.Generator(device=device).manual_seed(0) z = torch.randn(1, 128, 16, 16, device=device, generator=gen, dtype=dtype) with torch.no_grad(): for i in range(steps): t = torch.full((1,), i * dt, device=device) with torch.autocast(device_type="cuda", dtype=amp): out = model(torch.cat([z, z]), torch.cat([t, t]), torch.cat([uctx, ctx]), ctx_mask=torch.cat([umask, cmask]), deepsup=False, loop_iters=2) v_u, v_c = out.float().chunk(2) z = (z + dt * (v_u + cfg_scale * (v_c - v_u))).to(dtype) std = torch.sqrt(var.view(1, -1, 1, 1) + eps) lat = (z.float() * std.to(z.device) + mean.view(1, -1, 1, 1).to(z.device)).to(dtype) img = vae.decode(lat.view(1, 32, 2, 2, 16, 16 ).permute(0, 1, 4, 2, 5, 3).reshape(1, 32, 32, 32)).sample img = (img.clamp(-1, 1) + 1) / 2 from PIL import Image Image.fromarray( ((img[0].permute(1, 2, 0).float().cpu().numpy()) * 255).astype("uint8") ).save("out.png") ``` ### Recommended settings for sampling - `--steps`: 50 - `--cfg`: 3.0 - `loop_iters`: 2 (model default) - fp16 on pre-sm80 GPUs (e.g. T4), bf16 otherwise --- ## Evaluation GenEval2 ([Kamath et al., 2025](https://arxiv.org/abs/2512.16853)): 800 prompts, Soft-TIFA with Qwen3-VL-8B-Instruct, 1 image per prompt (256px, 50 steps, CFG 3.0, seed 42). Headline is Soft-TIFA GM. | metric | Surjo-Image-Preview | Supra2-IMG | |---|---|---| | Soft-TIFA GM | **8.07** | 6.80 | | Soft-TIFA AM | **42.30** | 36.38 | | object | **61.03** | 46.77 | | attribute | **45.80** | 43.56 | | count | **22.30** | 22.14 | | position | 28.91 | **31.07** | | verb | **2.03** | 1.51 | Takes objects/attributes/count; cedes position. Both models floor on verbs (~2%) and struggle counting (~22%): small-model limits, not a gap. --- ## Future These are the final weights for this preview. Surjo-Image (full release) will bring more data, refined architecture, and higher optimization. --- ![Agni](agni-logo-dark.svg) Trained using Agni --- ## Limitations - Renders legible text poorly; faces and hands degrade out-of-distribution. - English-only captions; 256px single-scale; small-model weak draws on complex multi-subject scenes. - Web-scale source biases inherited. --- ## AI Usage SurjoLabs has never trained a Text2Image model prior. AI was used heavily compared to previous models to write code. Research and designing the model was done by a human. --- ## Acknowledgments - Thanks to [SupraLabs](https://huggingface.co/SupraLabs/Supra2-IMG) for proving the tiny-T2I lane, and for the inspiration. - Thanks to the [FLUX-Reason-6M](https://huggingface.co/datasets/LucasFang/FLUX-Reason-6M) team for the training dataset, and to [QLNI](https://huggingface.co/datasets/QLNI/FLUX-Reason-6M-flux2-latents) for the precomputed latents. - Thanks to Black Forest Labs for the [FLUX.2 VAE](https://huggingface.co/black-forest-labs/FLUX.2-small-decoder). - Thanks to the authors of [i1](https://arxiv.org/abs/2606.11289) (configuration), [Looped-DiT](https://arxiv.org/abs/2609.40305) (looping), [XSA](https://arxiv.org/abs/2603.09078) (exclusive self-attention), and [PixelModel-v6](https://huggingface.co/bench-labs/PixelModel-v6) (training recipe).