aabb / finevit.py
toilaluan's picture
Update finevit.py
7018524 verified
Raw
History Blame Contribute Delete
15.7 kB
import math
from dataclasses import dataclass
from typing import Optional
import torch
import torch.nn as nn
import torch.nn.functional as F
from transformers import AutoConfig, AutoModel, PreTrainedModel
from transformers.utils import ModelOutput
from .config import FineViTConfig
class GatedMLP(nn.Module):
def __init__(
self,
input_dim: int,
hidden_dim: int,
output_dim: int,
bias: bool = True,
):
super().__init__()
self.fc_in = nn.Linear(input_dim, 2 * hidden_dim, bias=bias)
self.fc_out = nn.Linear(hidden_dim, output_dim, bias=bias)
def forward(self, x):
gate, value = self.fc_in(x).chunk(2, dim=-1)
return self.fc_out(F.silu(gate) * value)
class FourierPositionEncoder(nn.Module):
def __init__(self, feat_dim: int, hidden_size: int) -> None:
super().__init__()
if feat_dim <= 0 or feat_dim % 2 != 0:
raise ValueError("feat_dim must be a positive even integer.")
self.embed = nn.Linear(2, feat_dim // 2, bias=False)
self.proj = nn.Linear(feat_dim, hidden_size, bias=False)
self.mlp = GatedMLP(hidden_size, hidden_size * 4, hidden_size)
def forward(self, coordinates: torch.Tensor) -> torch.Tensor:
features = 2 * math.pi * self.embed(coordinates)
features = torch.cat([features.cos(), features.sin()], dim=-1)
hidden_states = self.proj(features)
return hidden_states + self.mlp(hidden_states)
class Attention(nn.Module):
def __init__(
self,
hidden_size: int,
head_dim: int,
num_attention_heads: int,
dropout: float = 0.0,
):
super().__init__()
self.head_dim = head_dim
self.num_attention_heads = num_attention_heads
self.dropout = dropout
self.q_norm = nn.RMSNorm(head_dim, eps=1e-6)
self.k_norm = nn.RMSNorm(head_dim, eps=1e-6)
self.to_qkv = nn.Linear(
hidden_size, 3 * head_dim * num_attention_heads, bias=False
)
self.to_out = nn.Linear(head_dim * num_attention_heads, hidden_size, bias=False)
def forward(self, hidden_states: torch.Tensor):
batch, seq, _ = hidden_states.shape
qkv = self.to_qkv(hidden_states).view(
batch, seq, 3, self.num_attention_heads, self.head_dim
)
q, k, v = qkv.permute(0, 2, 3, 1, 4).contiguous().unbind(dim=1)
attn = (
F.scaled_dot_product_attention(
self.q_norm(q),
self.k_norm(k),
v,
dropout_p=self.dropout if self.training else 0.0,
)
.transpose(1, 2)
.contiguous()
.view(batch, seq, -1)
)
return self.to_out(attn)
class CrossAttention(nn.Module):
def __init__(
self,
hidden_size: int,
head_dim: int,
num_attention_heads: int,
dropout: float = 0.0,
):
super().__init__()
self.head_dim = head_dim
self.num_attention_heads = num_attention_heads
self.dropout = dropout
inner_dim = head_dim * num_attention_heads
self.q_norm = nn.RMSNorm(head_dim, eps=1e-6)
self.k_norm = nn.RMSNorm(head_dim, eps=1e-6)
self.to_q = nn.Linear(hidden_size, inner_dim, bias=False)
self.to_kv = nn.Linear(hidden_size, 2 * inner_dim, bias=False)
self.to_out = nn.Linear(inner_dim, hidden_size, bias=False)
def forward(
self,
query_states: torch.Tensor,
kv_states: torch.Tensor,
return_weights: bool = False,
):
batch, q_seq, _ = query_states.shape
kv_seq = kv_states.shape[1]
q = (
self.to_q(query_states)
.view(batch, q_seq, self.num_attention_heads, self.head_dim)
.transpose(1, 2)
.contiguous()
)
kv = self.to_kv(kv_states).view(
batch, kv_seq, 2, self.num_attention_heads, self.head_dim
)
k, v = kv.permute(2, 0, 3, 1, 4).contiguous().unbind(dim=0)
q = self.q_norm(q)
k = self.k_norm(k)
if return_weights:
scale = q.shape[-1] ** -0.5
scores = (q * scale) @ k.transpose(-2, -1)
weights = scores.softmax(dim=-1)
if self.training and self.dropout > 0.0:
attn = F.dropout(weights, p=self.dropout) @ v
else:
attn = weights @ v
else:
attn = F.scaled_dot_product_attention(
q, k, v, dropout_p=self.dropout if self.training else 0.0
)
weights = None
attn = attn.transpose(1, 2).contiguous().view(batch, q_seq, -1)
out = self.to_out(attn)
return (out, weights) if return_weights else out
class TransformerBlock(nn.Module):
def __init__(
self,
hidden_size: int,
head_dim: int,
num_attention_heads: int,
mlp_hidden_dim: int,
dropout: float = 0.0,
):
super().__init__()
self.attn_norm = nn.LayerNorm(hidden_size)
self.attention = Attention(hidden_size, head_dim, num_attention_heads, dropout)
self.mlp_norm = nn.LayerNorm(hidden_size)
self.mlp = GatedMLP(hidden_size, mlp_hidden_dim, hidden_size)
def forward(self, hidden_states: torch.Tensor):
hidden_states = hidden_states + self.attention(self.attn_norm(hidden_states))
hidden_states = hidden_states + self.mlp(self.mlp_norm(hidden_states))
return hidden_states
class CrossAttentionBlock(nn.Module):
def __init__(
self,
hidden_size: int,
head_dim: int,
num_attention_heads: int,
mlp_hidden_dim: int,
fused_kv: bool = False,
dropout: float = 0.0,
):
super().__init__()
self.fused_kv = fused_kv
self.q_norm = nn.LayerNorm(hidden_size)
self.kv_norm = nn.LayerNorm(hidden_size)
self.attention = CrossAttention(
hidden_size, head_dim, num_attention_heads, dropout
)
self.mlp_norm = nn.LayerNorm(hidden_size)
self.mlp = GatedMLP(hidden_size, mlp_hidden_dim, hidden_size)
def forward(
self,
query_states: torch.Tensor,
kv_states: torch.Tensor,
return_weights: bool = False,
):
kv_input = (
torch.cat([query_states, kv_states], dim=1) if self.fused_kv else kv_states
)
attn_out = self.attention(
self.q_norm(query_states),
self.kv_norm(kv_input),
return_weights=return_weights,
)
if return_weights:
attn_out, weights = attn_out
query_states = query_states + attn_out
query_states = query_states + self.mlp(self.mlp_norm(query_states))
return (query_states, weights) if return_weights else query_states
def _run_layer(layer, *inputs, training: bool):
if training:
return torch.utils.checkpoint.checkpoint(layer, *inputs, use_reentrant=False)
return layer(*inputs)
class LatentEncoder(nn.Module):
def __init__(
self,
num_latents: int,
num_layers: int,
hidden_size: int,
head_dim: int,
num_attention_heads: int,
mlp_hidden_dim: int,
dropout: float = 0.0,
):
super().__init__()
self.latent_tokens = nn.Parameter(torch.zeros(num_latents, hidden_size))
self.pos_embed = nn.Parameter(torch.zeros(num_latents, hidden_size))
nn.init.trunc_normal_(self.latent_tokens, std=0.02)
nn.init.trunc_normal_(self.pos_embed, std=0.02)
self.cross_block = CrossAttentionBlock(
hidden_size,
head_dim,
num_attention_heads,
mlp_hidden_dim,
fused_kv=False,
dropout=dropout,
)
self.layers = nn.ModuleList(
[
TransformerBlock(
hidden_size, head_dim, num_attention_heads, mlp_hidden_dim, dropout
)
for _ in range(num_layers)
]
)
def forward(self, patch_tokens: torch.Tensor, return_weights: bool = False):
batch = patch_tokens.shape[0]
latents = self.latent_tokens.expand(batch, -1, -1)
out = self.cross_block(latents, patch_tokens, return_weights=return_weights)
if return_weights:
latents, weights = out
else:
latents, weights = out, None
latents = latents + self.pos_embed
for layer in self.layers:
latents = _run_layer(layer, latents, training=self.training)
return (latents, weights) if return_weights else latents
class PatchDecoder(nn.Module):
def __init__(
self,
num_layers: int,
hidden_size: int,
head_dim: int,
num_attention_heads: int,
mlp_hidden_dim: int,
pos_feat_dim: int,
dropout: float = 0.0,
):
super().__init__()
self.position_encoder = FourierPositionEncoder(pos_feat_dim, hidden_size)
self.layers = nn.ModuleList(
[
CrossAttentionBlock(
hidden_size,
head_dim,
num_attention_heads,
mlp_hidden_dim,
fused_kv=True,
dropout=dropout,
)
for _ in range(num_layers)
]
)
self.out_norm = nn.LayerNorm(hidden_size, elementwise_affine=False)
def _coordinates(
self, seq_length: int, device: torch.device, dtype: torch.dtype
) -> torch.Tensor:
side = math.isqrt(seq_length)
if side * side != seq_length:
raise ValueError(
f"PatchDecoder expects a square patch sequence, got length {seq_length}"
)
positions = torch.linspace(-1.0, 1.0, side, device=device, dtype=dtype)
y, x = torch.meshgrid(positions, positions, indexing="ij")
return torch.stack([x, y], dim=-1).view(1, seq_length, 2)
def forward(self, latents: torch.Tensor, target_seq_length: int) -> torch.Tensor:
coordinates = self._coordinates(
target_seq_length, latents.device, latents.dtype
)
hidden_states = self.position_encoder(coordinates).expand(
latents.shape[0], -1, -1
)
for layer in self.layers:
hidden_states = _run_layer(
layer, hidden_states, latents, training=self.training
)
return self.out_norm(hidden_states)
@dataclass
class FineViTModelOutput(ModelOutput):
decoded_patches: Optional[torch.FloatTensor] = None
latents: Optional[torch.FloatTensor] = None
latent_attention_weights: Optional[torch.FloatTensor] = None
cls_register_hidden_states: Optional[torch.FloatTensor] = None
patch_tokens: Optional[torch.FloatTensor] = None
uncertainty: Optional[torch.FloatTensor] = None
class FineViTModel(PreTrainedModel):
config_class = FineViTConfig
main_input_name = "pixel_values"
def __init__(self, config: FineViTConfig):
super().__init__(config)
self.encoder = self._build_encoder(config)
if hasattr(self.encoder, "embeddings"):
self.encoder.embeddings.mask_token = None
self.num_register_tokens = int(
getattr(self.encoder.config, "num_register_tokens", 0)
)
hidden_size = int(self.encoder.config.hidden_size)
self.uncertainty_head = GatedMLP(hidden_size, hidden_size * 3, 1)
self.latent_encoder = LatentEncoder(
num_latents=config.num_latents,
num_layers=config.latent_encoder_num_layers,
hidden_size=hidden_size,
head_dim=config.latent_encoder_head_dim,
num_attention_heads=config.latent_encoder_num_attention_heads,
mlp_hidden_dim=config.latent_encoder_mlp_hidden_dim,
dropout=config.dropout,
)
self.patch_decoder = PatchDecoder(
num_layers=config.patch_decoder_num_layers,
hidden_size=hidden_size,
head_dim=config.patch_decoder_head_dim,
num_attention_heads=config.patch_decoder_num_attention_heads,
mlp_hidden_dim=config.patch_decoder_mlp_hidden_dim,
pos_feat_dim=hidden_size,
dropout=config.dropout,
)
self.post_init()
def _build_encoder(self, config: FineViTConfig):
if getattr(config, "init_backbone_from_pretrained", True):
return AutoModel.from_pretrained(config.backbone_model_name)
if config.backbone_config is not None:
backbone_config_dict = dict(config.backbone_config)
model_type = backbone_config_dict.pop("model_type", None)
if model_type is not None:
backbone_config = AutoConfig.for_model(
model_type, **backbone_config_dict
)
return AutoModel.from_config(backbone_config)
return AutoModel.from_pretrained(config.backbone_model_name)
def post_init(self) -> None:
for module in (
self.uncertainty_head,
self.latent_encoder,
self.patch_decoder,
):
module.apply(self._init_weights)
def _init_weights(self, module: nn.Module) -> None:
if isinstance(module, nn.Linear):
nn.init.trunc_normal_(module.weight, std=self.config.initializer_range)
if module.bias is not None:
nn.init.zeros_(module.bias)
elif isinstance(module, (nn.LayerNorm, nn.RMSNorm)):
if module.weight is not None:
nn.init.ones_(module.weight)
bias = getattr(module, "bias", None)
if bias is not None:
nn.init.zeros_(bias)
def forward(
self,
pixel_values: torch.Tensor,
return_dict: Optional[bool] = None,
only_latents: bool = False,
latents: Optional[torch.Tensor] = None,
):
return_dict = (
return_dict if return_dict is not None else self.config.return_dict
)
patch_offset = 1 + self.num_register_tokens
hidden_states = self.encoder(pixel_values).last_hidden_state
patch_tokens = hidden_states[:, patch_offset:]
cls_register_hidden_states = hidden_states[:, :patch_offset]
if latents is None:
latents, latent_attention_weights = self.latent_encoder(
hidden_states, return_weights=True
)
latent_attention_weights = latent_attention_weights[
:, :, :, -patch_tokens.shape[1] :
]
else:
latent_attention_weights = None
if only_latents:
return latents
decoded_patches = self.patch_decoder(
latents, target_seq_length=patch_tokens.shape[1]
)
uncertainty = self.uncertainty_head(patch_tokens)
if not return_dict:
return (
decoded_patches,
latents,
latent_attention_weights,
cls_register_hidden_states,
patch_tokens,
uncertainty,
)
return FineViTModelOutput(
decoded_patches=decoded_patches,
latents=latents,
latent_attention_weights=latent_attention_weights,
cls_register_hidden_states=cls_register_hidden_states,
patch_tokens=patch_tokens,
uncertainty=uncertainty,
)
FineViTModel.register_for_auto_class("AutoModel")