Instructions to use ostris/primal_vae with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use ostris/primal_vae with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("ostris/primal_vae", 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
Update weights for 2x1 to latest step
Browse files- README.md +16 -16
- assets/compare_2x1.svg +0 -0
- primal_vae_2x1.safetensors +2 -2
README.md
CHANGED
|
@@ -84,10 +84,10 @@ A diffusion model is trained against Gaussian noise, so the latent should look l
|
|
| 84 |
KL term and a scaling factor someone measures after the fact, I put the statistics directly in the loss on the
|
| 85 |
encoder output.
|
| 86 |
|
| 87 |
-
- **Moment loss**
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
- **Gaussian order-statistic loss**
|
| 91 |
against the expected order statistics of a standard normal of the same size (Blom's approximation). This
|
| 92 |
pins the whole distribution shape, not just the first two moments, so a flat gray image still encodes to
|
| 93 |
something that looks like noise instead of a constant.
|
|
@@ -109,15 +109,15 @@ PSNR on these three 512 px images (dB):
|
|
| 109 |
| image | Primal 2x1 | Flux.1 VAE | Primal 2x2 | Flux.2 VAE |
|
| 110 |
|---|---|---|---|---|
|
| 111 |
| img01 (people, studio) | 32.1 | 35.7 | 37.5 | 37.2 |
|
| 112 |
-
| img02 (picnic, foliage) | 22.
|
| 113 |
-
| img03 (UI screenshot) | 21.
|
| 114 |
|
| 115 |
The 2x2 matches or beats the Flux.2 VAE here at the same information budget (both are 32 values per 8×8 pixel
|
| 116 |
block). Three images and PSNR alone are not a benchmark, and a KL VAE pays a reconstruction tax for its posterior
|
| 117 |
noise that a deterministic one does not. The 2x1 trails the Flux.1 VAE on textured content. One channel at 2x
|
| 118 |
-
is a 12:1 reduction with no slack, and it has
|
| 119 |
-
latent std
|
| 120 |
-
|
| 121 |
|
| 122 |
## Training
|
| 123 |
|
|
@@ -135,13 +135,13 @@ The tricks that should make the latent space easier to diffuse:
|
|
| 135 |
- **Noise-perturbed decode.** On 20% of steps the decoder gets `z + sigma * noise`, `sigma` drawn per image
|
| 136 |
from `U(0, 0.5)`, and still has to reconstruct the clean image. The decoder learns to tolerate diffusion
|
| 137 |
model error and the latent gets smooth: nearby codes decode to nearby images.
|
| 138 |
-
- **Solid colors**
|
| 139 |
-
|
| 140 |
-
- **Critic** (`2x1`): a frozen SigLIP2 NaFlex ViT gives patch features from layers 4, 8
|
| 141 |
-
reconstructed image at native resolution. Small spectral-normalized conv heads on
|
| 142 |
-
a hinge loss plus R1, and the decoder gets the generator term at weight 0.1,
|
| 143 |
-
steps. It sharpens the texture that MAE and LPIPS leave soft. The 2x2 was
|
| 144 |
-
seemed to converge fine without one.
|
| 145 |
|
| 146 |
Every step is one encode and one decode. On a flip/rotate step the transformed pair replaces the clean pair
|
| 147 |
instead of adding a second decoder pass.
|
|
|
|
| 84 |
KL term and a scaling factor someone measures after the fact, I put the statistics directly in the loss on the
|
| 85 |
encoder output.
|
| 86 |
|
| 87 |
+
- **Moment loss**: `mean^2 + (std - 1)^2`, weight 0.1, taken per image and per channel, so every single image
|
| 88 |
+
comes out mean 0 / std 1 in each channel, not just the dataset on average. The 2x1 spent its first 53k steps
|
| 89 |
+
with this pooled over the batch and switched to per image for the last 9k.
|
| 90 |
+
- **Gaussian order-statistic loss**: sort the latent values of each image and channel and MSE them
|
| 91 |
against the expected order statistics of a standard normal of the same size (Blom's approximation). This
|
| 92 |
pins the whole distribution shape, not just the first two moments, so a flat gray image still encodes to
|
| 93 |
something that looks like noise instead of a constant.
|
|
|
|
| 109 |
| image | Primal 2x1 | Flux.1 VAE | Primal 2x2 | Flux.2 VAE |
|
| 110 |
|---|---|---|---|---|
|
| 111 |
| img01 (people, studio) | 32.1 | 35.7 | 37.5 | 37.2 |
|
| 112 |
+
| img02 (picnic, foliage) | 22.0 | 24.2 | 27.1 | 26.5 |
|
| 113 |
+
| img03 (UI screenshot) | 21.3 | 24.7 | 27.4 | 25.9 |
|
| 114 |
|
| 115 |
The 2x2 matches or beats the Flux.2 VAE here at the same information budget (both are 32 values per 8×8 pixel
|
| 116 |
block). Three images and PSNR alone are not a benchmark, and a KL VAE pays a reconstruction tax for its posterior
|
| 117 |
noise that a deterministic one does not. The 2x1 trails the Flux.1 VAE on textured content. One channel at 2x
|
| 118 |
+
is a 12:1 reduction with no slack, and it has well under half the training steps of the 2x2 (62k vs 147k). Its
|
| 119 |
+
per-image latent std still drifts (1.07 to 1.21 here) because it only got the per-image normalization for its
|
| 120 |
+
last 9k steps. The 2x2, trained with it throughout, holds std within a few percent of 1.
|
| 121 |
|
| 122 |
## Training
|
| 123 |
|
|
|
|
| 135 |
- **Noise-perturbed decode.** On 20% of steps the decoder gets `z + sigma * noise`, `sigma` drawn per image
|
| 136 |
from `U(0, 0.5)`, and still has to reconstruct the clean image. The decoder learns to tolerate diffusion
|
| 137 |
model error and the latent gets smooth: nearby codes decode to nearby images.
|
| 138 |
+
- **Solid colors**: 5% of images are swapped for a random flat color. Flat regions are the hard case for a
|
| 139 |
+
per-image normalized latent.
|
| 140 |
+
- **Critic** (`2x1` only, last 9k steps): a frozen SigLIP2 NaFlex ViT gives patch features from layers 4, 8
|
| 141 |
+
and 12 of the real and reconstructed image at native resolution. Small spectral-normalized conv heads on
|
| 142 |
+
those features train with a hinge loss plus R1, and the decoder gets the generator term at weight 0.1,
|
| 143 |
+
ramped in over the first 1000 steps. It sharpens the texture that MAE and LPIPS leave soft. The 2x2 was
|
| 144 |
+
trained without a critic, it seemed to converge fine without one.
|
| 145 |
|
| 146 |
Every step is one encode and one decode. On a flip/rotate step the transformed pair replaces the clean pair
|
| 147 |
instead of adding a second decoder pass.
|
assets/compare_2x1.svg
CHANGED
|
|
|
|
primal_vae_2x1.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b035c96861e3d36ed0a5308e89535ba19c26846d98c358a3907949ff8d1776f3
|
| 3 |
+
size 321415516
|