# Minimal RAE ViT-MAE decoder (ported from # https://github.com/bytetriper/RAE — src/stage1/decoders/decoder.py). # # Only the decoder-side pieces are needed here: a trainable CLS token is # prepended to the patch tokens, fixed 2D sin-cos positional embeddings are # added, a stack of ViTMAELayer blocks processes them, and a linear head # predicts patch_size**2 * 3 values per patch which are unpatchified to # pixels. We reuse HuggingFace's `ViTMAELayer` / `ViTMAEConfig` so the # published RAE state_dict keys (`decoder_layers.N.attention.attention.*` # etc.) load cleanly with strict=True. from copy import deepcopy from typing import Optional, Tuple import numpy as np import torch import torch.nn as nn from transformers.models.vit_mae.configuration_vit_mae import ViTMAEConfig from transformers.models.vit_mae.modeling_vit_mae import ViTMAELayer def _get_1d_sincos_pos_embed_from_grid(embed_dim: int, pos: np.ndarray) -> np.ndarray: assert embed_dim % 2 == 0 omega = np.arange(embed_dim // 2, dtype=float) omega /= embed_dim / 2.0 omega = 1.0 / 10000**omega pos = pos.reshape(-1) out = np.einsum("m,d->md", pos, omega) return np.concatenate([np.sin(out), np.cos(out)], axis=1) def _get_2d_sincos_pos_embed_from_grid(embed_dim: int, grid: np.ndarray) -> np.ndarray: assert embed_dim % 2 == 0 emb_h = _get_1d_sincos_pos_embed_from_grid(embed_dim // 2, grid[0]) emb_w = _get_1d_sincos_pos_embed_from_grid(embed_dim // 2, grid[1]) return np.concatenate([emb_h, emb_w], axis=1) def get_2d_sincos_pos_embed(embed_dim: int, grid_size: int, add_cls_token: bool = False) -> np.ndarray: grid_h = np.arange(grid_size, dtype=np.float32) grid_w = np.arange(grid_size, dtype=np.float32) grid = np.meshgrid(grid_w, grid_h) # w goes first grid = np.stack(grid, axis=0).reshape([2, 1, grid_size, grid_size]) pos_embed = _get_2d_sincos_pos_embed_from_grid(embed_dim, grid) if add_cls_token: pos_embed = np.concatenate([np.zeros([1, embed_dim]), pos_embed], axis=0) return pos_embed class GeneralDecoder(nn.Module): """ViT-MAE decoder used by RAE. Input: (B, N, hidden_size) patch tokens — N usually matches num_patches. Output: pixel reconstruction (B, 3, image_size, image_size), via `unpatchify(decoder_pred(decoder_layers(..)))`. Differences from vanilla HF `ViTMAEDecoder`: * A learnable ``trainable_cls_token`` is prepended (no mask tokens). * Input length may differ from ``num_patches``; `interpolate_latent` bilinearly rescales it to match the decoder's positional grid. * `drop_cls_token=True` lets callers pass tokens that already contain a CLS at index 0 (stripped and replaced). RAE's pretrained weights always call with `drop_cls_token=False`, matching the pure-patch latent produced by our DINOv2 encoder. """ def __init__(self, config: ViTMAEConfig, num_patches: int): super().__init__() self.decoder_embed = nn.Linear(config.hidden_size, config.decoder_hidden_size, bias=True) self.decoder_pos_embed = nn.Parameter( torch.zeros(1, num_patches + 1, config.decoder_hidden_size), requires_grad=False ) decoder_config = deepcopy(config) decoder_config.hidden_size = config.decoder_hidden_size decoder_config.num_hidden_layers = config.decoder_num_hidden_layers decoder_config.num_attention_heads = config.decoder_num_attention_heads decoder_config.intermediate_size = config.decoder_intermediate_size # Newer transformers routes attention through ALL_ATTENTION_FUNCTIONS # keyed by `_attn_implementation`; default ("sdpa") keeps math identical # but dispatches via scaled_dot_product_attention. decoder_config._attn_implementation = getattr(config, "_attn_implementation", "sdpa") or "sdpa" self.decoder_layers = nn.ModuleList( [ViTMAELayer(decoder_config) for _ in range(config.decoder_num_hidden_layers)] ) self.decoder_norm = nn.LayerNorm(config.decoder_hidden_size, eps=config.layer_norm_eps) self.decoder_pred = nn.Linear(config.decoder_hidden_size, config.patch_size**2 * config.num_channels, bias=True) self.config = config self.num_patches = num_patches self.trainable_cls_token = nn.Parameter(torch.zeros(1, 1, config.decoder_hidden_size)) # Init fixed sin-cos decoder pos embed (CLS slot stays zero). pos = get_2d_sincos_pos_embed(self.decoder_pos_embed.shape[-1], int(num_patches**0.5), add_cls_token=True) self.decoder_pos_embed.data.copy_(torch.from_numpy(pos).float().unsqueeze(0)) def interpolate_latent(self, x: torch.Tensor) -> torch.Tensor: """(B, L, C) → (B, num_patches, C), bilinear on the 2D grid.""" b, l, c = x.shape if l == self.num_patches: return x h = w = int(l**0.5) assert h * w == l, f"cannot reshape length {l} to a square grid" x = x.reshape(b, h, w, c).permute(0, 3, 1, 2) target = int(self.num_patches**0.5) x = nn.functional.interpolate(x, size=(target, target), mode="bilinear", align_corners=False) return x.permute(0, 2, 3, 1).contiguous().view(b, self.num_patches, c) def unpatchify( self, patches: torch.Tensor, original_image_size: Optional[Tuple[int, int]] = None, ) -> torch.Tensor: patch_size, num_channels = self.config.patch_size, self.config.num_channels H, W = ( original_image_size if original_image_size is not None else (self.config.image_size, self.config.image_size) ) nph, npw = H // patch_size, W // patch_size assert nph * npw == patches.shape[1], f"patch count {patches.shape[1]} does not match grid {nph}*{npw}" B = patches.shape[0] patches = patches.reshape(B, nph, npw, patch_size, patch_size, num_channels) patches = torch.einsum("nhwpqc->nchpwq", patches) return patches.reshape(B, num_channels, nph * patch_size, npw * patch_size) def forward(self, hidden_states: torch.Tensor, drop_cls_token: bool = False) -> torch.Tensor: """Returns patch logits (B, num_patches, patch_size**2 * 3).""" x = self.decoder_embed(hidden_states) if drop_cls_token: x = self.interpolate_latent(x[:, 1:, :]) else: x = self.interpolate_latent(x) cls_token = self.trainable_cls_token.expand(x.shape[0], -1, -1) x = torch.cat([cls_token, x], dim=1) + self.decoder_pos_embed for layer in self.decoder_layers: x = layer(x) x = self.decoder_norm(x) logits = self.decoder_pred(x) return logits[:, 1:, :] # strip CLS slot