| --- |
| 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 |
| --- |
| |
| [](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. |
|
|
|  |
|
|
| ### 15 seconds, 1024x640, generated in the Space |
|
|
| <video controls width="100%" poster="https://huggingface.co/topabaem/LTX-2.5-Text-Encoder-4bit-8GB/resolve/main/samples/moe-idol-poster.png" src="https://huggingface.co/topabaem/LTX-2.5-Text-Encoder-4bit-8GB/resolve/main/samples/moe-idol-15s.mp4"></video> |
|
|
|  |
|
|
| 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 |
|
|
|  |
|
|
| 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 |
|
|
| <video controls width="100%" src="https://huggingface.co/topabaem/LTX-2.5-Text-Encoder-4bit-8GB/resolve/main/samples/idol/idol-15s.mp4"></video> |
|
|
| 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. |
|
|
| <video controls width="100%" src="https://huggingface.co/topabaem/LTX-2.5-Text-Encoder-4bit-8GB/resolve/main/samples/compare-det-02.mp4"></video> |
|
|
| **Dune — the pair that does not.** Same terrain, same sun, same shadow, same |
| walk, and a different person. |
|
|
| <video controls width="100%" src="https://huggingface.co/topabaem/LTX-2.5-Text-Encoder-4bit-8GB/resolve/main/samples/compare-det-01.mp4"></video> |
|
|
| Individually, the forge under the vendor's own sampler: |
|
|
| | BF16 original | 4-bit | |
| |---|---| |
| | <video controls width="100%" src="https://huggingface.co/topabaem/LTX-2.5-Text-Encoder-4bit-8GB/resolve/main/samples/bf16-02.mp4"></video> | <video controls width="100%" src="https://huggingface.co/topabaem/LTX-2.5-Text-Encoder-4bit-8GB/resolve/main/samples/4bit-02.mp4"></video> | |
|
|
| `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`. |
|
|