--- license: other license_name: ltx-2.5-community tags: - quantization - 4-bit - int4 - nvfp4 - gptq - awq - text-encoder - text-to-video - image-to-video - video-to-video - ltx-video - low-vram - comfyui base_model: Lightricks/LTX-2.5 --- [![Haverbex LTX2.5-4bit](https://huggingface.co/topabaem/LTX-2.5-Text-Encoder-4bit-8GB/resolve/main/banner.png)](https://buymeacoffee.com/choijjs83q) # LTX-2.5 Text Encoder — 4-bit, 8 GB > **The 8 GB in the name is the file**, not the card. On disk this is 8.46 GB > against the original's 26.264 GB. Running it needs about **9.7 GB of VRAM** — > an 8 GB card is not enough. See [Memory](#memory--what-the-card-actually-needs). > **EN** — I'm a student researching ML quantization. Getting this one done > burned through so much in server bills that from here on I'll only be able to > afford *niu lai* movies. Thank you for using the model. > > **한국어** — 저는 ML 양자화를 연구하는 학생입니다. 양자화를 진행하면서 서버 > 비용을 너무 많이 써서, 앞으로 영화는 *niu lai*만 봐야 할 것 같습니다. 모델을 > 사용해 주셔서 감사합니다. > > **中文** — 我是一名研究机器学习量化的学生。做这次量化烧掉了太多服务器费用, > 以后看电影大概只能看 *niu lai* 了。感谢您使用这个模型。 > > ☕ **[Buy me a coffee](https://buymeacoffee.com/choijjs83q)** · [커피 한 잔 사주기](https://buymeacoffee.com/choijjs83q) · [请我喝杯咖啡](https://buymeacoffee.com/choijjs83q) The Gemma4-12B text encoder that LTX-2.5 needs in order to read a prompt, compressed from **26.264 GB to 8.46 GB** (3.10x) and runnable on **any CUDA GPU** — no minimum compute capability, no custom kernels, no CUDA 13. If you have been unable to run LTX-2.5 because the text encoder alone wanted 26 GB, this is the part that was in your way. Drop it in and the rest of the model is unchanged. Built from the encoder Lightricks published on 2026-08-17 (`1b92891c`, *"Aligns the published encoders with the LTX-2.5 model checkpoints"*) — the revision that matches the released DiT. ## Why this one Every other public quantization of this encoder needs recent hardware: | build | size | needs | |---|---:|---| | Lightricks BF16 | 26.264 GB | — | | Lightricks `comfy-int8-convrot` | 15.373 GB | cc 8.9 | | DmitryDB `nvfp4` | 11.197 GB | cc 8.9, `comfy_kitchen`, CUDA 13 | | joeygambino / Winnougan `w4a8` | 10.604 GB | SM 8.0+ | | **this** | **8.46 GB** | **nothing beyond PyTorch** | Dequantization happens on the CPU at load and the resident model is BF16, so there is no kernel requirement to satisfy. Verified running on **Tesla V100 (cc 7.0)**, **A100 (cc 8.0)** and **RTX 6000 Ada (cc 8.9)**. It is also the smallest of the set, because it quantizes the three tensors the other nvfp4 recipe protects as BF16 "precision islands" — `embed_tokens` and both aggregate tables, 4.4 GB of the source. ## Files | file | build | video relL2 | audio relL2 | `\|\|Q\|\|/\|\|W\|\|` | |---|---|---:|---:|---:| | `A3.packed.safetensors` | bypass guard + group-bounded AWQ | **0.05204** | **0.04827** | **4.216** | | `A0.packed.safetensors` | legacy guard, for comparison | 0.06061 | 0.06095 | 302.654 | **Use `A3`.** `A0` is published only so the comparison can be checked; it carries weights up to 300x their proper norm in near-dead channels, which is harmless on this calibration set and fragile by construction. ## Samples `samples/` holds every clip twice — once from the **BF16 original (26.264 GB)** and once from this **4-bit build**, with everything downstream of the encoder held identical: same DiT, same seed, same schedule, same VAE settings, one process. `compare-NN.mp4` stacks each pair, BF16 on the left. Two sets, because they answer different questions. `compare-NN.mp4` uses the vendor's `euler_ancestral`, which is what you actually get; it re-rolls noise every step, so the two builds return different *takes*. `compare-det-NN.mp4` uses deterministic `euler`, where the seed fixes the starting noise and the conditioning is the only thing left that can move a pixel — that is the set that attributes a difference to the encoder. Mean absolute error against BF16 over every decoded frame: | clip | `euler_ancestral` | `euler` | |---|---:|---:| | robot in rain | 0.0172 | 0.0182 | | dune | 0.0596 | 0.0580 | | forge | 0.0098 | 0.0129 | | night road | 0.0702 | 0.0252 | | smoke | 0.0128 | 0.0165 | **Four of the five deterministic pairs hold together** — same composition, same lighting, same timing, differing in surface detail. **The dune does not**: BF16 renders a soldier in fatigues where the 4-bit renders a man in a business suit, from the same seed under a deterministic sampler. That difference belongs to the encoder, and it is published rather than cropped out. The qualification it deserves is that neither build followed that prompt — it asked for an astronaut and got neither — so the model had no confident answer there for a small conditioning change to disturb. All five deterministic pairs at a glance — BF16 left, 4-bit right, one row per prompt. Rows 1, 3, 4 and 5 hold together; row 2, the dune, is where the compression is visible. ![BF16 against 4-bit, five deterministic pairs](https://huggingface.co/topabaem/LTX-2.5-Text-Encoder-4bit-8GB/resolve/main/samples/comparison-sheet.png) ### 15 seconds, 1024x640, generated in the Space ![poster](https://huggingface.co/topabaem/LTX-2.5-Text-Encoder-4bit-8GB/resolve/main/samples/moe-idol-poster.png) 361 frames with sound, from a 300-word prompt naming ten sequenced gestures, made entirely through [the Space](https://huggingface.co/spaces/topabaem/LTX-2.5-Text-Encoder-4bit-8GB-Demo) on ZeroGPU in 198 s. The full prompt is in [`samples/idol/prompt.txt`](https://huggingface.co/topabaem/LTX-2.5-Text-Encoder-4bit-8GB/resolve/main/samples/idol/prompt.txt). Two things had to be fixed before a clip this long held together, and both were in this project's own defaults rather than in the compression: * **The sampling schedule did not adapt to length.** `DISTILLED_SIGMAS` is nine fixed numbers; `LTXVScheduler` derives its shift from the latent's token count, and a long clip carries several times more. Left fixed, the sample never converges — furniture renders semi-transparent and saturation halves. Clips past 15 s now switch automatically. * **The vendor's second pass was being skipped**, on an unmeasured assumption that a 16 GB card could not afford it. It can, at 10.03 GiB, and it is worth 4.1x the Laplacian variance. Details, the measurements, and the failures behind both are in [`samples/long/schedule.md`](https://huggingface.co/topabaem/LTX-2.5-Text-Encoder-4bit-8GB/resolve/main/samples/long/schedule.md). ### The samples, as the vendor's pipeline actually renders them ![full pipeline renders, 1024x640](https://huggingface.co/topabaem/LTX-2.5-Text-Encoder-4bit-8GB/resolve/main/samples/sharp/sheet.png) Six clips at **1024x640** — sample at 512x320, upscale the latent 2x, sample again. That second pass had been skipped here from the start on an assumption that a 16 GB card could not afford it; measured, it peaks at 10.03 GiB, and it is worth **4.1x the Laplacian variance**. Everything else on this page is one-pass and softer for it. [`samples/sharp/`](https://huggingface.co/topabaem/LTX-2.5-Text-Encoder-4bit-8GB/resolve/main/samples/sharp/README.md), including the one of the six that does not follow its prompt and why. ### Fifteen seconds, on one 16 GB card 353 frames at 512x320, generated in 123 s at a 6.26 GiB peak — no more memory than the ten-second version, so the length ceiling was not reached. The prompt and an honest account of what it did *not* do — the audio is not speech, and the "2D anime" instruction is ignored at other lengths by the BF16 original too — are in [`samples/idol/`](https://huggingface.co/topabaem/LTX-2.5-Text-Encoder-4bit-8GB/resolve/main/samples/idol/README.md). ### Watch the two that matter **Forge — the pair holding together.** Deterministic sampler, same seed. Left is BF16, right is 4-bit. **Dune — the pair that does not.** Same terrain, same sun, same shadow, same walk, and a different person. Individually, the forge under the vendor's own sampler: | BF16 original | 4-bit | |---|---| | | | `samples/README.md` carries the prompts, the per-clip conditioning drift, and what these clips do and do not establish. All thirty clips are in `samples/`, and the Space plays them side by side under its **BF16 vs 4-bit** tab. ## Memory — what the card actually needs | | resident (default) | dequantized | |---|---:|---:| | PyTorch allocated, peak | 8.48 GiB | ~22.3 GiB | | PyTorch reserved, peak | 9.33 GiB | — | | **what `nvidia-smi` shows** | **9.70 GiB** | — | **Size your card from the last row.** The first is `max_memory_allocated`, which counts only live allocator blocks — it misses the CUDA context and everything the caching allocator reserved and has not handed back, and it undercounts by nearly 2 GiB here. Earlier versions of this card quoted that number, and anyone who bought an 8 GB card on the strength of it would have been wrong. Measured on a **Tesla V100-SXM2-16GB (cc 7.0)** with a 55-token prompt, through the ComfyUI-faithful path that left-pads to 1024 tokens. So: * **16 GB and up** — comfortable. * **12 GB** — fits. * **10 GB** — fits, with roughly 300 MB of headroom. Nothing else on the card. * **8 GB** — does not fit. The two modes produce `torch.equal` conditioning, so the choice is footprint against speed and never quality. Resident dequantizes inside `forward`, which costs time on short prompts; dequantized builds one dense BF16 model at load and then needs a card that can hold 26 GB. These figures are for the **text encoder alone**. Generating video also needs the DiT and the VAEs, which this repository does not contain — the pipeline in the Space loads a `Q3_K_M` DiT alongside it. ## Use it ### Standalone Five packages and one file. No build step, no custom CUDA kernels, no compilation. ```bash pip install -r <(curl -sL https://huggingface.co/topabaem/LTX-2.5-Text-Encoder-4bit-8GB/resolve/main/requirements.txt) ``` ```python from huggingface_hub import hf_hub_download, snapshot_download repo = "topabaem/LTX-2.5-Text-Encoder-4bit-8GB" # The loader ships with the weights; put it on the path before importing it. import sys, os sys.path.insert(0, os.path.dirname(hf_hub_download(repo, "ltx_packed_codec.py"))) from ltx_packed_codec import load_packed_model from transformers import AutoTokenizer packed = hf_hub_download(repo, "A3.packed.safetensors") encoder_dir = snapshot_download(repo, allow_patterns=["encoder-hf/*"]) + "/encoder-hf" model = load_packed_model(encoder_dir, packed, resident=True) tokenizer = AutoTokenizer.from_pretrained(encoder_dir) ``` Verified end to end in a clean virtualenv containing nothing but those five packages, on **torch 2.13.0 / transformers 5.15.1** and on **torch 2.10.0 / transformers 5.12.1**. `encoder-hf/` is config and tokenizer only, 31 MB — the 26 GB original is not needed. ### What your GPU has to support Nothing unusual. The format needs no fp8 hardware: the group scales are stored as `float8_e4m3fn` bytes and converted in software during a CPU-side decode, so `float8` here is a container and never an instruction. There is no minimum compute capability, no `comfy_kitchen`, no CUDA 13. The resident model is BF16 and runs on cards with no bf16 tensor cores at all. **The one real constraint is your torch wheel, not your GPU.** The default wheel on PyPI is now a cu130 build and cu130 dropped Volta. Measured on a V100: | torch build | device | result | |---|---|---| | 2.13.0+cu130 | CPU | works | | 2.13.0+cu130 | V100, sm_70 | **`no kernel image is available`** | | 2.10.0+cu128 | V100, sm_70 | works | That failure arrives at the *first kernel launch*, well after `torch.cuda.is_available()` has returned `True`, so it does not look like an installation problem — which is why **the loader checks first**. Before reading a byte of the 8.46 GB it compares your card against `torch.cuda.get_arch_list()` and, on a mismatch, stops with the fix: ``` this torch (2.13.0+cu130) has no kernels for Tesla V100-SXM2-16GB (sm_70). It was built for sm_75, sm_80, sm_86, sm_90, sm_100, sm_120, and the first CUDA op would fail with 'no kernel image is available for execution on the device'. The model is fine - it needs no custom kernels. Install a torch built for your card, e.g. for sm_70: pip install torch --index-url https://download.pytorch.org/whl/cu128 or pass device='cpu' to load without touching the GPU. ``` On sm_70 install a cu128 build: ```bash pip install torch --index-url https://download.pytorch.org/whl/cu128 ``` Ampere and newer are unaffected — the stock wheel carries kernels for them. ### ComfyUI `comfy_nodes/ComfyUI-LTXPacked/` provides **LTX Packed Encoder Loader** and **LTX Packed Text Encode**, which replace `CLIPTextEncode` and emit a CONDITIONING directly. They do not go through `LTXAVTextEncoderLoader`, and cannot: ComfyUI's LTX CLIP path wants a sentencepiece `spiece_model` where this checkpoint carries `tokenizer_json`, and its nvfp4 support requires `comfy_kitchen` plus CUDA 13 plus cc 8.9 — the floor this file exists to avoid. The DiT only ever needed a CONDITIONING. > **Not yet run inside ComfyUI.** The encode path is the same code this > project's `ltx_conditioning_dump.py` runs daily and the conditioning wrapper > is lifted verbatim from its renderer, so the pieces are exercised — but the > nodes themselves have not been loaded in a live ComfyUI, and that is a > different claim. ## Try it **[Space: LTX-2.5 Text Encoder 4bit](https://huggingface.co/spaces/topabaem/LTX-2.5-Text-Encoder-4bit-8GB-Demo)** — text-, image- and video-to-video, running this encoder on ZeroGPU. Measured there at 512x320, 25 frames: t2v 53.0 s, i2v 46.9 s, v2v 37.6 s. ## Image- and video-to-video Both work, and both needed a fix ComfyUI does not ship. `LTXVAddGuide` is the only producer of guided LTX latents and it cannot take an LTX-2.5 one: it calls `torch.cat` on what is a `NestedTensor` for this model. The `ValueError` in that function saying AV guides are unsupported never fires — `NestedTensor.shape` proxies to the video half, whose channel count is exactly the 128 it checks for — so the real failure is a `TypeError`, and the message is stale. Everything below the node already supports AV guides: the model routes `keyframe_idxs` to the video branch, the sampler pads a video-only denoise mask with ones for audio, and `model_base` splits the packed mask apart again. So `ltx_av_guide.py` unwraps the pair, runs the stock node on the video half and re-wraps; the guide arithmetic stays the vendor's. Measured on a 16 GB V100 at 512x320, 25 frames: t2v 98.8 s / 5.84 GiB, i2v 62.7 s / 6.80 GiB, v2v 86.5 s / 5.84 GiB. An i2v first frame lands **relL2 0.0766** from its guide image against **0.7061** for the same seed and prompt without the guide, so the guide is honoured rather than merely accepted. ## How it was built nvfp4 4.5 bpw (E2M1, group 16 with an fp8-e4m3 scale) on 320 projections; int8 row-wise on `embed_tokens` and both aggregate tables; norms and asset blobs left BF16. Per-tensor AWQ alpha search, then sequential GPTQ error compensation (blocksize 128, percdamp 0.01), packed inside the build because the group scales cannot be recovered afterwards. `A3` adds three things the plain recipe lacks: 1. **No weight column is ever zeroed.** Channels are classified by an *absolute* activation RMS, not a threshold relative to `mean(diag(H))` — the relative rule moves with the calibration Hessian's dynamic range, which made the old guard's best setting differ between Volta and Ada. 2. **The AWQ scale is shaped to the storage grid.** nvfp4 carries one scale per 16 input channels and its E2M1 grid spans 12:1; an unshaped per-channel scale spans far wider inside a group and pushes the low channels under the grid floor, where they quantize to exactly zero. Bounding the spread to 12:1 keeps both the smoothing and the channels. The identity `x·diag(1/s) @ Q(W·diag(s))ᵀ` holds for any `s`, so this needs no format change. 3. **Escalating damping, recorded.** Group shaping removes conditioning that per-channel smoothing was supplying as a side effect; `A3` needed a 10x escalation, written into the artifact metadata so a damped build is never silently compared with an undamped one. ## Measured limits * **Packing is lossless.** A twin BF16 build plus a full `verify`: all 686 tensors value-exact. No drift here is attributable to the format. * **GPTQ builds do not reproduce across GPU architectures.** Same code, plan, calibration and guard: V100 0.06879, Ada 0.11210 on the older source. Within one architecture they reproduce to five decimals across separate machines. These files were built on an **A100**; quote that alongside any figure. * **No KL or CE ratio is reported.** This artifact carries no LM head, so vocabulary KL and CE ratio are undefined for its deployment path. Measured instead: conditioning relL2/cosine per branch, and a five-prompt render comparison against a BF16 conditioning from the same card, where `A3` is closer on four of five (mean 0.05931 against `A0`'s 0.06571). * **Not evaluated**: human listening, native DiT cross-attention KL, or whether the remaining gap to BF16 is visible at all in finished video. * Prompts asking for four-legged or wheeled robots still render humans. That happens with the BF16 encoder too — a model limit, not compression damage. ## Provenance Source `Lightricks/LTX-2.5` revision `1b92891c`+, torch 2.11.0+cu128, transformers 5.14.1, plan `r45c`, calibration `calib-large.txt`. Evidence in `evidence/`: gate JSON, build logs with per-layer drift and `||Q||/||W||`, the A100 BF16 reference, all three conditionings, and fifteen rendered clips. Earlier builds against the pre-2026-08-17 encoder, and the method results that came from them, are at `topabaem/Pacific-LTX-2.5-Encoder-r45d`.