import math import copy import os import json from dataclasses import dataclass, field, asdict, fields from pathlib import Path from typing import Any, Dict, List, Optional, Tuple, Union import torch import torch.nn.init as init from PIL import Image from einops import rearrange from torch import Tensor, nn from diffusers.configuration_utils import ConfigMixin, register_to_config from diffusers.image_processor import VaeImageProcessor from diffusers.pipelines.pipeline_utils import DiffusionPipeline from diffusers.schedulers.scheduling_utils import SchedulerMixin from diffusers.configuration_utils import FrozenDict from diffusers.utils import BaseOutput from diffusers.utils.torch_utils import randn_tensor from rosetta.utils import PRECISION_TO_TYPE def normal_weight_reset_parameters(std=0.02, bias_type="default"): def _wrap_fn(_self): init.normal_(_self.weight, std=std) if hasattr(_self, "bias") and _self.bias is not None: if bias_type == "default": fan_in, _ = init._calculate_fan_in_and_fan_out(_self.weight) bound = 1 / math.sqrt(fan_in) if fan_in > 0 else 0 init.uniform_(_self.bias, -bound, bound) elif bias_type == "zeros": init.zeros_(_self.bias) else: raise ValueError(f"Unsupported bias_init_type: {bias_type}") return _wrap_fn def timestep_embedding(t, dim, max_period=10000): half = dim // 2 freqs = torch.exp( -math.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32) / half ).to(device=t.device) args = t[:, None].float() * freqs[None] embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1) if dim % 2: embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1) return embedding class TimestepEmbedder(nn.Module): def __init__( self, hidden_size, act_layer=nn.GELU, frequency_embedding_size=256, max_period=10000, out_size=None, dtype=None, device=None, config=None, ): factory_kwargs = {'dtype': dtype, 'device': device} super().__init__() self.frequency_embedding_size = frequency_embedding_size self.max_period = max_period self.init_std = config.init_std if config is not None and hasattr(config, 'init_std') else 0.02 out_size = hidden_size if out_size is None else out_size self.mlp = nn.Sequential( nn.Linear(frequency_embedding_size, hidden_size, bias=True, **factory_kwargs), act_layer(), nn.Linear(hidden_size, out_size, bias=True, **factory_kwargs), ) nn.init.normal_(self.mlp[0].weight, std=self.init_std) nn.init.normal_(self.mlp[2].weight, std=self.init_std) def prepare_reset_parameters(self): self.mlp[0].reset_parameters = normal_weight_reset_parameters(std=self.init_std).__get__(self.mlp[0]) self.mlp[2].reset_parameters = normal_weight_reset_parameters(std=self.init_std).__get__(self.mlp[2]) def forward(self, t): t_freq = timestep_embedding(t, self.frequency_embedding_size, self.max_period).type(self.mlp[0].weight.dtype) return self.mlp(t_freq) def zero_reset_parameters(_self): init.zeros_(_self.weight) if hasattr(_self, 'bias') and _self.bias is not None: init.zeros_(_self.bias) def conv_nd(dims, *args, **kwargs): if dims != 2: raise ValueError(f"unsupported dimensions: {dims}") return nn.Conv2d(*args, **kwargs) def linear(*args, **kwargs): return nn.Linear(*args, **kwargs) def zero_module(module): for p in module.parameters(): p.detach().zero_() return module def normalization(channels, **kwargs): return nn.GroupNorm(32, channels, **kwargs) class ResBlock(nn.Module): def __init__( self, in_channels, emb_channels, out_channels=None, dropout=0.0, dims=2, device=None, dtype=None, kernel_size=3, padding=1, ): factory_kwargs = {'dtype': dtype, 'device': device} super().__init__() self.in_channels = in_channels self.dropout = dropout self.out_channels = out_channels or self.in_channels if dims != 2: raise ValueError("Only 2D image projector blocks are supported.") self.in_layers = nn.Sequential( normalization(self.in_channels, **factory_kwargs), nn.SiLU(), conv_nd(dims, self.in_channels, self.out_channels, kernel_size, padding=padding, **factory_kwargs), ) self.emb_layers = nn.Sequential( nn.SiLU(), linear(emb_channels, 2 * self.out_channels, **factory_kwargs), ) self.out_layers = nn.Sequential( normalization(self.out_channels, **factory_kwargs), nn.SiLU(), nn.Dropout(p=dropout), zero_module(conv_nd(dims, self.out_channels, self.out_channels, kernel_size, padding=padding, **factory_kwargs)), ) self.skip_connection = ( nn.Identity() if self.out_channels == self.in_channels else conv_nd(dims, self.in_channels, self.out_channels, 1, **factory_kwargs) ) def reset_parameters(self): self.out_layers[3].reset_parameters = zero_reset_parameters.__get__(self.out_layers[3]) def forward(self, x, emb): h = self.in_layers(x) emb_out = self.emb_layers(emb) while len(emb_out.shape) < len(h.shape): emb_out = emb_out[..., None] out_norm, out_rest = self.out_layers[0], self.out_layers[1:] scale, shift = torch.chunk(emb_out, 2, dim=1) h = out_norm(h) * (1. + scale) + shift h = out_rest(h) return self.skip_connection(x) + h class UNetDown(nn.Module): def __init__( self, in_channels, emb_channels, hidden_channels, out_channels, dropout=0.0, device=None, dtype=None, kernel_size=3, padding=1, ): factory_kwargs = {'dtype': dtype, 'device': device} super().__init__() self.model = nn.ModuleList([ conv_nd(2, in_channels=in_channels, out_channels=hidden_channels, kernel_size=kernel_size, padding=padding, **factory_kwargs), ResBlock( in_channels=hidden_channels, emb_channels=emb_channels, out_channels=out_channels, dropout=dropout, dims=2, kernel_size=kernel_size, padding=padding, **factory_kwargs, ), ]) def forward(self, x, t): assert x.ndim == 4, f"image latents should be 4D [B, C, H, W], got {list(x.shape)}" for module in self.model: x = module(x, t) if isinstance(module, ResBlock) else module(x) _, _, *token_sizes = x.shape x = rearrange(x, 'b c h w -> b (h w) c') return x, *token_sizes class UNetUp(nn.Module): def __init__( self, in_channels, emb_channels, hidden_channels, out_channels, dropout=0.0, device=None, dtype=None, out_norm=False, kernel_size=3, padding=1, ): factory_kwargs = {'dtype': dtype, 'device': device} super().__init__() self.model = nn.ModuleList([ ResBlock( in_channels=in_channels, emb_channels=emb_channels, out_channels=hidden_channels, dropout=dropout, dims=2, kernel_size=kernel_size, padding=padding, **factory_kwargs, ) ]) if out_norm: self.model.append(nn.Sequential( normalization(hidden_channels, **factory_kwargs), nn.SiLU(), conv_nd(2, in_channels=hidden_channels, out_channels=out_channels, kernel_size=kernel_size, padding=padding, **factory_kwargs), )) else: self.model.append(conv_nd( 2, in_channels=hidden_channels, out_channels=out_channels, kernel_size=kernel_size, padding=padding, **factory_kwargs, )) def forward(self, x, t, *token_sizes): token_h, token_w = token_sizes x = rearrange(x, 'b (h w) c -> b c h w', h=token_h, w=token_w) for module in self.model: x = module(x, t) if isinstance(module, ResBlock) else module(x) return x def project_in_layer(config, **kwargs): return UNetDown( emb_channels=config.hidden_size, in_channels=config.vae_latent_dim, hidden_channels=config.patch_embed_hidden_dim, out_channels=config.hidden_size, **kwargs, ) def project_out_layer(config, **kwargs): return UNetUp( emb_channels=config.hidden_size, in_channels=config.hidden_size, hidden_channels=config.patch_embed_hidden_dim, out_channels=config.vae_latent_dim, out_norm=True, **kwargs, ) class FlowMatchDiscreteScheduler(SchedulerMixin, ConfigMixin): order = 1 @register_to_config def __init__( self, num_train_timesteps: int = 1000, shift: float = 1.0, reverse: bool = True, start_sigma: float = 1.0, end_sigma: float = 0.0, ): sigmas = torch.linspace(start_sigma, end_sigma, num_train_timesteps + 1) if not reverse: sigmas = sigmas.flip(0) self.sigmas = sigmas self.timesteps = (sigmas[:-1] * num_train_timesteps).to(dtype=torch.float32) self._step_index = None def set_timesteps(self, num_inference_steps: int, device: Union[str, torch.device] = None): self.num_inference_steps = num_inference_steps sigmas = torch.linspace(self.config.start_sigma, self.config.end_sigma, num_inference_steps + 1) if self.config.shift != 1.: sigmas = (self.config.shift * sigmas) / (1 + (self.config.shift - 1) * sigmas) if not self.config.reverse: sigmas = 1 - sigmas self.sigmas = sigmas self.timesteps = (sigmas[:-1] * self.config.num_train_timesteps).to(dtype=torch.float32, device=device) self._step_index = None def _init_step_index(self, timestep): if isinstance(timestep, torch.Tensor): timestep = timestep.to(self.timesteps.device) indices = (self.timesteps == timestep).nonzero() self._step_index = indices[1 if len(indices) > 1 else 0].item() def step( self, model_output: torch.FloatTensor, timestep: Union[float, torch.FloatTensor], sample: torch.FloatTensor, ) -> Tuple[torch.Tensor]: if isinstance(timestep, (int, torch.IntTensor, torch.LongTensor)): raise ValueError("Pass a scheduler timestep value, not an integer step index.") if self._step_index is None: self._init_step_index(timestep) sample = sample.to(torch.float32) model_output = model_output.to(torch.float32) sigma = self.sigmas[self._step_index] sigma_next = self.sigmas[self._step_index + 1] self._step_index += 1 return (sample + model_output * (sigma_next - sigma),) class ClassifierFreeGuidance: def __call__(self, pred_cond: torch.Tensor, pred_uncond: torch.Tensor, guidance_scale: float, step: int): return pred_uncond + guidance_scale * (pred_cond - pred_uncond) class MultimodalPipeline(DiffusionPipeline): def __init__( self, model, scheduler: SchedulerMixin, vae, vae_autocast_dtype: Optional[torch.dtype] = None, progress_bar_config: Dict[str, Any] = None, ): super().__init__() self._progress_bar_config = getattr(self, "_progress_bar_config", {}) self._progress_bar_config.update(progress_bar_config or {}) self.vae_autocast_dtype = vae_autocast_dtype self.register_modules(model=model, scheduler=scheduler, vae=vae) self.latent_scale_factor = self.model.config.vae_downsample_factor self.image_processor = VaeImageProcessor(vae_scale_factor=self.latent_scale_factor) self.cfg_operator = ClassifierFreeGuidance() def prepare_latents(self, batch_size, latent_channel, image_size, dtype, device, generator, latents=None): if isinstance(image_size, list): assert len(image_size) == batch_size return self._prepare_latents_variable_res( batch_size, latent_channel, image_size, dtype, device, generator, latents, ) if self.latent_scale_factor is None: latent_scale_factor = (1,) * len(image_size) elif isinstance(self.latent_scale_factor, int): latent_scale_factor = (self.latent_scale_factor,) * len(image_size) elif isinstance(self.latent_scale_factor, (tuple, list)): latent_scale_factor = self.latent_scale_factor else: raise ValueError(f"Unsupported latent_scale_factor: {self.latent_scale_factor}") latents_shape = ( batch_size, latent_channel, *[int(s) // f for s, f in zip(image_size, latent_scale_factor)], ) if isinstance(generator, list) and len(generator) != batch_size: raise ValueError("Generator list length must match batch size.") latents = randn_tensor(latents_shape, generator=generator, device=device, dtype=dtype) if latents is None else latents.to(device) if hasattr(self.scheduler, "init_noise_sigma"): latents = latents * self.scheduler.init_noise_sigma return latents def _prepare_latents_variable_res( self, batch_size, latent_channel, image_sizes, dtype, device, generator, latents=None, ): ndim = len(image_sizes[0]) if self.latent_scale_factor is None: scale_factors = (1,) * ndim elif isinstance(self.latent_scale_factor, int): scale_factors = (self.latent_scale_factor,) * ndim else: scale_factors = tuple(self.latent_scale_factor) latents_list = [] for i, img_size in enumerate(image_sizes): shape = (1, latent_channel, *tuple(int(s) // f for s, f in zip(img_size, scale_factors))) gen = generator[i] if isinstance(generator, list) else generator lat = randn_tensor(shape, generator=gen, device=device, dtype=dtype) if latents is None else latents[i:i + 1].to(device) if hasattr(self.per_sample_schedulers[i], "init_noise_sigma"): lat = lat * self.per_sample_schedulers[i].init_noise_sigma latents_list.append(lat) return latents_list @property def guidance_scale(self): return self._guidance_scale @property def do_classifier_free_guidance(self): return self._guidance_scale > 1.0 and self._cfg_factor > 1 @torch.no_grad() def __call__( self, batch_size: int, image_size: tuple[int, int] | list[tuple[int, int]], num_inference_steps: int = 50, guidance_scale: float = 7.5, generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None, latents: Optional[torch.Tensor] = None, output_type: Optional[str] = "pil", model_kwargs: Dict[str, Any] = None, **kwargs, ) -> list[Image.Image]: self._guidance_scale = guidance_scale cfg_factor = kwargs.pop('cfg_factor', None) self._cfg_factor = cfg_factor if cfg_factor is not None else (2 if guidance_scale > 1.0 else 1) device = self.model.device is_variable_res = isinstance(image_size, list) if is_variable_res: self.per_sample_schedulers = [copy.deepcopy(self.scheduler) for _ in range(batch_size)] latents = self.prepare_latents( batch_size=batch_size, latent_channel=self.model.config.vae_latent_dim, image_size=image_size, dtype=torch.float32, device=device, generator=generator, latents=latents, ) if is_variable_res: per_sample_timesteps = [] for b_idx in range(batch_size): self.per_sample_schedulers[b_idx].set_timesteps(num_inference_steps, device=device) per_sample_timesteps.append(self.per_sample_schedulers[b_idx].timesteps) timesteps = per_sample_timesteps[0] scheduler_order = self.per_sample_schedulers[0].order else: self.scheduler.set_timesteps(num_inference_steps, device=device) timesteps = self.scheduler.timesteps scheduler_order = self.scheduler.order input_ids = model_kwargs.pop("input_ids") attention_mask = self.model._prepare_attention_mask_for_generation( input_ids, self.model.generation_config, model_kwargs=model_kwargs, ) model_kwargs["attention_mask"] = attention_mask.to(device) num_warmup_steps = len(timesteps) - num_inference_steps * scheduler_order with self.progress_bar(total=num_inference_steps) as progress_bar: for i, t in enumerate(timesteps): if is_variable_res: latent_model_input = [lat.clone() for lat in latents] * self._cfg_factor per_t = [per_sample_timesteps[j][i] for j in range(batch_size)] t_expand = torch.stack(per_t * self._cfg_factor) else: latent_model_input = torch.cat([latents] * self._cfg_factor) t_expand = t.repeat(latent_model_input.shape[0]) model_inputs = self.model.prepare_inputs_for_generation( input_ids, images=latent_model_input, timesteps=t_expand, **model_kwargs, ) with torch.autocast(device_type="cuda", dtype=torch.bfloat16, enabled=True): model_output = self.model(**model_inputs, first_step=(i == 0)) pred = model_output["diffusion_prediction"] if is_variable_res: pred = [p.to(dtype=torch.float32) for p in pred] if self.do_classifier_free_guidance: half = len(pred) // 2 pred = [ self.cfg_operator(pc, pu, self.guidance_scale, step=i) for pc, pu in zip(pred[:half], pred[half:]) ] for j in range(batch_size): latents[j] = self.per_sample_schedulers[j].step( pred[j], per_sample_timesteps[j][i], latents[j], )[0] else: pred = pred.to(dtype=torch.float32) if pred.ndim == 5 and pred.size(2) == 1 and latents.ndim == 4: pred = pred.squeeze(2) if self.do_classifier_free_guidance: pred_cond, pred_uncond = pred.chunk(2) pred = self.cfg_operator(pred_cond, pred_uncond, self.guidance_scale, step=i) latents = self.scheduler.step(pred, t, latents)[0] if i != len(timesteps) - 1: model_kwargs = self.model._update_model_kwargs_for_generation(model_output, model_kwargs) input_ids = None if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % scheduler_order == 0): progress_bar.update() return self.decode_latents(latents, is_variable_res, output_type, generator) def decode_latents(self, latents, is_variable_res, output_type, generator): if is_variable_res: images = [] for lat in latents: images.extend(self.decode_latents(lat, False, output_type, generator)) return images if hasattr(self.vae.config, 'scaling_factor') and self.vae.config.scaling_factor: latents = latents / self.vae.config.scaling_factor if hasattr(self.vae.config, 'shift_factor') and self.vae.config.shift_factor: latents = latents + self.vae.config.shift_factor with torch.autocast( device_type="cuda", dtype=self.vae_autocast_dtype, enabled=self.vae_autocast_dtype is not None and self.vae_autocast_dtype != torch.float32, ): image = self.vae.decode(latents, return_dict=False, generator=generator)[0] return self.image_processor.postprocess( image, output_type=output_type, do_denormalize=[True] * image.shape[0], ) @dataclass class AutoEncoderParams: resolution: int = 256 in_channels: int = 3 ch: int = 128 out_ch: int = 3 ch_mult: list[int] = field(default_factory=lambda: [1, 2, 4, 4]) num_res_blocks: int = 2 z_channels: int = 32 @classmethod def from_json(cls, json_path: str | Path) -> "AutoEncoderParams": with open(json_path, 'r') as f: config_dict = json.load(f) valid_fields = {f.name for f in fields(cls)} filtered_dict = {k: v for k, v in config_dict.items() if k in valid_fields} return cls(**filtered_dict) def to_dict(self) -> dict[str, Any]: return asdict(self) @dataclass class DecoderOutput(BaseOutput): sample: torch.FloatTensor def swish(x: Tensor) -> Tensor: return x * torch.sigmoid(x) class AttnBlock(nn.Module): def __init__(self, in_channels: int): super().__init__() self.in_channels = in_channels self.norm = nn.GroupNorm(num_groups=32, num_channels=in_channels, eps=1e-6, affine=True) self.q = nn.Conv2d(in_channels, in_channels, kernel_size=1) self.k = nn.Conv2d(in_channels, in_channels, kernel_size=1) self.v = nn.Conv2d(in_channels, in_channels, kernel_size=1) self.proj_out = nn.Conv2d(in_channels, in_channels, kernel_size=1) def attention(self, h_: Tensor) -> Tensor: h_ = self.norm(h_) q = self.q(h_) k = self.k(h_) v = self.v(h_) b, c, h, w = q.shape q = rearrange(q, "b c h w -> b 1 (h w) c").contiguous() k = rearrange(k, "b c h w -> b 1 (h w) c").contiguous() v = rearrange(v, "b c h w -> b 1 (h w) c").contiguous() h_ = nn.functional.scaled_dot_product_attention(q, k, v) return rearrange(h_, "b 1 (h w) c -> b c h w", h=h, w=w, c=c, b=b) def forward(self, x: Tensor) -> Tensor: return x + self.proj_out(self.attention(x)) class ResnetBlock(nn.Module): def __init__(self, in_channels: int, out_channels: int): super().__init__() self.in_channels = in_channels out_channels = in_channels if out_channels is None else out_channels self.out_channels = out_channels self.norm1 = nn.GroupNorm(num_groups=32, num_channels=in_channels, eps=1e-6, affine=True) self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=1, padding=1) self.norm2 = nn.GroupNorm(num_groups=32, num_channels=out_channels, eps=1e-6, affine=True) self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1) if self.in_channels != self.out_channels: self.nin_shortcut = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1, padding=0) def forward(self, x): h = x h = self.norm1(h) h = swish(h) h = self.conv1(h) h = self.norm2(h) h = swish(h) h = self.conv2(h) if self.in_channels != self.out_channels: x = self.nin_shortcut(x) return x + h class Downsample(nn.Module): def __init__(self, in_channels: int): super().__init__() self.conv = nn.Conv2d(in_channels, in_channels, kernel_size=3, stride=2, padding=0) def forward(self, x: Tensor): pad = (0, 1, 0, 1) x = nn.functional.pad(x, pad, mode="constant", value=0) x = self.conv(x) return x class Upsample(nn.Module): def __init__(self, in_channels: int): super().__init__() self.conv = nn.Conv2d(in_channels, in_channels, kernel_size=3, stride=1, padding=1) def forward(self, x: Tensor): x = nn.functional.interpolate(x, scale_factor=2.0, mode="nearest") x = self.conv(x) return x class Encoder(nn.Module): def __init__( self, resolution: int, in_channels: int, ch: int, ch_mult: list[int], num_res_blocks: int, z_channels: int, ): super().__init__() self.quant_conv = torch.nn.Conv2d(2 * z_channels, 2 * z_channels, 1) self.ch = ch self.num_resolutions = len(ch_mult) self.num_res_blocks = num_res_blocks self.resolution = resolution self.in_channels = in_channels self.conv_in = nn.Conv2d(in_channels, self.ch, kernel_size=3, stride=1, padding=1) curr_res = resolution in_ch_mult = (1,) + tuple(ch_mult) self.in_ch_mult = in_ch_mult self.down = nn.ModuleList() block_in = self.ch for i_level in range(self.num_resolutions): block = nn.ModuleList() attn = nn.ModuleList() block_in = ch * in_ch_mult[i_level] block_out = ch * ch_mult[i_level] for _ in range(self.num_res_blocks): block.append(ResnetBlock(in_channels=block_in, out_channels=block_out)) block_in = block_out down = nn.Module() down.block = block down.attn = attn if i_level != self.num_resolutions - 1: down.downsample = Downsample(block_in) curr_res = curr_res // 2 self.down.append(down) self.mid = nn.Module() self.mid.block_1 = ResnetBlock(in_channels=block_in, out_channels=block_in) self.mid.attn_1 = AttnBlock(block_in) self.mid.block_2 = ResnetBlock(in_channels=block_in, out_channels=block_in) self.norm_out = nn.GroupNorm(num_groups=32, num_channels=block_in, eps=1e-6, affine=True) self.conv_out = nn.Conv2d(block_in, 2 * z_channels, kernel_size=3, stride=1, padding=1) def forward(self, x: Tensor) -> Tensor: hs = [self.conv_in(x)] for i_level in range(self.num_resolutions): for i_block in range(self.num_res_blocks): h = self.down[i_level].block[i_block](hs[-1]) if len(self.down[i_level].attn) > 0: h = self.down[i_level].attn[i_block](h) hs.append(h) if i_level != self.num_resolutions - 1: hs.append(self.down[i_level].downsample(hs[-1])) h = hs[-1] h = self.mid.block_1(h) h = self.mid.attn_1(h) h = self.mid.block_2(h) h = self.norm_out(h) h = swish(h) h = self.conv_out(h) h = self.quant_conv(h) return h class Decoder(nn.Module): def __init__( self, ch: int, out_ch: int, ch_mult: list[int], num_res_blocks: int, in_channels: int, resolution: int, z_channels: int, ): super().__init__() self.post_quant_conv = torch.nn.Conv2d(z_channels, z_channels, 1) self.ch = ch self.num_resolutions = len(ch_mult) self.num_res_blocks = num_res_blocks self.resolution = resolution self.in_channels = in_channels self.ffactor = 2 ** (self.num_resolutions - 1) block_in = ch * ch_mult[self.num_resolutions - 1] curr_res = resolution // 2 ** (self.num_resolutions - 1) self.z_shape = (1, z_channels, curr_res, curr_res) self.conv_in = nn.Conv2d(z_channels, block_in, kernel_size=3, stride=1, padding=1) self.mid = nn.Module() self.mid.block_1 = ResnetBlock(in_channels=block_in, out_channels=block_in) self.mid.attn_1 = AttnBlock(block_in) self.mid.block_2 = ResnetBlock(in_channels=block_in, out_channels=block_in) self.up = nn.ModuleList() for i_level in reversed(range(self.num_resolutions)): block = nn.ModuleList() attn = nn.ModuleList() block_out = ch * ch_mult[i_level] for _ in range(self.num_res_blocks + 1): block.append(ResnetBlock(in_channels=block_in, out_channels=block_out)) block_in = block_out up = nn.Module() up.block = block up.attn = attn if i_level != 0: up.upsample = Upsample(block_in) curr_res = curr_res * 2 self.up.insert(0, up) # prepend to get consistent order self.norm_out = nn.GroupNorm(num_groups=32, num_channels=block_in, eps=1e-6, affine=True) self.conv_out = nn.Conv2d(block_in, out_ch, kernel_size=3, stride=1, padding=1) def forward(self, z: Tensor) -> Tensor: z = self.post_quant_conv(z) upscale_dtype = next(self.up.parameters()).dtype h = self.conv_in(z) h = self.mid.block_1(h) h = self.mid.attn_1(h) h = self.mid.block_2(h) h = h.to(upscale_dtype) for i_level in reversed(range(self.num_resolutions)): for i_block in range(self.num_res_blocks + 1): h = self.up[i_level].block[i_block](h) if len(self.up[i_level].attn) > 0: h = self.up[i_level].attn[i_block](h) if i_level != 0: h = self.up[i_level].upsample(h) h = self.norm_out(h) h = swish(h) h = self.conv_out(h) return h class AutoEncoder(nn.Module): def __init__(self, params: AutoEncoderParams): super().__init__() self.params = params self.encoder = Encoder( resolution=params.resolution, in_channels=params.in_channels, ch=params.ch, ch_mult=params.ch_mult, num_res_blocks=params.num_res_blocks, z_channels=params.z_channels, ) self.decoder = Decoder( resolution=params.resolution, in_channels=params.in_channels, ch=params.ch, out_ch=params.out_ch, ch_mult=params.ch_mult, num_res_blocks=params.num_res_blocks, z_channels=params.z_channels, ) self.bn_eps = 1e-4 self.bn_momentum = 0.1 self.ps = [2, 2] self.bn = torch.nn.BatchNorm2d( math.prod(self.ps) * params.z_channels, eps=self.bn_eps, momentum=self.bn_momentum, affine=False, track_running_stats=True, ) def normalize(self, z): self.bn.eval() return self.bn(z) def inv_normalize(self, z): self.bn.eval() s = torch.sqrt(self.bn.running_var.view(1, -1, 1, 1) + self.bn_eps) m = self.bn.running_mean.view(1, -1, 1, 1) return z * s + m def encode(self, x: Tensor) -> Tensor: moments = self.encoder(x) mean = torch.chunk(moments, 2, dim=1)[0] z = rearrange( mean, "... c (i pi) (j pj) -> ... (c pi pj) i j", pi=self.ps[0], pj=self.ps[1], ) z = self.normalize(z) return z def decode(self, z: Tensor, return_dict: bool = True, generator=None) -> Tensor: z = self.inv_normalize(z) z = rearrange( z, "... (c pi pj) i j -> ... c (i pi) (j pj)", pi=self.ps[0], pj=self.ps[1], ) dec = self.decoder(z) if not return_dict: return (dec,) return DecoderOutput(sample=dec) @classmethod def from_pretrained(cls, path: str): path = Path(path) config_path = path / "config.json" if config_path.exists(): with open(config_path, 'r') as f: config_dict = json.load(f) params = AutoEncoderParams.from_json(config_path) config = FrozenDict(config_dict) else: print(f"Warning: config.json not found at {config_path}, using default params") params = AutoEncoderParams() config = FrozenDict(params.to_dict()) model = cls(params=params) model.config = config model.load_state_dict(torch.load(path / "model.pt", map_location="cpu", weights_only=True), strict=True) return model ASSETS_BASE = os.getenv("ASSETS_BASE", "./public_assets").rstrip("/") VAE_BASE = os.getenv("VAE_BASE", f"{ASSETS_BASE}/image_encoder").rstrip("/") VAE_META_INFO = { "16x16-128c-flux2": { "path": f"{VAE_BASE}/flux2-vae", "downsample_factor": [16, 16], "trans_type": "-11", }, } def load_vae( vae_type, vae_precision=None, device=None, logger=None, args=None, ): if logger is None: from loguru import logger vae_meta_info = VAE_META_INFO[vae_type] vae_path = Path(vae_meta_info["path"]) config_file = vae_path / "config.json" with open(config_file, "r") as f: config = json.load(f) if "_class_name" in config: classname = config.pop("_class_name") else: raise ValueError(f"Cannot find the _class_name in {config_file}") logger.info(f"Load VAE with class {classname} from {config_file}") logger.info(f"Load vae_type: {vae_type} from path: {vae_path}") if classname != "AutoencoderKLFlux2" and "flux" not in vae_type: raise NotImplementedError(f"VAE class {classname} is not supported.") vae = AutoEncoder.from_pretrained(vae_path) vae._downsample_factor = vae_meta_info["downsample_factor"] if not hasattr(vae, 'downsample_factor'): vae.downsample_factor = vae_meta_info["downsample_factor"][0] vae._trans_type = vae_meta_info["trans_type"] if args is not None: vae.autocast_dtype = PRECISION_TO_TYPE[args.vae_autocast_dtype] if vae_precision is not None: logger.warning(f"You are transforming VAE to {vae_precision} precision! Please make sure this is what you want.") vae = vae.to(dtype=PRECISION_TO_TYPE[vae_precision]) if device is not None: vae = vae.to(device=device) vae.requires_grad_(False) vae.eval() return vae