GLINT-CT / vjepa21_encoder.py
seongeun-lee's picture
Release GLINT-CT
c5a246d
Raw History Blame Contribute Delete
36.4 kB
"""V-JEPA 2.1 encoder.
Adapted from https://github.com/facebookresearch/vjepa2
Copyright (c) Meta Platforms, Inc. and affiliates. MIT License (LICENSES/MIT.txt)
drop_path from timm (https://github.com/huggingface/pytorch-image-models).
Copyright 2020 Ross Wightman. Apache License 2.0 (LICENSES/Apache-2.0.txt)
"""
import logging
import math
import torch
logger = logging.getLogger("glint")
def _no_grad_trunc_normal_(tensor, mean, std, a, b):
def norm_cdf(x):
return (1.0 + math.erf(x / math.sqrt(2.0))) / 2.0
with torch.no_grad():
lower = norm_cdf((a - mean) / std)
upper = norm_cdf((b - mean) / std)
tensor.uniform_(2 * lower - 1, 2 * upper - 1)
tensor.erfinv_()
tensor.mul_(std * math.sqrt(2.0))
tensor.add_(mean)
tensor.clamp_(min=a, max=b)
return tensor
def trunc_normal_(tensor, mean=0.0, std=1.0, a=-2.0, b=2.0):
return _no_grad_trunc_normal_(tensor, mean, std, a, b)
def repeat_interleave_batch(x, B, repeat):
N = len(x) // B
x = torch.cat([torch.cat([x[i * B : (i + 1) * B] for _ in range(repeat)], dim=0) for i in range(N)], dim=0)
return x
def apply_masks(x, masks, concat=True):
all_x = []
for m in masks:
mask_keep = m.unsqueeze(-1).repeat(1, 1, x.size(-1))
all_x += [torch.gather(x, dim=1, index=mask_keep)]
if not concat:
return all_x
return torch.cat(all_x, dim=0)
import torch.nn as nn # noqa: E402
class PatchEmbed3D(nn.Module):
def __init__(
self,
patch_size=16,
tubelet_size=2,
in_chans=3,
embed_dim=768,
):
super().__init__()
self.patch_size = patch_size
self.tubelet_size = tubelet_size
self.proj = nn.Conv3d(
in_channels=in_chans,
out_channels=embed_dim,
kernel_size=(tubelet_size, patch_size, patch_size),
stride=(tubelet_size, patch_size, patch_size),
)
def forward(self, x, **kwargs):
B, C, T, H, W = x.shape
x = self.proj(x).flatten(2).transpose(1, 2)
return x
import torch.nn.functional as F # noqa: E402
from torch.nn.attention import SDPBackend, sdpa_kernel # noqa: E402
def drop_path(x, drop_prob=0.0, training=False, scale_by_keep=True):
if drop_prob == 0.0 or not training:
return x
keep_prob = 1 - drop_prob
shape = (x.shape[0],) + (1,) * (x.ndim - 1)
random_tensor = x.new_empty(shape).bernoulli_(keep_prob)
if keep_prob > 0.0 and scale_by_keep:
random_tensor.div_(keep_prob)
return x * random_tensor
def rotate_queries_or_keys(x, pos, n_registers, has_cls_first):
B, num_heads, N, D = x.size()
assert D % 2 == 0, "Embedding dimension must be a multiple of 2 for block matrix rotation"
n_cls = 1 if has_cls_first else 0
start_ctx = n_cls
end_ctx = N - n_registers
x_cls = x[..., :n_cls, :] if n_cls else None
x_ctx = x[..., start_ctx:end_ctx, :]
x_reg = x[..., end_ctx:, :] if n_registers > 0 else None
omega = torch.arange(D // 2, dtype=x.dtype, device=x.device)
omega /= D / 2.0
omega = 1.0 / 10000**omega
freq = torch.einsum("..., f -> ... f", pos, omega)
emb_sin = freq.sin()
emb_cos = freq.cos()
emb_sin = emb_sin.repeat_interleave(2, dim=-1)
emb_cos = emb_cos.repeat_interleave(2, dim=-1)
y = x_ctx.unflatten(-1, (-1, 2))
y1, y2 = y.unbind(
dim=-1,
)
y = torch.stack((-y2, y1), dim=-1)
y = y.flatten(-2)
out_ctx = (x_ctx * emb_cos) + (y * emb_sin)
parts = []
if n_cls:
parts.append(x_cls)
parts.append(out_ctx)
if n_registers:
parts.append(x_reg)
out = torch.cat(parts, dim=-2)
return out
class DropPath(nn.Module):
def __init__(self, drop_prob=None):
super(DropPath, self).__init__()
self.drop_prob = drop_prob
def forward(self, x):
return drop_path(x, self.drop_prob, self.training)
def extra_repr(self) -> str:
return "p={}".format(self.drop_prob)
class MLP(nn.Module):
def __init__(
self,
in_features,
hidden_features=None,
out_features=None,
act_layer=nn.GELU,
drop=0.0,
):
super().__init__()
out_features = out_features or in_features
hidden_features = hidden_features or in_features
self.fc1 = nn.Linear(in_features, hidden_features)
self.act = act_layer()
self.fc2 = nn.Linear(hidden_features, out_features)
self.drop = nn.Dropout(drop)
def forward(self, x):
x = self.fc1(x)
x = self.act(x)
x = self.drop(x)
x = self.fc2(x)
x = self.drop(x)
return x
class SwiGLUFFN(nn.Module):
def __init__(
self,
in_features,
hidden_features=None,
out_features=None,
act_layer=nn.SiLU,
drop=0.0,
wide_silu=True,
):
super().__init__()
out_features = out_features or in_features
swiglu_hidden_features = hidden_features = hidden_features or in_features
if wide_silu:
swiglu_hidden_features = int(2 * hidden_features / 3)
align_as = 8
swiglu_hidden_features = (swiglu_hidden_features + align_as - 1) // align_as * align_as
self.fc1 = nn.Linear(in_features, swiglu_hidden_features)
self.fc2 = nn.Linear(in_features, swiglu_hidden_features)
self.act = act_layer()
self.fc3 = nn.Linear(swiglu_hidden_features, out_features)
def forward(self, x):
x1 = self.fc1(x)
x2 = self.fc2(x)
hidden = F.silu(x1) * x2
return self.fc3(hidden)
class RoPEAttention(nn.Module):
def __init__(
self,
dim,
num_heads=8,
qkv_bias=False,
qk_scale=None,
attn_drop=0.0,
proj_drop=0.0,
use_sdpa=True,
grid_size=14,
is_causal=False,
n_registers=0,
has_cls_first=False,
interpolate_rope=False,
patch_size=16,
):
super().__init__()
self.num_heads = num_heads
self.head_dim = head_dim = dim // num_heads
self.scale = qk_scale or head_dim**-0.5
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
self.attn_drop = nn.Dropout(attn_drop)
self.proj = nn.Linear(dim, dim)
self.proj_drop_prob = proj_drop
self.proj_drop = nn.Dropout(proj_drop)
self.use_sdpa = use_sdpa
self.d_dim = int(2 * ((head_dim // 3) // 2))
self.h_dim = int(2 * ((head_dim // 3) // 2))
self.w_dim = int(2 * ((head_dim // 3) // 2))
self.grid_size = grid_size
self.is_causal = is_causal
self.n_registers = n_registers
self.has_cls_first = has_cls_first
self.interpolate_rope = interpolate_rope
self.pretrained_patch_size = patch_size
if patch_size == 14:
self.pretrained_grid_size = int(252 / patch_size)
elif patch_size == 16:
self.pretrained_grid_size = int(256 / patch_size)
def _get_frame_pos(self, ids, H_patches=None, W_patches=None):
if H_patches is None or W_patches is None:
tokens_per_frame = int(self.grid_size * self.grid_size)
else:
tokens_per_frame = int(H_patches * W_patches)
return ids // tokens_per_frame
def _get_height_pos(self, ids, H_patches=None, W_patches=None):
if H_patches is None or W_patches is None:
tokens_per_frame = int(self.grid_size * self.grid_size)
tokens_per_row = self.grid_size
else:
tokens_per_frame = int(H_patches * W_patches)
tokens_per_row = W_patches
frame_ids = self._get_frame_pos(ids, H_patches, W_patches)
ids = ids - tokens_per_frame * frame_ids
return ids // tokens_per_row
def separate_positions(self, ids, H_patches=None, W_patches=None):
if H_patches is None or W_patches is None:
tokens_per_frame = int(self.grid_size * self.grid_size)
tokens_per_row = self.grid_size
else:
tokens_per_frame = int(H_patches * W_patches)
tokens_per_row = W_patches
frame_ids = self._get_frame_pos(ids, H_patches, W_patches)
height_ids = self._get_height_pos(ids, H_patches, W_patches)
width_ids = (ids - tokens_per_frame * frame_ids) - tokens_per_row * height_ids
return 1.0 * frame_ids, 1.0 * height_ids, 1.0 * width_ids
def forward(
self,
x,
mask=None,
T=None,
H_patches=None,
W_patches=None,
return_attn=False,
):
B, N, C = x.size()
N_ctx = N - self.n_registers
grid_depth = int(N_ctx // (self.grid_size * self.grid_size))
qkv = self.qkv(x).unflatten(-1, (3, self.num_heads, -1)).permute(2, 0, 3, 1, 4)
q, k, v = qkv[0], qkv[1], qkv[2]
if mask is not None:
mask = mask.unsqueeze(1).repeat(1, self.num_heads, 1)
d_mask, h_mask, w_mask = self.separate_positions(mask, H_patches, W_patches)
else:
if T is None or H_patches is None or W_patches is None:
mask = torch.arange(int(grid_depth * self.grid_size * self.grid_size), device=x.device)
else:
mask = torch.arange(int(T * H_patches * W_patches), device=x.device)
d_mask, h_mask, w_mask = self.separate_positions(mask, H_patches, W_patches)
if self.interpolate_rope:
if H_patches is None:
H_patches = int(self.grid_size)
if W_patches is None:
W_patches = int(self.grid_size)
h_mask = h_mask * (self.pretrained_grid_size - 1) / (H_patches - 1)
w_mask = w_mask * (self.pretrained_grid_size - 1) / (W_patches - 1)
s = 0
qd = rotate_queries_or_keys(
q[..., s : s + self.d_dim],
pos=d_mask,
n_registers=self.n_registers,
has_cls_first=self.has_cls_first,
)
kd = rotate_queries_or_keys(
k[..., s : s + self.d_dim],
pos=d_mask,
n_registers=self.n_registers,
has_cls_first=self.has_cls_first,
)
s += self.d_dim
qh = rotate_queries_or_keys(
q[..., s : s + self.h_dim],
pos=h_mask,
n_registers=self.n_registers,
has_cls_first=self.has_cls_first,
)
kh = rotate_queries_or_keys(
k[..., s : s + self.h_dim],
pos=h_mask,
n_registers=self.n_registers,
has_cls_first=self.has_cls_first,
)
s += self.h_dim
qw = rotate_queries_or_keys(
q[..., s : s + self.w_dim],
pos=w_mask,
n_registers=self.n_registers,
has_cls_first=self.has_cls_first,
)
kw = rotate_queries_or_keys(
k[..., s : s + self.w_dim],
pos=w_mask,
n_registers=self.n_registers,
has_cls_first=self.has_cls_first,
)
s += self.w_dim
if s < self.head_dim:
qr = q[..., s:]
kr = k[..., s:]
q = torch.cat([qd, qh, qw, qr], dim=-1)
k = torch.cat([kd, kh, kw, kr], dim=-1)
else:
q = torch.cat([qd, qh, qw], dim=-1)
k = torch.cat([kd, kh, kw], dim=-1)
q = q.to(v.dtype)
k = k.to(v.dtype)
if self.use_sdpa:
with sdpa_kernel([SDPBackend.FLASH_ATTENTION, SDPBackend.EFFICIENT_ATTENTION, SDPBackend.MATH]):
x = F.scaled_dot_product_attention(q, k, v, dropout_p=self.proj_drop_prob, is_causal=self.is_causal)
attn = None
else:
attn = (q @ k.transpose(-2, -1)) * self.scale
attn = attn.softmax(dim=-1)
attn = self.attn_drop(attn)
x = attn @ v
x = x.transpose(1, 2).reshape(B, N, C)
x = self.proj(x)
x = self.proj_drop(x)
if return_attn:
return x, attn
else:
return x, None
class Attention(nn.Module):
def __init__(
self,
dim,
num_heads=8,
qkv_bias=False,
qk_scale=None,
attn_drop=0.0,
proj_drop=0.0,
use_sdpa=True,
is_causal=False,
):
super().__init__()
self.num_heads = num_heads
head_dim = dim // num_heads
self.scale = qk_scale or head_dim**-0.5
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
self.attn_drop = nn.Dropout(attn_drop)
self.proj = nn.Linear(dim, dim)
self.proj_drop_prob = proj_drop
self.proj_drop = nn.Dropout(proj_drop)
self.use_sdpa = use_sdpa
self.is_causal = is_causal
def forward(self, x):
B, N, C = x.shape
qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
q, k, v = qkv[0], qkv[1], qkv[2]
if self.use_sdpa:
with sdpa_kernel([SDPBackend.FLASH_ATTENTION, SDPBackend.EFFICIENT_ATTENTION, SDPBackend.MATH]):
x = F.scaled_dot_product_attention(q, k, v, dropout_p=self.proj_drop_prob, is_causal=self.is_causal)
attn = None
else:
attn = (q @ k.transpose(-2, -1)) * self.scale
attn = attn.softmax(dim=-1)
attn = self.attn_drop(attn)
x = attn @ v
x = x.transpose(1, 2).reshape(B, N, C)
x = self.proj(x)
x = self.proj_drop(x)
return x
class Block(nn.Module):
def __init__(
self,
dim,
num_heads,
mlp_ratio=4.0,
qkv_bias=False,
qk_scale=None,
drop=0.0,
attn_drop=0.0,
drop_path=0.0,
act_layer=nn.GELU,
wide_silu=True,
norm_layer=nn.LayerNorm,
use_sdpa=True,
is_causal=False,
grid_size=16,
use_rope=False,
n_registers=0,
has_cls_first=False,
interpolate_rope=False,
patch_size=16,
**kwargs,
):
super().__init__()
self.norm1 = norm_layer(dim)
self.use_rope = use_rope
if use_rope:
self.attn = RoPEAttention(
dim,
num_heads=num_heads,
qkv_bias=qkv_bias,
qk_scale=qk_scale,
attn_drop=attn_drop,
use_sdpa=use_sdpa,
is_causal=is_causal,
grid_size=grid_size,
proj_drop=drop,
n_registers=n_registers,
has_cls_first=has_cls_first,
interpolate_rope=interpolate_rope,
patch_size=patch_size,
)
else:
self.attn = Attention(
dim,
num_heads=num_heads,
qkv_bias=qkv_bias,
qk_scale=qk_scale,
attn_drop=attn_drop,
use_sdpa=use_sdpa,
is_causal=is_causal,
proj_drop=drop,
)
self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
self.norm2 = norm_layer(dim)
mlp_hidden_dim = int(dim * mlp_ratio)
if act_layer is nn.SiLU:
self.mlp = SwiGLUFFN(
in_features=dim,
hidden_features=mlp_hidden_dim,
act_layer=act_layer,
wide_silu=wide_silu,
drop=drop,
)
else:
self.mlp = MLP(
in_features=dim,
hidden_features=mlp_hidden_dim,
act_layer=act_layer,
drop=drop,
)
def forward(
self,
x,
mask=None,
T=None,
H_patches=None,
W_patches=None,
return_attn=False,
mode="video",
):
if self.use_rope:
y, attn = self.attn(
self.norm1(x),
mask=mask,
T=T,
H_patches=H_patches,
W_patches=W_patches,
return_attn=return_attn,
)
else:
y = self.attn(self.norm1(x))
attn = None
x = x + self.drop_path(y)
x = x + self.drop_path(self.mlp(self.norm2(x)))
if return_attn:
return x, attn
else:
return x, None
class Lambda_LinearWarmupHold:
def __init__(self, lambda_value: float, start_iter: int = 15_000, end_iter: int = 30_000):
assert end_iter > start_iter, "end_iter must be > start_iter"
self.lambda_value = float(lambda_value)
self.start = int(start_iter)
self.end = int(end_iter)
self.span = self.end - self.start
def value(self, global_iter: int) -> float:
if global_iter < self.start:
return 0.0
if global_iter >= self.end:
return self.lambda_value
alpha = (global_iter - self.start) / self.span
return self.lambda_value * alpha
from functools import partial # noqa: E402
class VisionTransformer(nn.Module):
def __init__(
self,
img_size=(224, 224),
patch_size=16,
num_frames=1,
tubelet_size=2,
in_chans=3,
embed_dim=768,
depth=12,
num_heads=12,
mlp_ratio=4.0,
qkv_bias=True,
qk_scale=None,
drop_rate=0.0,
attn_drop_rate=0.0,
drop_path_rate=0.0,
norm_layer=nn.LayerNorm,
init_std=0.02,
out_layers=None,
uniform_power=False,
use_silu=False,
wide_silu=True,
use_sdpa=True,
use_activation_checkpointing=False,
is_causal=False,
use_rope=False,
init_type: str = "default",
handle_nonsquare_inputs=True,
img_temporal_dim_size=None,
n_registers=0,
has_cls_first=False,
interpolate_rope=False,
modality_embedding=True,
n_output_distillation=4,
**kwargs,
):
super().__init__()
self.num_features = self.embed_dim = embed_dim
self.num_heads = num_heads
self.out_layers = out_layers
self.init_type = init_type
self.handle_nonsquare_inputs = handle_nonsquare_inputs
self.img_temporal_dim_size = img_temporal_dim_size
if type(img_size) is int:
img_size = (img_size, img_size)
self.img_height, self.img_width = img_size
self.patch_size = patch_size
self.num_frames = num_frames
self.tubelet_size = tubelet_size
self.is_video = num_frames > 1
self.use_activation_checkpointing = use_activation_checkpointing
dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)]
if self.is_video:
self.patch_embed = PatchEmbed3D(
patch_size=patch_size,
tubelet_size=tubelet_size,
in_chans=in_chans,
embed_dim=embed_dim,
)
self.num_patches = (num_frames // tubelet_size) * (img_size[0] // patch_size) * (img_size[1] // patch_size)
else:
self.patch_embed = PatchEmbed3D(
patch_size=patch_size,
tubelet_size=1,
in_chans=in_chans,
embed_dim=embed_dim,
)
self.num_patches = (img_size[0] // patch_size) * (img_size[1] // patch_size)
if self.img_temporal_dim_size is not None:
if not isinstance(self.img_temporal_dim_size, int):
raise ValueError(f"img_temporal_dim_size must be an int, got {self.img_temporal_dim_size}")
self.patch_embed_img = PatchEmbed3D(
patch_size=patch_size,
tubelet_size=1,
in_chans=in_chans,
embed_dim=embed_dim,
)
else:
self.patch_embed_img = None
self.uniform_power = uniform_power
self.use_rope = use_rope
self.blocks = nn.ModuleList(
[
Block(
use_rope=use_rope,
grid_size=img_size[0] // patch_size,
grid_depth=num_frames // tubelet_size,
dim=embed_dim,
num_heads=num_heads,
mlp_ratio=mlp_ratio,
use_sdpa=use_sdpa,
is_causal=is_causal,
qkv_bias=qkv_bias,
qk_scale=qk_scale,
drop=drop_rate,
act_layer=nn.SiLU if use_silu else nn.GELU,
wide_silu=wide_silu,
attn_drop=attn_drop_rate,
drop_path=dpr[i],
norm_layer=norm_layer,
n_registers=n_registers,
has_cls_first=has_cls_first,
interpolate_rope=interpolate_rope,
patch_size=patch_size,
)
for i in range(depth)
]
)
self.attn_out = False
self.init_std = init_std
self.apply(self._init_weights)
self._rescale_blocks()
if depth == 12:
self.hierarchical_layers = [2, 5, 8, 11]
if n_output_distillation == 4:
self.out_layers_distillation = [2, 5, 8, 11]
elif n_output_distillation == 1:
self.out_layers_distillation = [11]
elif depth == 24:
self.hierarchical_layers = [5, 11, 17, 23]
if n_output_distillation == 4:
self.out_layers_distillation = [5, 11, 17, 23]
elif n_output_distillation == 1:
self.out_layers_distillation = [23]
elif depth == 40:
self.hierarchical_layers = [9, 19, 29, 39]
if n_output_distillation == 4:
self.out_layers_distillation = [9, 19, 29, 39]
elif n_output_distillation == 1:
self.out_layers_distillation = [39]
elif depth == 48:
self.hierarchical_layers = [11, 23, 37, 47]
if n_output_distillation == 4:
self.out_layers_distillation = [11, 23, 37, 47]
elif n_output_distillation == 1:
self.out_layers_distillation = [47]
else:
print("Check the code! ;)")
self.norms_block = nn.ModuleList([norm_layer(embed_dim) for _ in range(len(self.hierarchical_layers))])
self.cls_token = None
self.return_hierarchical = False
self.modality_embedding = False
if modality_embedding:
self.img_mod_embed = nn.Parameter(torch.zeros(1, 1, embed_dim))
self.video_mod_embed = nn.Parameter(torch.zeros(1, 1, embed_dim))
nn.init.normal_(self.img_mod_embed, std=1e-6)
nn.init.normal_(self.video_mod_embed, std=1e-6)
self.modality_embedding = True
def _init_weights(self, m):
if isinstance(m, nn.LayerNorm):
nn.init.constant_(m.bias, 0)
nn.init.constant_(m.weight, 1.0)
return
if self.init_type == "default":
if isinstance(m, nn.Linear):
trunc_normal_(m.weight, std=self.init_std)
if isinstance(m, nn.Linear) and m.bias is not None:
nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.Conv2d):
trunc_normal_(m.weight, std=self.init_std)
if m.bias is not None:
nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.Conv3d):
trunc_normal_(m.weight, std=self.init_std)
if m.bias is not None:
nn.init.constant_(m.bias, 0)
elif self.init_type == "xavier_uniform":
if isinstance(m, nn.Linear) or isinstance(m, nn.Conv2d) or isinstance(m, nn.Conv3d):
nn.init.xavier_uniform_(m.weight)
if m.bias is not None:
nn.init.constant_(m.bias, 0)
elif self.init_type == "xavier_normal":
if isinstance(m, nn.Linear) or isinstance(m, nn.Conv2d) or isinstance(m, nn.Conv3d):
nn.init.xavier_normal_(m.weight)
if m.bias is not None:
nn.init.constant_(m.bias, 0)
else:
raise ValueError(f"Unknown init type {self.init_type}")
def _rescale_blocks(self):
def rescale(param, layer_id):
param.div_(math.sqrt(2.0 * layer_id))
for layer_id, layer in enumerate(self.blocks):
rescale(layer.attn.proj.weight.data, layer_id + 1)
rescale(layer.mlp.fc2.weight.data, layer_id + 1)
def get_num_layers(self):
return len(self.blocks)
def no_weight_decay(self):
return {}
def check_temporal_dim(self, shape) -> bool:
if self.img_temporal_dim_size is not None:
if shape[2] == self.img_temporal_dim_size:
return True
return False
def forward(self, x, masks=None, training=False):
if masks is not None and not isinstance(masks, list):
masks = [masks]
if x.ndim == 4:
_, _, H, W = x.shape
T = 1
elif x.ndim == 5:
_, _, T, H, W = x.shape
if self.check_temporal_dim(x.shape):
T = T // 1
else:
T = T // self.tubelet_size
H_patches = H // self.patch_size
W_patches = W // self.patch_size
if not self.handle_nonsquare_inputs:
T = H_patches = W_patches = None
if self.check_temporal_dim(x.shape):
assert self.patch_embed_img is not None
x = self.patch_embed_img(x)
mode = "img"
if self.modality_embedding:
x += self.img_mod_embed.repeat(x.shape[0], 1, 1)
else:
x = self.patch_embed(x)
mode = "video"
if self.modality_embedding:
x += self.video_mod_embed.repeat(x.shape[0], 1, 1)
if masks is not None:
x = apply_masks(x, masks)
masks = torch.cat(masks, dim=0)
outs = []
hier = []
for i, blk in enumerate(self.blocks):
if self.use_activation_checkpointing:
x, attn = torch.utils.checkpoint.checkpoint(
blk,
x,
masks,
T=T,
H_patches=H_patches,
W_patches=W_patches,
use_reentrant=False,
return_attn=self.attn_out,
mode=mode,
)
else:
x, attn = blk(
x,
mask=masks,
T=T,
H_patches=H_patches,
W_patches=W_patches,
return_attn=self.attn_out,
mode=mode,
)
if self.out_layers is not None and i in self.out_layers:
out_idx = self.hierarchical_layers.index(i)
out_norm = self.norms_block[out_idx](x)
outs.append(out_norm)
if i in self.out_layers_distillation:
out_idx = self.hierarchical_layers.index(i)
hier.append(self.norms_block[out_idx](x))
if self.out_layers is not None:
return outs
if training or self.return_hierarchical:
hier = torch.cat(hier, dim=2)
return hier
else:
x = self.norms_block[-1](x)
return x
def vit_base(patch_size=16, **kwargs):
model = VisionTransformer(
patch_size=patch_size,
embed_dim=768,
depth=12,
num_heads=12,
mlp_ratio=4,
qkv_bias=True,
norm_layer=partial(nn.LayerNorm, eps=1e-6),
**kwargs,
)
return model
def vit_large(patch_size=16, **kwargs):
model = VisionTransformer(
patch_size=patch_size,
embed_dim=1024,
depth=24,
num_heads=16,
mlp_ratio=4,
qkv_bias=True,
norm_layer=partial(nn.LayerNorm, eps=1e-6),
**kwargs,
)
return model
def vit_huge(patch_size=16, **kwargs):
model = VisionTransformer(
patch_size=patch_size,
embed_dim=1280,
depth=32,
num_heads=16,
mlp_ratio=4,
qkv_bias=True,
norm_layer=partial(nn.LayerNorm, eps=1e-6),
**kwargs,
)
return model
def vit_giant_xformers(patch_size=16, **kwargs):
model = VisionTransformer(
patch_size=patch_size,
embed_dim=1408,
depth=40,
num_heads=22,
mlp_ratio=48 / 11,
qkv_bias=True,
norm_layer=partial(nn.LayerNorm, eps=1e-6),
**kwargs,
)
return model
def vit_gigantic_xformers(patch_size=16, **kwargs):
model = VisionTransformer(
patch_size=patch_size,
embed_dim=1664,
depth=48,
num_heads=26,
mlp_ratio=64 / 13,
qkv_bias=True,
norm_layer=partial(nn.LayerNorm, eps=1e-6),
**kwargs,
)
return model
VIT_EMBED_DIMS = {
"vit_base": 768,
"vit_large": 1024,
"vit_huge": 1280,
"vit_giant_xformers": 1408,
"vit_gigantic_xformers": 1664,
}
ARCH_MAP = {
"vjepa2_1_vit_base_384": (vit_base, 1),
"vjepa2_1_vit_large_384": (vit_large, 1),
"vjepa2_1_vit_giant_384": (vit_giant_xformers, 4),
"vjepa2_1_vit_gigantic_384": (vit_gigantic_xformers, 4),
}
class VisionEncoderOutput:
def __init__(self, last_hidden_state, hidden_states=None):
self.last_hidden_state = last_hidden_state
self.hidden_states = hidden_states
def __getitem__(self, key):
return getattr(self, key)
def get(self, key, default=None):
return getattr(self, key, default)
class VJEPA21Encoder(nn.Module):
def __init__(self, config):
super().__init__()
self.config = config
arch = getattr(config, "arch", "vjepa2_1_vit_large_384")
img_size = getattr(config, "img_size", 384)
num_frames = getattr(config, "num_frames", 80)
n_out = getattr(config, "n_output_distillation", None)
use_ckpt = getattr(config, "use_activation_checkpointing", False)
if arch not in ARCH_MAP:
raise ValueError(f"Unknown arch '{arch}'. Choose from {list(ARCH_MAP.keys())}")
factory_fn, default_n_out = ARCH_MAP[arch]
if n_out is None:
n_out = default_n_out
self.encoder = factory_fn(
img_size=(img_size, img_size),
num_frames=num_frames,
tubelet_size=2,
use_sdpa=True,
use_silu=False,
wide_silu=True,
uniform_power=False,
use_rope=True,
img_temporal_dim_size=1,
interpolate_rope=True,
n_output_distillation=n_out,
use_activation_checkpointing=use_ckpt,
)
self.use_hierarchical = getattr(config, "use_hierarchical", False)
self.encoder.return_hierarchical = self.use_hierarchical
n_levels = len(self.encoder.out_layers_distillation)
self._hidden_size = self.encoder.embed_dim * n_levels if self.use_hierarchical else self.encoder.embed_dim
ckpt_path = getattr(config, "pretrained_checkpoint", None)
if ckpt_path:
import os
if not os.path.isabs(ckpt_path):
pretrained_dir = getattr(config, "pretrained_dir", "")
ckpt_path = os.path.join(pretrained_dir, ckpt_path)
load_vjepa21_checkpoint(self, ckpt_path)
@property
def hidden_size(self):
return self._hidden_size
def gradient_checkpointing_enable(self, gradient_checkpointing_kwargs=None):
self.encoder.use_activation_checkpointing = True
def gradient_checkpointing_disable(self):
self.encoder.use_activation_checkpointing = False
def save_pretrained(self, save_directory, **kwargs):
import os
from safetensors.torch import save_file
os.makedirs(save_directory, exist_ok=True)
self.config.save_pretrained(save_directory)
save_file(self.state_dict(), os.path.join(save_directory, "model.safetensors"))
logger.info(f"VJEPA21Encoder saved to {save_directory}")
def forward(self, pixel_values, output_hidden_states=False, **kwargs):
x = pixel_values.permute(0, 2, 1, 3, 4).contiguous()
if output_hidden_states:
if self.encoder.check_temporal_dim(x.shape):
x_tok = self.encoder.patch_embed_img(x)
if self.encoder.modality_embedding:
x_tok = x_tok + self.encoder.img_mod_embed
else:
x_tok = self.encoder.patch_embed(x)
if self.encoder.modality_embedding:
x_tok = x_tok + self.encoder.video_mod_embed
_, _, T, H, W = x.shape
T_p = T // (1 if self.encoder.check_temporal_dim(x.shape) else self.encoder.tubelet_size)
H_p, W_p = H // self.encoder.patch_size, W // self.encoder.patch_size
hidden_states = [x_tok]
h = x_tok
for blk in self.encoder.blocks:
h, _ = blk(h, mask=None, T=T_p, H_patches=H_p, W_patches=W_p)
hidden_states.append(h)
if self.use_hierarchical:
hier = []
for idx in self.encoder.out_layers_distillation:
out_idx = self.encoder.hierarchical_layers.index(idx)
hier.append(self.encoder.norms_block[out_idx](hidden_states[idx]))
features = torch.cat(hier, dim=2)
else:
features = self.encoder.norms_block[-1](hidden_states[-1])
return VisionEncoderOutput(last_hidden_state=features, hidden_states=tuple(hidden_states))
features = self.encoder(x, masks=None, training=self.use_hierarchical and self.training)
return VisionEncoderOutput(last_hidden_state=features, hidden_states=None)
def load_vjepa21_checkpoint(model, checkpoint_path, strict=False):
import os
if not os.path.exists(checkpoint_path):
raise FileNotFoundError(f"V-JEPA 2.1 checkpoint not found: {checkpoint_path}")
if os.path.isdir(checkpoint_path):
st_file = os.path.join(checkpoint_path, "model.safetensors")
if not os.path.exists(st_file):
raise FileNotFoundError(f"Directory {checkpoint_path} is missing model.safetensors")
from safetensors.torch import load_file
logger.info(f"Detected VJEPA21Encoder safetensors dir: {checkpoint_path}")
state_dict = load_file(st_file)
missing, unexpected = model.load_state_dict(state_dict, strict=strict)
n_loaded = len(state_dict) - len(unexpected)
logger.info(
f"V-JEPA 2.1 safetensors loaded: {n_loaded} keys, {len(missing)} missing, {len(unexpected)} unexpected"
)
if missing:
logger.info(f" Missing (first 10): {missing[:10]}")
if unexpected:
logger.info(f" Unexpected (first 10): {unexpected[:10]}")
return model
state_dict = torch.load(checkpoint_path, map_location="cpu", weights_only=False)
if "ema_encoder" in state_dict:
logger.info("Detected V-JEPA 2.1 format (ema_encoder)")
state_dict = state_dict["ema_encoder"]
elif "target_encoder" in state_dict:
logger.info("Target Encoder only detected")
state_dict = state_dict["target_encoder"]
elif "model" in state_dict:
state_dict = state_dict["model"]
cleaned = {}
for k, v in state_dict.items():
cleaned[k.replace("module.", "").replace("backbone.", "")] = v
mapped = {f"encoder.{k}": v for k, v in cleaned.items()}
missing, unexpected = model.load_state_dict(mapped, strict=strict)
n_loaded = len(mapped) - len(unexpected)
logger.info(f"V-JEPA 2.1 loaded: {n_loaded} keys, {len(missing)} missing, {len(unexpected)} unexpected")
if missing:
logger.info(f" Missing (first 10): {missing[:10]}")
if unexpected:
logger.info(f" Unexpected (first 10): {unexpected[:10]}")
return model