MinhNH232331M commited on
Commit
8399861
·
verified ·
1 Parent(s): 24391ab

Upload folder using huggingface_hub

Browse files
README.md ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ library_name: diffusers
3
+ base_model: microsoft/Mage-Flow
4
+ base_model_relation: adapter
5
+ tags:
6
+ - vae
7
+ - autoencoder
8
+ - flux.2
9
+ - image-generation
10
+ ---
11
+
12
+ # MegaFlow VAE (diffusers)
13
+
14
+ A 🧨 diffusers-native `AutoencoderKLMega` port of the VAE from
15
+ [microsoft/Mage-Flow](https://huggingface.co/microsoft/Mage-Flow)
16
+ (DConvEncoder + DConvDenoiser/CoD decoder), usable as a drop-in adapter for
17
+ pipelines that expect a Flux.2-style VAE.
18
+
19
+ - **Encode:** one-step diffusion encoder (t=0) → `latent_dist` over `(B, 32, H/8, W/8)`
20
+ - **Decode:** DConvDenoiser + CoD decoder (t=0) → image `(B, 3, H, W)` in `[-1, 1]`
21
+ - **Latent space:** shaped *and* valued like the raw Flux.2 VAE latent — the native
22
+ 128-channel `H/16` code is 2×2-unpatchified and denormalized with the Flux.2 BN
23
+ latent stats stored in `config.json` (anchor-latent regularization,
24
+ [arXiv:2607.19064](https://arxiv.org/abs/2607.19064)). No dependency on the
25
+ Flux.2 VAE at runtime (0.972 mean latent correlation over a 50-image test
26
+ set; see the [comparison](#comparison-with-the-original-flux2-vae) below).
27
+ - **Checkpoint:** bf16, with the t=0 adaLN MLPs constant-folded at conversion time
28
+ (`folded: true` in the config), so it is ready to run on load.
29
+
30
+ > [!NOTE]
31
+ > The latents exposed by this model are **not** the native MegaVAE code. The
32
+ > native `(B, 128, H/16, W/16)` code is **2×2-unpatchified** to
33
+ > `(B, 32, H/8, W/8)` and **denormalized** with the Flux.2 BN latent statistics
34
+ > (stored in `config.json`) so that `encode`/`decode` operate directly in the
35
+ > original Flux.2 VAE latent space. Latents from this model can be decoded by
36
+ > the Flux.2 VAE and vice versa.
37
+
38
+ ## Comparison with the original Flux.2 VAE
39
+
40
+ Measured against `black-forest-labs/FLUX.2-dev` (subfolder `vae`,
41
+ `AutoencoderKLFlux2`) on an NVIDIA L4, bf16, batch 1 (torch 2.8.0,
42
+ diffusers 0.37.1). Speed/memory at 1024×1024, means over 5 runs after warmup,
43
+ memory is peak CUDA allocation during the op. Quality is the mean over a
44
+ 50-image test set (06JPEG, ~2040×1524 photos, center-cropped to a multiple
45
+ of 16, evaluated at native resolution with deterministic `.mode()` latents):
46
+
47
+ | | MegaFlow VAE (this repo) | Flux.2 VAE |
48
+ | --- | --- | --- |
49
+ | Parameters | 100.8M | 84.0M |
50
+ | Encode time | **23 ms** (~10× faster) | 240 ms |
51
+ | Decode time | **71 ms** (~6× faster) | 441 ms |
52
+ | Encode peak memory | **0.46 GiB** | 1.90 GiB |
53
+ | Decode peak memory | **0.98 GiB** | 2.78 GiB |
54
+ | Roundtrip PSNR (50 images) | 34.1 dB | 34.7 dB |
55
+
56
+ **Quality** is on par with the original: same-VAE roundtrip reconstruction
57
+ averages within ~0.5 dB of the Flux.2 VAE over the 50-image set. The latent
58
+ spaces are interchangeable — mean latent correlation with raw Flux.2 latents
59
+ is **0.972** (stable per image, ~0.97 on every one of the 50), and
60
+ cross-decoding (`flux.decode(mega.encode(x))` at 34.0 dB,
61
+ `mega.decode(flux.encode(x))` at 33.8 dB) stays within ~0.7 dB of the
62
+ same-VAE roundtrip.
63
+
64
+ **Repeated roundtrips** hold up slightly *better* than the original: MegaFlow
65
+ starts ~0.5 dB below the Flux.2 VAE at one roundtrip but degrades more
66
+ slowly, matching it by the second cycle and leading by ~1.1 dB after five —
67
+ so it is well suited to iterative editing workflows. Alternating the two
68
+ VAEs each cycle (MegaFlow encode → Flux.2 decode) tracks in between
69
+ (50-image means):
70
+
71
+ | PSNR vs original (dB) | k=1 | k=2 | k=3 | k=4 | k=5 |
72
+ | --- | --- | --- | --- | --- | --- |
73
+ | MegaFlow VAE ×k | 34.1 | 32.1 | **30.3** | **28.9** | **27.7** |
74
+ | Flux.2 VAE ×k | **34.7** | 32.1 | 29.9 | 28.1 | 26.5 |
75
+ | Alternating (Mega enc → Flux.2 dec) | 34.0 | 31.5 | 29.5 | 27.8 | 26.4 |
76
+
77
+ **Speed** comes from the one-step architecture: both directions run a single
78
+ t=0 forward pass of depthwise-conv (DiCo) blocks with the adaLN modulation
79
+ constant-folded into the checkpoint, instead of the Flux.2 VAE's deep
80
+ ResNet/attention encoder-decoder.
81
+
82
+ **Memory** stays flat at high resolution: the decoder's per-patch MLP tail is
83
+ chunked (`decode_chunk_size`, default 4096 = one 1024×1024 image worth of
84
+ 16×16 patches), so decode peak memory is roughly constant beyond 1024×1024
85
+ instead of growing with image area.
86
+
87
+ ## Files
88
+
89
+ | File | Purpose |
90
+ | --- | --- |
91
+ | `autoencoder_kl_mega.py` | Self-contained: `AutoencoderKLMega` (ModelMixin/ConfigMixin), all network blocks, and the one-time checkpoint converter |
92
+ | `config.json` | Model config, incl. Flux.2 BN latent stats |
93
+ | `diffusion_pytorch_model.safetensors` | Folded bf16 weights |
94
+
95
+ ## Usage
96
+
97
+ The model class lives in this repo, so grab the code files alongside the weights:
98
+
99
+ ```python
100
+ import sys
101
+
102
+ import torch
103
+ from huggingface_hub import snapshot_download
104
+
105
+ repo_dir = snapshot_download("<your-username>/MegaFlow-VAE-diffusers")
106
+ sys.path.insert(0, repo_dir)
107
+
108
+ from autoencoder_kl_mega import AutoencoderKLMega
109
+
110
+ vae = AutoencoderKLMega.from_pretrained(repo_dir, torch_dtype=torch.bfloat16).to("cuda")
111
+
112
+ image = torch.rand(1, 3, 1024, 1024, device="cuda", dtype=torch.bfloat16) * 2 - 1
113
+ latents = vae.encode(image).latent_dist.sample() # (1, 32, 128, 128), Flux.2 latent space
114
+ recon = vae.decode(latents, return_dict=False)[0] # (1, 3, 1024, 1024) in [-1, 1]
115
+ ```
116
+
117
+ Because the latents are shape- and value-compatible with the Flux.2 VAE, you can
118
+ swap this in as the `vae` of a Flux.2 pipeline:
119
+
120
+ ```python
121
+ pipe.vae = vae
122
+ ```
123
+
124
+ Input `H`/`W` must be multiples of 16. `from_pretrained` accepts
125
+ `fold_adaln=False` if you need the unfolded adaLN MLPs (only relevant for
126
+ unfolded checkpoints).
127
+
128
+ ## Requirements
129
+
130
+ ```
131
+ torch
132
+ diffusers>=0.37
133
+ safetensors
134
+ loguru
135
+ ```
136
+
137
+ ## Conversion
138
+
139
+ The checkpoint here was produced from the original CoD checkpoint layout with:
140
+
141
+ ```bash
142
+ python autoencoder_kl_mega.py # convert_mega_ckpt: MegaFlow/vae -> MegaFlow/vae_diffusers
143
+ ```
144
+
145
+ which remaps `student.dconv_encoder.* -> encoder.*` and `pipeline.* -> decoder.*`,
146
+ carries the Flux.2 BN stats into the config, constant-folds the t=0 adaLN MLPs
147
+ (~74 MB smaller), and saves in bf16.
__pycache__/autoencoder_kl_mega.cpython-313.pyc ADDED
Binary file (49.1 kB). View file
 
autoencoder_kl_mega.py ADDED
@@ -0,0 +1,753 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Diffusers-native Mage-VAE (DConvEncoder + DConvDenoiser/CoD decoder).
3
+
4
+ AutoencoderKLMega mirrors the AutoencoderKLFlux2Asym API surface used in this
5
+ project — ModelMixin/ConfigMixin, `from_pretrained`/`save_pretrained`,
6
+ `encode(x).latent_dist`, `decode(z, return_dict=False)[0]` — while exposing
7
+ latents shaped AND valued like the raw Flux.2 VAE: (B, 32, H/8, W/8),
8
+ 2x2-unpatchified from the native 128ch @ H/16 code and denormalized with the
9
+ Flux.2 BN latent stats stored in the model config (anchor-latent
10
+ regularization, arXiv:2607.19064). No dependency on the Flux.2 VAE at runtime.
11
+
12
+ Convert the original CoD checkpoint layout once:
13
+
14
+ python autoencoder_kl_mega.py # MegaFlow/vae -> MegaFlow/vae_diffusers
15
+
16
+ then load with:
17
+
18
+ vae = AutoencoderKLMega.from_pretrained("MegaFlow/vae_diffusers", torch_dtype=torch.bfloat16)
19
+ """
20
+
21
+ from typing import List, Optional
22
+ import math
23
+ from functools import lru_cache
24
+
25
+ import torch
26
+ import torch.nn as nn
27
+ import torch.nn.functional as F
28
+ from diffusers.configuration_utils import ConfigMixin, register_to_config
29
+ from diffusers.models.autoencoders.vae import DecoderOutput, DiagonalGaussianDistribution
30
+ from diffusers.models.modeling_outputs import AutoencoderKLOutput
31
+ from diffusers.models.modeling_utils import ModelMixin
32
+ from loguru import logger
33
+
34
+
35
+ # ---------------------------------------------------------------------------
36
+ # Primitive layers (vendored from GenCodec, inference subset)
37
+ # ---------------------------------------------------------------------------
38
+ def nonlinearity(x):
39
+ return x * torch.sigmoid(x)
40
+
41
+
42
+ def Normalize(in_channels):
43
+ return torch.nn.GroupNorm(num_groups=32, num_channels=in_channels, eps=1e-6, affine=True)
44
+
45
+
46
+ def modulate(x, shift, scale):
47
+ if x.dim() == 4:
48
+ b, c = x.shape[:2]
49
+ return x * (1 + scale.view(b, c, 1, 1)) + shift.view(b, c, 1, 1)
50
+ return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1)
51
+
52
+
53
+ class LayerNorm2d(nn.LayerNorm):
54
+ def __init__(self, num_channels, eps=1e-6, affine=True):
55
+ super().__init__(num_channels, eps=eps, elementwise_affine=affine)
56
+
57
+ def forward(self, x):
58
+ # .contiguous() prevents a channels_last-strided NCHW view from
59
+ # propagating into downstream depthwise convs, which would otherwise
60
+ # hit a slow cuDNN path with a per-shape heuristic search.
61
+ x = x.permute(0, 2, 3, 1).contiguous()
62
+ x = F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps)
63
+ return x.permute(0, 3, 1, 2).contiguous()
64
+
65
+
66
+ class _EncoderLayerNorm2d(LayerNorm2d):
67
+ pass
68
+
69
+
70
+ class RMSNorm(nn.Module):
71
+ def __init__(self, hidden_size, eps=1e-6):
72
+ super().__init__()
73
+ self.weight = nn.Parameter(torch.ones(hidden_size))
74
+ self.variance_epsilon = eps
75
+
76
+ def forward(self, x):
77
+ in_dtype = x.dtype
78
+ x = x.to(torch.float32)
79
+ var = x.pow(2).mean(-1, keepdim=True)
80
+ x = x * torch.rsqrt(var + self.variance_epsilon)
81
+ return self.weight * x.to(in_dtype)
82
+
83
+
84
+ class TimestepEmbedder(nn.Module):
85
+ """DConv-style timestep MLP (max_period=10000, freq_size=256, hidden=384)."""
86
+
87
+ def __init__(self, hidden_size, frequency_embedding_size=256):
88
+ super().__init__()
89
+ self.mlp = nn.Sequential(
90
+ nn.Linear(frequency_embedding_size, hidden_size, bias=True),
91
+ nn.SiLU(),
92
+ nn.Linear(hidden_size, hidden_size, bias=True),
93
+ )
94
+ self.frequency_embedding_size = frequency_embedding_size
95
+
96
+ @staticmethod
97
+ def timestep_embedding(t, dim, max_period=10000):
98
+ half = dim // 2
99
+ freqs = torch.exp(
100
+ -math.log(max_period) * torch.arange(0, half, dtype=torch.float32) / half
101
+ ).to(t.device)
102
+ args = t[:, None].float() * freqs[None]
103
+ emb = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
104
+ if dim % 2:
105
+ emb = torch.cat([emb, torch.zeros_like(emb[:, :1])], dim=-1)
106
+ return emb
107
+
108
+ def forward(self, t):
109
+ emb = self.timestep_embedding(t, self.frequency_embedding_size)
110
+ return self.mlp(emb.to(self.mlp[0].weight.dtype))
111
+
112
+
113
+ class BottleneckPatchEmbed(nn.Module):
114
+ """Image patch embed concatenated with a per-patch conditioning vector."""
115
+
116
+ def __init__(self, patch_size=16, in_chans=3, pca_dim=128, embed_dim=384, bias=True):
117
+ super().__init__()
118
+ self.proj1 = nn.Conv2d(in_chans, pca_dim, kernel_size=patch_size, stride=patch_size, bias=False)
119
+ self.proj2 = nn.Conv2d(pca_dim + embed_dim, embed_dim, kernel_size=1, bias=bias)
120
+
121
+ def forward(self, x, cond):
122
+ return self.proj2(torch.cat([self.proj1(x), cond], dim=1))
123
+
124
+
125
+ class DiCoBlock(nn.Module):
126
+ """DConv block with adaLN modulation."""
127
+
128
+ def __init__(self, hidden_size, mlp_ratio=4.0):
129
+ super().__init__()
130
+ self.conv1 = nn.Conv2d(hidden_size, hidden_size, 1, bias=True)
131
+ self.conv2 = nn.Conv2d(hidden_size, hidden_size, 3, padding=1, groups=hidden_size, bias=True)
132
+ self.conv3 = nn.Conv2d(hidden_size, hidden_size, 1, bias=True)
133
+
134
+ self.ca = nn.Sequential(
135
+ nn.AdaptiveAvgPool2d(1),
136
+ nn.Conv2d(hidden_size, hidden_size, 1, bias=True),
137
+ nn.Sigmoid(),
138
+ )
139
+
140
+ ffn = int(mlp_ratio * hidden_size)
141
+ self.conv4 = nn.Conv2d(hidden_size, ffn, 1, bias=True)
142
+ self.conv5 = nn.Conv2d(ffn, hidden_size, 1, bias=True)
143
+
144
+ self.norm1 = LayerNorm2d(hidden_size, affine=False)
145
+ self.norm2 = LayerNorm2d(hidden_size, affine=False)
146
+
147
+ self.adaLN_modulation = nn.Sequential(
148
+ nn.SiLU(),
149
+ nn.Linear(hidden_size, 6 * hidden_size, bias=True),
150
+ )
151
+
152
+ def forward(self, inp, c):
153
+ shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = self.adaLN_modulation(c).chunk(6, dim=1)
154
+ x = modulate(self.norm1(inp), shift_msa, scale_msa)
155
+ x = F.gelu(self.conv2(self.conv1(x)))
156
+ x = x * self.ca(x)
157
+ x = self.conv3(x)
158
+ x = inp + gate_msa[..., None, None] * x
159
+ x = x + gate_mlp[..., None, None] * self.conv5(
160
+ F.gelu(self.conv4(modulate(self.norm2(x), shift_mlp, scale_mlp)))
161
+ )
162
+ return x
163
+
164
+
165
+ class _EncoderDiCoBlock(nn.Module):
166
+ """DiCoBlock without adaLN, for the encoder pathway."""
167
+
168
+ def __init__(self, hidden_size, mlp_ratio=4.0):
169
+ super().__init__()
170
+ self.conv1 = nn.Conv2d(hidden_size, hidden_size, 1, bias=True)
171
+ self.conv2 = nn.Conv2d(hidden_size, hidden_size, 3, padding=1, groups=hidden_size, bias=True)
172
+ self.conv3 = nn.Conv2d(hidden_size, hidden_size, 1, bias=True)
173
+ self.ca = nn.Sequential(
174
+ nn.AdaptiveAvgPool2d(1),
175
+ nn.Conv2d(hidden_size, hidden_size, 1, bias=True),
176
+ nn.Sigmoid(),
177
+ )
178
+ ffn = int(mlp_ratio * hidden_size)
179
+ self.conv4 = nn.Conv2d(hidden_size, ffn, 1, bias=True)
180
+ self.conv5 = nn.Conv2d(ffn, hidden_size, 1, bias=True)
181
+ self.norm1 = _EncoderLayerNorm2d(hidden_size)
182
+ self.norm2 = _EncoderLayerNorm2d(hidden_size)
183
+
184
+ def forward(self, inp):
185
+ x = self.norm1(inp)
186
+ x = F.gelu(self.conv2(self.conv1(x)))
187
+ x = x * self.ca(x)
188
+ x = self.conv3(x)
189
+ x = inp + x
190
+ return x + self.conv5(F.gelu(self.conv4(self.norm2(x))))
191
+
192
+
193
+ class NerfEmbedder(nn.Module):
194
+ """Patch-position embedder used by the DConv decoder x-pathway."""
195
+
196
+ def __init__(self, in_channels, hidden_size_input, max_freqs=8):
197
+ super().__init__()
198
+ self.max_freqs = max_freqs
199
+ self.embedder = nn.Sequential(
200
+ nn.Linear(in_channels + max_freqs ** 2, hidden_size_input, bias=True),
201
+ )
202
+
203
+ @lru_cache
204
+ def fetch_pos(self, patch_size, device, dtype):
205
+ pos = torch.linspace(0, 1, patch_size, device=device, dtype=dtype)
206
+ pos_y, pos_x = torch.meshgrid(pos, pos, indexing="ij")
207
+ pos_x = pos_x.reshape(-1, 1, 1)
208
+ pos_y = pos_y.reshape(-1, 1, 1)
209
+ freqs = torch.linspace(0, self.max_freqs, self.max_freqs, dtype=dtype, device=device)
210
+ fx = freqs[None, :, None]
211
+ fy = freqs[None, None, :]
212
+ coeffs = (1 + fx * fy) ** -1
213
+ dct_x = torch.cos(pos_x * fx * torch.pi)
214
+ dct_y = torch.cos(pos_y * fy * torch.pi)
215
+ return (dct_x * dct_y * coeffs).view(1, -1, self.max_freqs ** 2)
216
+
217
+ def forward(self, x):
218
+ B, P2, _ = x.shape
219
+ ps = int(P2 ** 0.5)
220
+ dct = self.fetch_pos(ps, x.device, x.dtype).expand(B, -1, -1)
221
+ return self.embedder(torch.cat([x, dct], dim=-1))
222
+
223
+
224
+ class NerfFinalLayer(nn.Module):
225
+ def __init__(self, hidden_size, out_channels):
226
+ super().__init__()
227
+ self.norm = RMSNorm(hidden_size)
228
+ self.linear = nn.Linear(hidden_size, out_channels, bias=True)
229
+
230
+ def forward(self, x):
231
+ return self.linear(self.norm(x))
232
+
233
+
234
+ class SimpleMLPAdaLN(nn.Module):
235
+ """Final small MLP that maps NerfEmbedder features to per-patch RGB."""
236
+
237
+ def __init__(self, in_channels, model_channels, out_channels, z_channels, num_res_blocks, patch_size):
238
+ super().__init__()
239
+ self.in_channels = in_channels
240
+ self.model_channels = model_channels
241
+ self.out_channels = out_channels
242
+ self.num_res_blocks = num_res_blocks
243
+ self.patch_size = patch_size
244
+
245
+ self.cond_embed = nn.Linear(z_channels, patch_size ** 2 * model_channels)
246
+ self.input_proj = nn.Linear(in_channels, model_channels)
247
+
248
+ self.res_blocks = nn.ModuleList(_MLPResBlock(model_channels) for _ in range(num_res_blocks))
249
+
250
+ def forward(self, x, c):
251
+ x = self.input_proj(x)
252
+ c = self.cond_embed(c).reshape(c.shape[0], self.patch_size ** 2, -1)
253
+ for block in self.res_blocks:
254
+ x = block(x, c)
255
+ return x
256
+
257
+
258
+ class _MLPResBlock(nn.Module):
259
+ def __init__(self, channels):
260
+ super().__init__()
261
+ self.in_ln = nn.LayerNorm(channels, eps=1e-6)
262
+ self.mlp = nn.Sequential(
263
+ nn.Linear(channels, channels, bias=True),
264
+ nn.SiLU(),
265
+ nn.Linear(channels, channels, bias=True),
266
+ )
267
+ self.adaLN_modulation = nn.Sequential(
268
+ nn.SiLU(),
269
+ nn.Linear(channels, 3 * channels, bias=True),
270
+ )
271
+
272
+ def forward(self, x, y):
273
+ shift, scale, gate = self.adaLN_modulation(y).chunk(3, dim=-1)
274
+ h = self.in_ln(x) * (1 + scale) + shift
275
+ return x + gate * self.mlp(h)
276
+
277
+
278
+ class ResnetBlock(nn.Module):
279
+ """GroupNorm + Conv ResBlock used by the CoD Decoder."""
280
+
281
+ def __init__(self, *, in_channels, out_channels=None, dropout=0.0):
282
+ super().__init__()
283
+ out_channels = out_channels or in_channels
284
+ self.in_channels = in_channels
285
+ self.out_channels = out_channels
286
+
287
+ self.norm1 = Normalize(in_channels)
288
+ self.conv1 = nn.Conv2d(in_channels, out_channels, 3, padding=1)
289
+ self.norm2 = Normalize(out_channels)
290
+ self.dropout = nn.Dropout(dropout)
291
+ self.conv2 = nn.Conv2d(out_channels, out_channels, 3, padding=1)
292
+ if in_channels != out_channels:
293
+ self.nin_shortcut = nn.Conv2d(in_channels, out_channels, 1)
294
+
295
+ def forward(self, x):
296
+ h = self.conv1(nonlinearity(self.norm1(x)))
297
+ h = self.conv2(self.dropout(nonlinearity(self.norm2(h))))
298
+ if self.in_channels != self.out_channels:
299
+ x = self.nin_shortcut(x)
300
+ return x + h
301
+
302
+
303
+ class AttnBlock(nn.Module):
304
+ """Patched self-attention used at inference (eval mode of the original)."""
305
+
306
+ def __init__(self, in_channels, patch_size=32):
307
+ super().__init__()
308
+ self.in_channels = in_channels
309
+ self.patch_size = patch_size
310
+ self.norm = Normalize(in_channels)
311
+ self.q = nn.Conv2d(in_channels, in_channels, 1)
312
+ self.k = nn.Conv2d(in_channels, in_channels, 1)
313
+ self.v = nn.Conv2d(in_channels, in_channels, 1)
314
+ self.proj_out = nn.Conv2d(in_channels, in_channels, 1)
315
+
316
+ def forward(self, x):
317
+ h_ = self.norm(x)
318
+ Q = self.q(h_)
319
+ K = self.k(h_)
320
+ V = self.v(h_)
321
+
322
+ d = self.patch_size
323
+ b, c, H, W = Q.shape
324
+ pad_h = (d - H % d) % d
325
+ pad_w = (d - W % d) % d
326
+ if pad_h or pad_w:
327
+ Q = F.pad(Q, (0, pad_w, 0, pad_h), mode="replicate")
328
+ K = F.pad(K, (0, pad_w, 0, pad_h), mode="replicate")
329
+ V = F.pad(V, (0, pad_w, 0, pad_h), mode="replicate")
330
+ _, _, H_pad, W_pad = Q.shape
331
+ nph, npw = H_pad // d, W_pad // d
332
+ np_ = nph * npw
333
+
334
+ def to_patches(t):
335
+ return (t.reshape(b, c, nph, d, npw, d)
336
+ .permute(0, 2, 4, 1, 3, 5)
337
+ .reshape(b * np_, c, d * d))
338
+
339
+ Q = to_patches(Q)
340
+ K = to_patches(K)
341
+ V = to_patches(V)
342
+
343
+ w_ = torch.bmm(Q.permute(0, 2, 1), K) * (c ** -0.5)
344
+ w_ = F.softmax(w_, dim=2).permute(0, 2, 1)
345
+ h_ = torch.bmm(V, w_).reshape(b, nph, npw, c, d, d).permute(0, 3, 1, 4, 2, 5).reshape(b, c, H_pad, W_pad)
346
+ if pad_h or pad_w:
347
+ h_ = h_[:, :, :H, :W]
348
+ return x + self.proj_out(h_)
349
+
350
+
351
+ # ---------------------------------------------------------------------------
352
+ # adaLN constant-folding: at fixed t=0, adaLN_modulation(c) is constant.
353
+ # Replace the MLP with a buffer so DiCoBlock.forward stays unchanged and
354
+ # torch.compile can fuse the surrounding ops normally.
355
+ # ---------------------------------------------------------------------------
356
+ class _ConstAdaLN(nn.Module):
357
+ def __init__(self, modulation: torch.Tensor):
358
+ super().__init__()
359
+ self.register_buffer("modulation", modulation.detach().clone())
360
+
361
+ def forward(self, c):
362
+ b = c.shape[0]
363
+ if self.modulation.shape[0] != b:
364
+ return self.modulation.expand(b, *self.modulation.shape[1:])
365
+ return self.modulation
366
+
367
+
368
+ def _replace_adaln_with_const(module: nn.Module, c: torch.Tensor) -> int:
369
+ # Only DiCoBlock is targeted: its adaLN is conditioned solely on t.
370
+ # Other adaLN_modulation submodules (e.g. _MLPResBlock in the decoder MLP)
371
+ # take a per-position latent and must not be folded.
372
+ n = 0
373
+ for child in module.modules():
374
+ if not isinstance(child, DiCoBlock):
375
+ continue
376
+ adaln = child.adaLN_modulation
377
+ if isinstance(adaln, _ConstAdaLN):
378
+ continue
379
+ with torch.no_grad():
380
+ mod = adaln(c)
381
+ child.adaLN_modulation = _ConstAdaLN(mod)
382
+ n += 1
383
+ return n
384
+
385
+
386
+ # ---------------------------------------------------------------------------
387
+ # CoD Decoder: latent → conditioning features for the denoiser
388
+ # ---------------------------------------------------------------------------
389
+ class _Decoder(nn.Module):
390
+ """ds=16, up2x=True, light=True only."""
391
+
392
+ def __init__(self, out_ch=384, z_ch=128):
393
+ super().__init__()
394
+ self.conv_in = nn.Conv2d(z_ch, out_ch, kernel_size=3, stride=1, padding=1)
395
+ self.block = nn.Sequential(
396
+ ResnetBlock(in_channels=out_ch, out_channels=out_ch),
397
+ AttnBlock(out_ch, patch_size=32),
398
+ ResnetBlock(in_channels=out_ch, out_channels=out_ch),
399
+ AttnBlock(out_ch, patch_size=32),
400
+ ResnetBlock(in_channels=out_ch, out_channels=out_ch),
401
+ )
402
+ self.norm_out = Normalize(out_ch)
403
+ self.conv_out = nn.Conv2d(out_ch, out_ch, kernel_size=3, stride=1, padding=1)
404
+ self.ada = nn.Identity()
405
+
406
+ def forward(self, z):
407
+ h = self.block(self.conv_in(z))
408
+ h = self.conv_out(nonlinearity(self.norm_out(h)))
409
+ return self.ada(h)
410
+
411
+
412
+ # ---------------------------------------------------------------------------
413
+ # DConvEncoder: image → packed (mean, logvar) latent
414
+ # ---------------------------------------------------------------------------
415
+ class _DConvEncoder(nn.Module):
416
+ def __init__(
417
+ self,
418
+ z_ch=128,
419
+ hidden_size=384,
420
+ num_blocks=21,
421
+ patch_size=16,
422
+ mlp_ratio=4.0,
423
+ head_size=768,
424
+ num_head_blocks=2,
425
+ out_ch_mult=2,
426
+ ):
427
+ super().__init__()
428
+ self.z_ch = z_ch
429
+ self.patch_size = patch_size
430
+ self.patch_cond_embed = nn.Conv2d(3, head_size, kernel_size=patch_size, stride=patch_size, bias=True)
431
+ self.head_blocks = nn.ModuleList([
432
+ _EncoderDiCoBlock(head_size, mlp_ratio=mlp_ratio) for _ in range(num_head_blocks)
433
+ ])
434
+ self.proj_down = nn.Conv2d(head_size, hidden_size, kernel_size=1, bias=True)
435
+ self.z_proj = nn.Conv2d(z_ch, hidden_size, kernel_size=1, bias=True)
436
+ self.fuse_proj = nn.Conv2d(hidden_size * 2, hidden_size, kernel_size=1, bias=True)
437
+ self.t_embedder = TimestepEmbedder(hidden_size)
438
+ self.blocks = nn.ModuleList([
439
+ DiCoBlock(hidden_size, mlp_ratio=mlp_ratio) for _ in range(num_blocks)
440
+ ])
441
+ self.norm_out = LayerNorm2d(hidden_size)
442
+ self.proj_out = nn.Conv2d(hidden_size, z_ch * out_ch_mult, kernel_size=1, bias=True)
443
+
444
+ def forward_pred(self, z_t, t, y):
445
+ cond = self.patch_cond_embed(y)
446
+ for block in self.head_blocks:
447
+ cond = block(cond)
448
+ cond = self.proj_down(cond)
449
+
450
+ s = self.fuse_proj(torch.cat([cond, self.z_proj(z_t)], dim=1))
451
+ c = self.t_embedder(t.view(-1))
452
+ for block in self.blocks:
453
+ s = block(s, c)
454
+ return self.proj_out(self.norm_out(s))
455
+
456
+
457
+ # ---------------------------------------------------------------------------
458
+ # DConv denoiser: latent (via cond) + zero noise → reconstructed image
459
+ # ---------------------------------------------------------------------------
460
+ class _YEmbedder(nn.Module):
461
+ """Holds only the CoD decoder; the original Flux2 VAE encoder side is omitted."""
462
+
463
+ def __init__(self, ch=384, z_ch=128):
464
+ super().__init__()
465
+ self.decoder = _Decoder(out_ch=ch, z_ch=z_ch)
466
+
467
+
468
+ class _DConvDenoiser(nn.Module):
469
+ def __init__(
470
+ self,
471
+ patch_size=16,
472
+ in_channels=3,
473
+ hidden_size=384,
474
+ hidden_size_x=32,
475
+ mlp_ratio=4.0,
476
+ num_blocks=24,
477
+ num_cond_blocks=21,
478
+ bottleneck_dim=128,
479
+ ):
480
+ super().__init__()
481
+ self.in_channels = in_channels
482
+ self.patch_size = patch_size
483
+ self.hidden_size = hidden_size
484
+ self.num_cond_blocks = num_cond_blocks
485
+
486
+ self.t_embedder = TimestepEmbedder(hidden_size)
487
+ self.y_embedder_x = nn.Conv2d(hidden_size, hidden_size_x * patch_size ** 2, 1, 1, 0)
488
+ self.x_embedder = NerfEmbedder(in_channels + hidden_size_x, hidden_size_x, max_freqs=8)
489
+ self.s_embedder = BottleneckPatchEmbed(patch_size, in_channels, bottleneck_dim, hidden_size, bias=True)
490
+ self.blocks = nn.ModuleList([
491
+ DiCoBlock(hidden_size, mlp_ratio=mlp_ratio) for _ in range(num_cond_blocks)
492
+ ])
493
+ self.dec_net = SimpleMLPAdaLN(
494
+ in_channels=hidden_size_x,
495
+ model_channels=hidden_size_x,
496
+ out_channels=in_channels,
497
+ z_channels=hidden_size,
498
+ num_res_blocks=num_blocks - num_cond_blocks,
499
+ patch_size=patch_size,
500
+ )
501
+ self.final_layer = NerfFinalLayer(hidden_size_x, in_channels)
502
+ self.y_embedder = _YEmbedder(ch=hidden_size, z_ch=bottleneck_dim)
503
+
504
+ def forward(self, x, t, cond, chunk_size=None):
505
+ b, _, h, w = x.shape
506
+ c = self.t_embedder(t.view(-1))
507
+
508
+ s = self.s_embedder(x, cond)
509
+ for block in self.blocks:
510
+ s = block(s, c)
511
+
512
+ length = s.shape[-2] * s.shape[-1]
513
+ s = s.permute(0, 2, 3, 1).reshape(b, length, self.hidden_size)
514
+
515
+ p2 = self.patch_size ** 2
516
+ x = torch.nn.functional.unfold(x, kernel_size=self.patch_size, stride=self.patch_size)
517
+
518
+ if chunk_size is None or chunk_size >= length:
519
+ x = torch.cat([x, self.y_embedder_x(cond).flatten(2)], dim=1)
520
+ x = x.reshape(b, -1, p2, length).permute(0, 3, 2, 1).flatten(0, 1)
521
+ x = self.x_embedder(x)
522
+ x = self.dec_net(x, s.reshape(-1, self.hidden_size))
523
+ x = self.final_layer(x)
524
+ x = x.transpose(1, 2).reshape(b, length, -1)
525
+ return torch.nn.functional.fold(
526
+ x.transpose(1, 2).contiguous(), (h, w),
527
+ kernel_size=self.patch_size, stride=self.patch_size,
528
+ )
529
+
530
+ # Chunked per-patch tail: each 16x16 output patch is independent here,
531
+ # so peak memory is capped at ~chunk_size patches with identical output.
532
+ cond_flat = cond.flatten(2)
533
+ out_cols = x.new_empty(b, self.in_channels * p2, length)
534
+ for i0 in range(0, length, chunk_size):
535
+ i1 = min(i0 + chunk_size, length)
536
+ n = i1 - i0
537
+ yx = self.y_embedder_x(cond_flat[:, :, i0:i1].unsqueeze(-1)).squeeze(-1)
538
+ xc = torch.cat([x[:, :, i0:i1], yx], dim=1)
539
+ xc = xc.reshape(b, -1, p2, n).permute(0, 3, 2, 1).flatten(0, 1)
540
+ xc = self.x_embedder(xc)
541
+ xc = self.dec_net(xc, s[:, i0:i1].reshape(-1, self.hidden_size))
542
+ xc = self.final_layer(xc)
543
+ out_cols[:, :, i0:i1] = xc.transpose(1, 2).reshape(b, n, -1).permute(0, 2, 1)
544
+ return torch.nn.functional.fold(
545
+ out_cols, (h, w), kernel_size=self.patch_size, stride=self.patch_size,
546
+ )
547
+
548
+
549
+ # ---------------------------------------------------------------------------
550
+ # Wrapper
551
+ # ---------------------------------------------------------------------------
552
+
553
+
554
+ class AutoencoderKLMega(ModelMixin, ConfigMixin):
555
+ """
556
+ Encode: DConvEncoder (one-step diffusion, t=0) → latent_dist over (B, 32, H/8, W/8)
557
+ Decode: DConvDenoiser + CoD Decoder (t=0) → image (B, 3, H, W) in [-1, 1]
558
+
559
+ With `flux_bn_mean`/`flux_bn_std` in the config, latents are emitted in and
560
+ accepted from the raw Flux.2 VAE latent space; without them, the normalized
561
+ anchor space. `latent_dist` is a DiagonalGaussianDistribution in the public
562
+ latent space (mean and logvar are transformed consistently), so both
563
+ `.mode()` and `.sample()` behave like the Flux.2 VAE's.
564
+ """
565
+
566
+ @register_to_config
567
+ def __init__(
568
+ self,
569
+ latent_channels: int = 32,
570
+ downsample_factor: int = 8,
571
+ code_channels: int = 128,
572
+ code_downsample_factor: int = 16,
573
+ flux_bn_mean: Optional[List[float]] = None,
574
+ flux_bn_std: Optional[List[float]] = None,
575
+ decode_chunk_size: Optional[int] = 4096,
576
+ folded: bool = False,
577
+ ):
578
+ super().__init__()
579
+ self.encoder = _DConvEncoder()
580
+ self.decoder = _DConvDenoiser()
581
+
582
+ if folded:
583
+ # Checkpoint carries precomputed t=0 modulation buffers instead of
584
+ # the adaLN MLPs — install placeholder buffers so keys line up.
585
+ for mod in (self.encoder, self.decoder):
586
+ for child in mod.modules():
587
+ if isinstance(child, DiCoBlock):
588
+ out = child.adaLN_modulation[1].out_features
589
+ child.adaLN_modulation = _ConstAdaLN(torch.zeros(1, out))
590
+
591
+ if flux_bn_mean is not None and flux_bn_std is not None:
592
+ if len(flux_bn_mean) != code_channels or len(flux_bn_std) != code_channels:
593
+ raise ValueError(
594
+ f"flux_bn stats must have {code_channels} entries, "
595
+ f"got {len(flux_bn_mean)}/{len(flux_bn_std)}"
596
+ )
597
+ mean = torch.tensor(flux_bn_mean, dtype=torch.float32).view(1, -1, 1, 1)
598
+ std = torch.tensor(flux_bn_std, dtype=torch.float32).view(1, -1, 1, 1)
599
+ self.register_buffer("bn_mean", mean, persistent=False)
600
+ self.register_buffer("bn_std", std, persistent=False)
601
+ self.register_buffer("bn_2logstd", 2.0 * std.log(), persistent=False)
602
+ else:
603
+ self.register_buffer("bn_mean", None, persistent=False)
604
+ self.register_buffer("bn_std", None, persistent=False)
605
+ self.register_buffer("bn_2logstd", None, persistent=False)
606
+
607
+ # -- Flux2 2x2 latent (un)packing, diffusers channel order ---------------
608
+ @staticmethod
609
+ def _patchify_latents(latents: torch.Tensor) -> torch.Tensor:
610
+ b, c, h, w = latents.shape
611
+ latents = latents.view(b, c, h // 2, 2, w // 2, 2)
612
+ latents = latents.permute(0, 1, 3, 5, 2, 4)
613
+ return latents.reshape(b, c * 4, h // 2, w // 2)
614
+
615
+ @staticmethod
616
+ def _unpatchify_latents(latents: torch.Tensor) -> torch.Tensor:
617
+ b, c, h, w = latents.shape
618
+ latents = latents.reshape(b, c // 4, 2, 2, h, w)
619
+ latents = latents.permute(0, 1, 4, 2, 5, 3)
620
+ return latents.reshape(b, c // 4, h * 2, w * 2)
621
+
622
+ def encode(self, x: torch.Tensor, return_dict: bool = True):
623
+ ds = self.config.code_downsample_factor
624
+ B, _, H, W = x.shape
625
+ if H % ds or W % ds:
626
+ raise ValueError(f"H, W must be multiples of {ds}, got ({H}, {W})")
627
+ z_t = torch.zeros(B, self.config.code_channels, H // ds, W // ds, device=x.device, dtype=x.dtype)
628
+ t = torch.zeros(B, device=x.device, dtype=x.dtype)
629
+ out = self.encoder.forward_pred(z_t, t, x)
630
+ mean = out[:, : self.config.code_channels]
631
+ logvar = out[:, self.config.code_channels :].clamp(min=-20.0, max=10.0)
632
+ if self.bn_mean is not None:
633
+ mean = mean * self.bn_std.to(mean.dtype) + self.bn_mean.to(mean.dtype)
634
+ logvar = logvar + self.bn_2logstd.to(logvar.dtype)
635
+ moments = torch.cat(
636
+ [self._unpatchify_latents(mean), self._unpatchify_latents(logvar)], dim=1
637
+ )
638
+ posterior = DiagonalGaussianDistribution(moments)
639
+ if not return_dict:
640
+ return (posterior,)
641
+ return AutoencoderKLOutput(latent_dist=posterior)
642
+
643
+ def decode(self, z: torch.Tensor, return_dict: bool = True):
644
+ if z.shape[1] != self.config.latent_channels or z.shape[2] % 2 or z.shape[3] % 2:
645
+ raise ValueError(
646
+ f"expected Flux.2-shaped latent (B, {self.config.latent_channels}, H/8, W/8) "
647
+ f"with even spatial dims, got {tuple(z.shape)}"
648
+ )
649
+ z = self._patchify_latents(z)
650
+ if self.bn_mean is not None:
651
+ z = (z - self.bn_mean.to(z.dtype)) / self.bn_std.to(z.dtype)
652
+ cond = self.decoder.y_embedder.decoder(z)
653
+ B = z.shape[0]
654
+ H = z.shape[2] * self.config.code_downsample_factor
655
+ W = z.shape[3] * self.config.code_downsample_factor
656
+ noise = torch.zeros(B, 3, H, W, device=z.device, dtype=z.dtype)
657
+ t = torch.zeros(B, device=z.device, dtype=z.dtype)
658
+ sample = self.decoder.forward(noise, t, cond, chunk_size=self.config.decode_chunk_size)
659
+ if not return_dict:
660
+ return (sample,)
661
+ return DecoderOutput(sample=sample)
662
+
663
+ def forward(self, sample: torch.Tensor, return_dict: bool = True):
664
+ z = self.encode(sample, return_dict=False)[0].mode()
665
+ return self.decode(z, return_dict=return_dict)
666
+
667
+ @torch.no_grad()
668
+ def fold_adaln(self) -> int:
669
+ """Constant-fold the DiCoBlock adaLN MLPs at t=0 (we only run t=0).
670
+
671
+ Same optimization as mega_vae.MageVAE, folded in fp32 for numerical
672
+ parity with it (MageVAE folds before its bf16 cast). Mutates the module
673
+ structure (MLPs become buffers). No-op on checkpoints converted with
674
+ fold=True (already folded at conversion time).
675
+ """
676
+ if self.config.folded:
677
+ return 0
678
+ p = next(self.parameters())
679
+ n = 0
680
+ for mod in (self.encoder, self.decoder):
681
+ t = torch.zeros(1, device=p.device, dtype=torch.float32)
682
+ c = mod.t_embedder.float()(t)
683
+ for child in mod.modules():
684
+ if isinstance(child, DiCoBlock) and not isinstance(child.adaLN_modulation, _ConstAdaLN):
685
+ child.adaLN_modulation = _ConstAdaLN(
686
+ child.adaLN_modulation.float()(c).to(p.dtype)
687
+ )
688
+ n += 1
689
+ mod.t_embedder.to(p.dtype)
690
+ return n
691
+
692
+ @classmethod
693
+ def from_pretrained(cls, *args, fold_adaln: bool = True, **kwargs):
694
+ model = super().from_pretrained(*args, **kwargs)
695
+ if fold_adaln:
696
+ model.fold_adaln()
697
+ return model
698
+
699
+
700
+ def convert_mega_ckpt(
701
+ src_ckpt: str = "MegaFlow/vae/diffusion_pytorch_model.safetensors",
702
+ src_config: str = "MegaFlow/vae/config.json",
703
+ dst_dir: str = "MegaFlow/vae_diffusers",
704
+ dtype: torch.dtype = torch.bfloat16,
705
+ fold: bool = True,
706
+ ) -> AutoencoderKLMega:
707
+ """One-time conversion from the CoD checkpoint layout to diffusers layout.
708
+
709
+ 'student.dconv_encoder.*' -> 'encoder.*', 'pipeline.*' -> 'decoder.*'
710
+ (dropping the unused original-VAE 'y_embedder.encoder/bottleneck' branch);
711
+ BN stats are carried over from the source config.json. With fold=True the
712
+ t=0 adaLN MLPs are constant-folded in fp32 before the dtype cast and the
713
+ checkpoint stores the modulation buffers instead (~74 MB smaller,
714
+ ready-to-run on load with no fold step).
715
+ """
716
+ import json
717
+
718
+ from safetensors.torch import load_file
719
+
720
+ sd = load_file(src_ckpt, device="cpu")
721
+ new_sd = {}
722
+ for k, v in sd.items():
723
+ if k.startswith("student.dconv_encoder."):
724
+ new_sd["encoder." + k[len("student.dconv_encoder.") :]] = v
725
+ elif k.startswith("pipeline."):
726
+ nk = k[len("pipeline.") :]
727
+ if nk.startswith("y_embedder.encoder.") or nk.startswith("y_embedder.bottleneck."):
728
+ continue
729
+ new_sd["decoder." + nk] = v
730
+
731
+ with open(src_config) as f:
732
+ cfg = json.load(f)
733
+ model = AutoencoderKLMega(
734
+ flux_bn_mean=cfg.get("flux_bn_mean"), flux_bn_std=cfg.get("flux_bn_std")
735
+ )
736
+ missing, unexpected = model.load_state_dict(new_sd, strict=False)
737
+ logger.info(
738
+ f"convert_mega_ckpt: {len(new_sd)} keys mapped, missing={len(missing)}, "
739
+ f"unexpected={len(unexpected)}"
740
+ )
741
+ if missing:
742
+ raise RuntimeError(f"convert_mega_ckpt: missing model keys: {missing[:10]}")
743
+ if fold:
744
+ n = model.fold_adaln()
745
+ model.register_to_config(folded=True)
746
+ logger.info(f"convert_mega_ckpt: constant-folded {n} adaLN blocks at t=0")
747
+ model.to(dtype).save_pretrained(dst_dir)
748
+ logger.info(f"convert_mega_ckpt: saved to {dst_dir} ({dtype})")
749
+ return model
750
+
751
+
752
+ if __name__ == "__main__":
753
+ convert_mega_ckpt()
config.json ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_class_name": "AutoencoderKLMega",
3
+ "_diffusers_version": "0.37.1",
4
+ "code_channels": 128,
5
+ "code_downsample_factor": 16,
6
+ "decode_chunk_size": 4096,
7
+ "downsample_factor": 8,
8
+ "flux_bn_mean": [
9
+ -0.06738281,
10
+ -0.07128906,
11
+ -0.07519531,
12
+ -0.07470703,
13
+ 0.02233887,
14
+ 0.01794434,
15
+ 0.01422119,
16
+ 0.01831055,
17
+ -6.294e-05,
18
+ -0.0062561,
19
+ -0.00020981,
20
+ -0.00314331,
21
+ -0.02722168,
22
+ -0.02807617,
23
+ -0.02758789,
24
+ -0.02905273,
25
+ -0.07666016,
26
+ -0.06738281,
27
+ -0.09033203,
28
+ -0.08935547,
29
+ 0.0168457,
30
+ 0.01519775,
31
+ 0.00787354,
32
+ 0.00860596,
33
+ 0.00836182,
34
+ 0.00154114,
35
+ 0.00025749,
36
+ -0.00427246,
37
+ -0.04394531,
38
+ -0.04199219,
39
+ -0.04370117,
40
+ -0.04321289,
41
+ -0.01025391,
42
+ -0.01318359,
43
+ -0.00662231,
44
+ -0.00476074,
45
+ -0.03100586,
46
+ -0.03051758,
47
+ -0.0279541,
48
+ -0.01794434,
49
+ 0.00302124,
50
+ 0.00150299,
51
+ 0.01257324,
52
+ 0.01446533,
53
+ 0.03466797,
54
+ 0.03369141,
55
+ 0.03369141,
56
+ 0.02832031,
57
+ 0.00198364,
58
+ 0.00473022,
59
+ 0.00466919,
60
+ 0.00497437,
61
+ 0.01226807,
62
+ 0.00811768,
63
+ 0.00805664,
64
+ 0.0145874,
65
+ 0.06787109,
66
+ 0.06787109,
67
+ 0.07666016,
68
+ 0.07324219,
69
+ -0.04614258,
70
+ -0.04736328,
71
+ -0.03930664,
72
+ -0.05102539,
73
+ -0.05273438,
74
+ -0.04785156,
75
+ -0.04711914,
76
+ -0.05175781,
77
+ -0.03173828,
78
+ -0.03173828,
79
+ -0.03442383,
80
+ -0.02819824,
81
+ 0.05102539,
82
+ 0.04443359,
83
+ 0.05786133,
84
+ 0.04589844,
85
+ -0.04125977,
86
+ -0.04589844,
87
+ -0.04882812,
88
+ -0.04663086,
89
+ -0.0088501,
90
+ -0.01062012,
91
+ -0.00878906,
92
+ -0.00460815,
93
+ -0.03759766,
94
+ -0.04321289,
95
+ -0.04345703,
96
+ -0.04980469,
97
+ 0.01184082,
98
+ 0.01660156,
99
+ 0.02026367,
100
+ 0.0279541,
101
+ 0.0112915,
102
+ 0.01287842,
103
+ 0.0015564,
104
+ 0.00714111,
105
+ -0.01177979,
106
+ -0.00183868,
107
+ -0.01416016,
108
+ -0.00537109,
109
+ -0.00909424,
110
+ -0.01379395,
111
+ -0.01446533,
112
+ -0.01867676,
113
+ 0.03222656,
114
+ 0.03051758,
115
+ 0.02587891,
116
+ 0.02990723,
117
+ 0.05395508,
118
+ 0.06152344,
119
+ 0.04956055,
120
+ 0.05908203,
121
+ -0.05102539,
122
+ -0.06030273,
123
+ -0.04785156,
124
+ -0.05249023,
125
+ -0.02270508,
126
+ -0.02746582,
127
+ -0.01538086,
128
+ -0.0255127,
129
+ -0.05712891,
130
+ -0.05639648,
131
+ -0.05175781,
132
+ -0.04956055,
133
+ 0.01159668,
134
+ 0.00543213,
135
+ 0.01635742,
136
+ 0.01037598
137
+ ],
138
+ "flux_bn_std": [
139
+ 1.8028034,
140
+ 1.77661192,
141
+ 1.78538513,
142
+ 1.78538513,
143
+ 1.77220905,
144
+ 1.75893426,
145
+ 1.75893426,
146
+ 1.75002849,
147
+ 1.73207963,
148
+ 1.73658431,
149
+ 1.73207963,
150
+ 1.73207963,
151
+ 1.86248481,
152
+ 1.8540765,
153
+ 1.86248481,
154
+ 1.85828543,
155
+ 1.75893426,
156
+ 1.75448704,
157
+ 1.75448704,
158
+ 1.75893426,
159
+ 1.73658431,
160
+ 1.74107718,
161
+ 1.73658431,
162
+ 1.74107718,
163
+ 1.73207963,
164
+ 1.7230351,
165
+ 1.74107718,
166
+ 1.73207963,
167
+ 1.75448704,
168
+ 1.75002849,
169
+ 1.75448704,
170
+ 1.75002849,
171
+ 1.84562993,
172
+ 1.84562993,
173
+ 1.8540765,
174
+ 1.8540765,
175
+ 1.82434237,
176
+ 1.78100395,
177
+ 1.78538513,
178
+ 1.7941153,
179
+ 1.8028034,
180
+ 1.78975558,
181
+ 1.76779521,
182
+ 1.76779521,
183
+ 1.77661192,
184
+ 1.76779521,
185
+ 1.7941153,
186
+ 1.78538513,
187
+ 1.75893426,
188
+ 1.74107718,
189
+ 1.75002849,
190
+ 1.73658431,
191
+ 1.7941153,
192
+ 1.79846454,
193
+ 1.7941153,
194
+ 1.7941153,
195
+ 1.77661192,
196
+ 1.77661192,
197
+ 1.77220905,
198
+ 1.77661192,
199
+ 1.75448704,
200
+ 1.74555862,
201
+ 1.76779521,
202
+ 1.75002849,
203
+ 1.77661192,
204
+ 1.77220905,
205
+ 1.78100395,
206
+ 1.78100395,
207
+ 1.81575739,
208
+ 1.8028034,
209
+ 1.8028034,
210
+ 1.8028034,
211
+ 1.75448704,
212
+ 1.73207963,
213
+ 1.75448704,
214
+ 1.75002849,
215
+ 1.75893426,
216
+ 1.75002849,
217
+ 1.76779521,
218
+ 1.76337028,
219
+ 1.76779521,
220
+ 1.75448704,
221
+ 1.78100395,
222
+ 1.77220905,
223
+ 1.78975558,
224
+ 1.79846454,
225
+ 1.78538513,
226
+ 1.77661192,
227
+ 1.76337028,
228
+ 1.76337028,
229
+ 1.75448704,
230
+ 1.75893426,
231
+ 1.77661192,
232
+ 1.76779521,
233
+ 1.78100395,
234
+ 1.78100395,
235
+ 1.71849489,
236
+ 1.70480204,
237
+ 1.72756326,
238
+ 1.70937848,
239
+ 1.78100395,
240
+ 1.75893426,
241
+ 1.75002849,
242
+ 1.74555862,
243
+ 1.75893426,
244
+ 1.75002849,
245
+ 1.75448704,
246
+ 1.73658431,
247
+ 1.76337028,
248
+ 1.75893426,
249
+ 1.74555862,
250
+ 1.74107718,
251
+ 1.75002849,
252
+ 1.75893426,
253
+ 1.76337028,
254
+ 1.76337028,
255
+ 1.72756326,
256
+ 1.71394277,
257
+ 1.73207963,
258
+ 1.7230351,
259
+ 1.76779521,
260
+ 1.76337028,
261
+ 1.77220905,
262
+ 1.76779521,
263
+ 1.74555862,
264
+ 1.74555862,
265
+ 1.75893426,
266
+ 1.75448704
267
+ ],
268
+ "folded": true,
269
+ "latent_channels": 32
270
+ }
diffusion_pytorch_model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6ed49bcd8503d27ea460cdeb3a320bc2b06a3203a7fb5bd9147110d22dba248b
3
+ size 201858086