1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129 |
- # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
- import copy
- import math
- from functools import partial
- from typing import Any, Optional, Tuple, Type, Union
- import numpy as np
- import torch
- import torch.nn.functional as F
- from torch import Tensor, nn
- from ultralytics.nn.modules import MLP, LayerNorm2d, MLPBlock
- from .transformer import Attention, TwoWayAttentionBlock, TwoWayTransformer
- from .utils import add_decomposed_rel_pos, apply_rotary_enc, compute_axial_cis, window_partition, window_unpartition
- class DropPath(nn.Module):
- """
- Implements stochastic depth regularization for neural networks during training.
- Attributes:
- drop_prob (float): Probability of dropping a path during training.
- scale_by_keep (bool): Whether to scale the output by the keep probability.
- Methods:
- forward: Applies stochastic depth to input tensor during training, with optional scaling.
- Examples:
- >>> drop_path = DropPath(drop_prob=0.2, scale_by_keep=True)
- >>> x = torch.randn(32, 64, 224, 224)
- >>> output = drop_path(x)
- """
- def __init__(self, drop_prob=0.0, scale_by_keep=True):
- """Initialize DropPath module for stochastic depth regularization during training."""
- super().__init__()
- self.drop_prob = drop_prob
- self.scale_by_keep = scale_by_keep
- def forward(self, x):
- """Applies stochastic depth to input tensor during training, with optional scaling."""
- if self.drop_prob == 0.0 or not self.training:
- return x
- keep_prob = 1 - self.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 self.scale_by_keep:
- random_tensor.div_(keep_prob)
- return x * random_tensor
- class MaskDownSampler(nn.Module):
- """
- A mask downsampling and embedding module for efficient processing of input masks.
- This class implements a mask downsampler that progressively reduces the spatial dimensions of input masks
- while expanding their channel dimensions using convolutional layers, layer normalization, and activation
- functions.
- Attributes:
- encoder (nn.Sequential): A sequential container of convolutional layers, layer normalization, and
- activation functions for downsampling and embedding masks.
- Methods:
- forward: Downsamples and encodes input mask to embed_dim channels.
- Examples:
- >>> mask_downsampler = MaskDownSampler(embed_dim=256, kernel_size=4, stride=4, padding=0, total_stride=16)
- >>> input_mask = torch.randn(1, 1, 256, 256)
- >>> output = mask_downsampler(input_mask)
- >>> print(output.shape)
- torch.Size([1, 256, 16, 16])
- """
- def __init__(
- self,
- embed_dim=256,
- kernel_size=4,
- stride=4,
- padding=0,
- total_stride=16,
- activation=nn.GELU,
- ):
- """Initializes a mask downsampler module for progressive downsampling and channel expansion."""
- super().__init__()
- num_layers = int(math.log2(total_stride) // math.log2(stride))
- assert stride**num_layers == total_stride
- self.encoder = nn.Sequential()
- mask_in_chans, mask_out_chans = 1, 1
- for _ in range(num_layers):
- mask_out_chans = mask_in_chans * (stride**2)
- self.encoder.append(
- nn.Conv2d(
- mask_in_chans,
- mask_out_chans,
- kernel_size=kernel_size,
- stride=stride,
- padding=padding,
- )
- )
- self.encoder.append(LayerNorm2d(mask_out_chans))
- self.encoder.append(activation())
- mask_in_chans = mask_out_chans
- self.encoder.append(nn.Conv2d(mask_out_chans, embed_dim, kernel_size=1))
- def forward(self, x):
- """Downsamples and encodes input mask to embed_dim channels using convolutional layers and LayerNorm2d."""
- return self.encoder(x)
- class CXBlock(nn.Module):
- """
- ConvNeXt Block for efficient feature extraction in convolutional neural networks.
- This block implements a modified version of the ConvNeXt architecture, offering improved performance and
- flexibility in feature extraction.
- Attributes:
- dwconv (nn.Conv2d): Depthwise or standard 2D convolution layer.
- norm (LayerNorm2d): Layer normalization applied to channels.
- pwconv1 (nn.Linear): First pointwise convolution implemented as a linear layer.
- act (nn.GELU): GELU activation function.
- pwconv2 (nn.Linear): Second pointwise convolution implemented as a linear layer.
- gamma (nn.Parameter | None): Learnable scale parameter for layer scaling.
- drop_path (nn.Module): DropPath layer for stochastic depth regularization.
- Methods:
- forward: Processes the input tensor through the ConvNeXt block.
- Examples:
- >>> import torch
- >>> x = torch.randn(1, 64, 56, 56)
- >>> block = CXBlock(dim=64, kernel_size=7, padding=3)
- >>> output = block(x)
- >>> print(output.shape)
- torch.Size([1, 64, 56, 56])
- """
- def __init__(
- self,
- dim,
- kernel_size=7,
- padding=3,
- drop_path=0.0,
- layer_scale_init_value=1e-6,
- use_dwconv=True,
- ):
- """
- Initialize a ConvNeXt Block for efficient feature extraction in convolutional neural networks.
- This block implements a modified version of the ConvNeXt architecture, offering improved performance and
- flexibility in feature extraction.
- Args:
- dim (int): Number of input channels.
- kernel_size (int): Size of the convolutional kernel.
- padding (int): Padding size for the convolution.
- drop_path (float): Stochastic depth rate.
- layer_scale_init_value (float): Initial value for Layer Scale.
- use_dwconv (bool): Whether to use depthwise convolution.
- Examples:
- >>> block = CXBlock(dim=64, kernel_size=7, padding=3)
- >>> x = torch.randn(1, 64, 32, 32)
- >>> output = block(x)
- >>> print(output.shape)
- torch.Size([1, 64, 32, 32])
- """
- super().__init__()
- self.dwconv = nn.Conv2d(
- dim,
- dim,
- kernel_size=kernel_size,
- padding=padding,
- groups=dim if use_dwconv else 1,
- ) # depthwise conv
- self.norm = LayerNorm2d(dim, eps=1e-6)
- self.pwconv1 = nn.Linear(dim, 4 * dim) # pointwise/1x1 convs, implemented with linear layers
- self.act = nn.GELU()
- self.pwconv2 = nn.Linear(4 * dim, dim)
- self.gamma = (
- nn.Parameter(layer_scale_init_value * torch.ones(dim), requires_grad=True)
- if layer_scale_init_value > 0
- else None
- )
- self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
- def forward(self, x):
- """Applies ConvNeXt block operations to input tensor, including convolutions and residual connection."""
- input = x
- x = self.dwconv(x)
- x = self.norm(x)
- x = x.permute(0, 2, 3, 1) # (N, C, H, W) -> (N, H, W, C)
- x = self.pwconv1(x)
- x = self.act(x)
- x = self.pwconv2(x)
- if self.gamma is not None:
- x = self.gamma * x
- x = x.permute(0, 3, 1, 2) # (N, H, W, C) -> (N, C, H, W)
- x = input + self.drop_path(x)
- return x
- class Fuser(nn.Module):
- """
- A module for fusing features through multiple layers of a neural network.
- This class applies a series of identical layers to an input tensor, optionally projecting the input first.
- Attributes:
- proj (nn.Module): An optional input projection layer. Identity if no projection is needed.
- layers (nn.ModuleList): A list of identical layers to be applied sequentially.
- Methods:
- forward: Applies the fuser to an input tensor.
- Examples:
- >>> layer = CXBlock(dim=256)
- >>> fuser = Fuser(layer, num_layers=3, dim=256, input_projection=True)
- >>> x = torch.randn(1, 256, 32, 32)
- >>> output = fuser(x)
- >>> print(output.shape)
- torch.Size([1, 256, 32, 32])
- """
- def __init__(self, layer, num_layers, dim=None, input_projection=False):
- """
- Initializes the Fuser module for feature fusion through multiple layers.
- This module creates a sequence of identical layers and optionally applies an input projection.
- Args:
- layer (nn.Module): The layer to be replicated in the fuser.
- num_layers (int): The number of times to replicate the layer.
- dim (int | None): The dimension for input projection, if used.
- input_projection (bool): Whether to use input projection.
- Examples:
- >>> layer = nn.Linear(64, 64)
- >>> fuser = Fuser(layer, num_layers=3, dim=64, input_projection=True)
- >>> input_tensor = torch.randn(1, 64)
- >>> output = fuser(input_tensor)
- """
- super().__init__()
- self.proj = nn.Identity()
- self.layers = nn.ModuleList([copy.deepcopy(layer) for _ in range(num_layers)])
- if input_projection:
- assert dim is not None
- self.proj = nn.Conv2d(dim, dim, kernel_size=1)
- def forward(self, x):
- """Applies a series of layers to the input tensor, optionally projecting it first."""
- x = self.proj(x)
- for layer in self.layers:
- x = layer(x)
- return x
- class SAM2TwoWayAttentionBlock(TwoWayAttentionBlock):
- """
- A two-way attention block for performing self-attention and cross-attention in both directions.
- This block extends the TwoWayAttentionBlock and consists of four main components: self-attention on
- sparse inputs, cross-attention from sparse to dense inputs, an MLP block on sparse inputs, and
- cross-attention from dense to sparse inputs.
- Attributes:
- self_attn (Attention): Self-attention layer for queries.
- norm1 (nn.LayerNorm): Layer normalization after the first attention block.
- cross_attn_token_to_image (Attention): Cross-attention layer from queries to keys.
- norm2 (nn.LayerNorm): Layer normalization after the second attention block.
- mlp (MLP): MLP block for transforming query embeddings.
- norm3 (nn.LayerNorm): Layer normalization after the MLP block.
- norm4 (nn.LayerNorm): Layer normalization after the third attention block.
- cross_attn_image_to_token (Attention): Cross-attention layer from keys to queries.
- skip_first_layer_pe (bool): Flag to skip positional encoding in the first layer.
- Methods:
- forward: Processes input through the attention blocks and MLP.
- Examples:
- >>> block = SAM2TwoWayAttentionBlock(embedding_dim=256, num_heads=8)
- >>> sparse_input = torch.randn(1, 100, 256)
- >>> dense_input = torch.randn(1, 256, 16, 16)
- >>> sparse_output, dense_output = block(sparse_input, dense_input)
- """
- def __init__(
- self,
- embedding_dim: int,
- num_heads: int,
- mlp_dim: int = 2048,
- activation: Type[nn.Module] = nn.ReLU,
- attention_downsample_rate: int = 2,
- skip_first_layer_pe: bool = False,
- ) -> None:
- """
- Initializes a SAM2TwoWayAttentionBlock for performing self-attention and cross-attention in two directions.
- This block extends the TwoWayAttentionBlock and consists of four main components: self-attention on sparse
- inputs, cross-attention from sparse to dense inputs, an MLP block on sparse inputs, and cross-attention
- from dense to sparse inputs.
- Args:
- embedding_dim (int): The channel dimension of the embeddings.
- num_heads (int): The number of heads in the attention layers.
- mlp_dim (int): The hidden dimension of the MLP block.
- activation (Type[nn.Module]): The activation function of the MLP block.
- attention_downsample_rate (int): The downsample rate for attention computations.
- skip_first_layer_pe (bool): Whether to skip the positional encoding in the first layer.
- Examples:
- >>> block = SAM2TwoWayAttentionBlock(embedding_dim=256, num_heads=8, mlp_dim=2048)
- >>> sparse_inputs = torch.randn(1, 100, 256)
- >>> dense_inputs = torch.randn(1, 256, 32, 32)
- >>> sparse_outputs, dense_outputs = block(sparse_inputs, dense_inputs)
- """
- super().__init__(embedding_dim, num_heads, mlp_dim, activation, attention_downsample_rate, skip_first_layer_pe)
- self.mlp = MLP(embedding_dim, mlp_dim, embedding_dim, num_layers=2, act=activation)
- class SAM2TwoWayTransformer(TwoWayTransformer):
- """
- A Two-Way Transformer module for simultaneous attention to image and query points.
- This class extends the TwoWayTransformer, implementing a specialized transformer decoder that attends to an
- input image using queries with supplied positional embeddings. It is particularly useful for tasks like
- object detection, image segmentation, and point cloud processing.
- Attributes:
- depth (int): Number of layers in the transformer.
- embedding_dim (int): Channel dimension for input embeddings.
- num_heads (int): Number of heads for multihead attention.
- mlp_dim (int): Internal channel dimension for the MLP block.
- layers (nn.ModuleList): List of SAM2TwoWayAttentionBlock layers comprising the transformer.
- final_attn_token_to_image (Attention): Final attention layer from queries to image.
- norm_final_attn (nn.LayerNorm): Layer normalization applied to final queries.
- Methods:
- forward: Processes input image embeddings and query embeddings through the transformer.
- Examples:
- >>> transformer = SAM2TwoWayTransformer(depth=5, embedding_dim=256, num_heads=8, mlp_dim=2048)
- >>> image_embedding = torch.randn(1, 256, 64, 64)
- >>> query_embedding = torch.randn(1, 100, 256)
- >>> output = transformer(image_embedding, query_embedding)
- >>> print(output[0].shape, output[1].shape)
- torch.Size([1, 100, 256]) torch.Size([1, 256, 64, 64])
- """
- def __init__(
- self,
- depth: int,
- embedding_dim: int,
- num_heads: int,
- mlp_dim: int,
- activation: Type[nn.Module] = nn.ReLU,
- attention_downsample_rate: int = 2,
- ) -> None:
- """
- Initializes a SAM2TwoWayTransformer instance.
- This transformer decoder attends to an input image using queries with supplied positional embeddings.
- It is designed for tasks like object detection, image segmentation, and point cloud processing.
- Args:
- depth (int): Number of layers in the transformer.
- embedding_dim (int): Channel dimension for the input embeddings.
- num_heads (int): Number of heads for multihead attention. Must divide embedding_dim.
- mlp_dim (int): Channel dimension internal to the MLP block.
- activation (Type[nn.Module]): Activation function to use in the MLP block.
- attention_downsample_rate (int): Downsampling rate for attention computations.
- Examples:
- >>> transformer = SAM2TwoWayTransformer(depth=5, embedding_dim=256, num_heads=8, mlp_dim=2048)
- >>> transformer
- SAM2TwoWayTransformer(
- (layers): ModuleList(
- (0-4): 5 x SAM2TwoWayAttentionBlock(...)
- )
- (final_attn_token_to_image): Attention(...)
- (norm_final_attn): LayerNorm(...)
- )
- """
- super().__init__(depth, embedding_dim, num_heads, mlp_dim, activation, attention_downsample_rate)
- self.layers = nn.ModuleList()
- for i in range(depth):
- self.layers.append(
- SAM2TwoWayAttentionBlock(
- embedding_dim=embedding_dim,
- num_heads=num_heads,
- mlp_dim=mlp_dim,
- activation=activation,
- attention_downsample_rate=attention_downsample_rate,
- skip_first_layer_pe=(i == 0),
- )
- )
- class RoPEAttention(Attention):
- """
- Implements rotary position encoding for attention mechanisms in transformer architectures.
- This class extends the base Attention class by incorporating Rotary Position Encoding (RoPE) to enhance
- the positional awareness of the attention mechanism.
- Attributes:
- compute_cis (Callable): Function to compute axial complex numbers for rotary encoding.
- freqs_cis (Tensor): Precomputed frequency tensor for rotary encoding.
- rope_k_repeat (bool): Flag to repeat query RoPE to match key length for cross-attention to memories.
- Methods:
- forward: Applies rotary position encoding and computes attention between query, key, and value tensors.
- Examples:
- >>> rope_attn = RoPEAttention(embedding_dim=256, num_heads=8, rope_theta=10000.0, feat_sizes=(32, 32))
- >>> q = torch.randn(1, 1024, 256)
- >>> k = torch.randn(1, 1024, 256)
- >>> v = torch.randn(1, 1024, 256)
- >>> output = rope_attn(q, k, v)
- >>> print(output.shape)
- torch.Size([1, 1024, 256])
- """
- def __init__(
- self,
- *args,
- rope_theta=10000.0,
- rope_k_repeat=False,
- feat_sizes=(32, 32), # [w, h] for stride 16 feats at 512 resolution
- **kwargs,
- ):
- """Initializes RoPEAttention with rotary position encoding for enhanced positional awareness."""
- super().__init__(*args, **kwargs)
- self.compute_cis = partial(compute_axial_cis, dim=self.internal_dim // self.num_heads, theta=rope_theta)
- freqs_cis = self.compute_cis(end_x=feat_sizes[0], end_y=feat_sizes[1])
- self.freqs_cis = freqs_cis
- self.rope_k_repeat = rope_k_repeat # repeat q rope to match k length, needed for cross-attention to memories
- def forward(self, q: Tensor, k: Tensor, v: Tensor, num_k_exclude_rope: int = 0) -> Tensor:
- """Applies rotary position encoding and computes attention between query, key, and value tensors."""
- q = self.q_proj(q)
- k = self.k_proj(k)
- v = self.v_proj(v)
- # Separate into heads
- q = self._separate_heads(q, self.num_heads)
- k = self._separate_heads(k, self.num_heads)
- v = self._separate_heads(v, self.num_heads)
- # Apply rotary position encoding
- w = h = math.sqrt(q.shape[-2])
- self.freqs_cis = self.freqs_cis.to(q.device)
- if self.freqs_cis.shape[0] != q.shape[-2]:
- self.freqs_cis = self.compute_cis(end_x=w, end_y=h).to(q.device)
- if q.shape[-2] != k.shape[-2]:
- assert self.rope_k_repeat
- num_k_rope = k.size(-2) - num_k_exclude_rope
- q, k[:, :, :num_k_rope] = apply_rotary_enc(
- q,
- k[:, :, :num_k_rope],
- freqs_cis=self.freqs_cis,
- repeat_freqs_k=self.rope_k_repeat,
- )
- # Attention
- _, _, _, c_per_head = q.shape
- attn = q @ k.permute(0, 1, 3, 2) # B x N_heads x N_tokens x N_tokens
- attn = attn / math.sqrt(c_per_head)
- attn = torch.softmax(attn, dim=-1)
- # Get output
- out = attn @ v
- out = self._recombine_heads(out)
- out = self.out_proj(out)
- return out
- def do_pool(x: torch.Tensor, pool: nn.Module, norm: nn.Module = None) -> torch.Tensor:
- """Applies pooling and optional normalization to a tensor, handling spatial dimension permutations."""
- if pool is None:
- return x
- # (B, H, W, C) -> (B, C, H, W)
- x = x.permute(0, 3, 1, 2)
- x = pool(x)
- # (B, C, H', W') -> (B, H', W', C)
- x = x.permute(0, 2, 3, 1)
- if norm:
- x = norm(x)
- return x
- class MultiScaleAttention(nn.Module):
- """
- Implements multiscale self-attention with optional query pooling for efficient feature extraction.
- This class provides a flexible implementation of multiscale attention, allowing for optional
- downsampling of query features through pooling. It's designed to enhance the model's ability to
- capture multiscale information in visual tasks.
- Attributes:
- dim (int): Input dimension of the feature map.
- dim_out (int): Output dimension of the attention module.
- num_heads (int): Number of attention heads.
- scale (float): Scaling factor for dot-product attention.
- q_pool (nn.Module | None): Optional pooling module for query features.
- qkv (nn.Linear): Linear projection for query, key, and value.
- proj (nn.Linear): Output projection.
- Methods:
- forward: Applies multiscale attention to the input tensor.
- Examples:
- >>> import torch
- >>> from torch import nn
- >>> x = torch.randn(1, 64, 64, 256)
- >>> msa = MultiScaleAttention(dim=256, dim_out=256, num_heads=8)
- >>> output = msa(x)
- >>> print(output.shape)
- torch.Size([1, 64, 64, 256])
- """
- def __init__(
- self,
- dim: int,
- dim_out: int,
- num_heads: int,
- q_pool: nn.Module = None,
- ):
- """Initializes multiscale attention with optional query pooling for efficient feature extraction."""
- super().__init__()
- self.dim = dim
- self.dim_out = dim_out
- self.num_heads = num_heads
- head_dim = dim_out // num_heads
- self.scale = head_dim**-0.5
- self.q_pool = q_pool
- self.qkv = nn.Linear(dim, dim_out * 3)
- self.proj = nn.Linear(dim_out, dim_out)
- def forward(self, x: torch.Tensor) -> torch.Tensor:
- """Applies multiscale attention with optional query pooling to extract multiscale features."""
- B, H, W, _ = x.shape
- # qkv with shape (B, H * W, 3, nHead, C)
- qkv = self.qkv(x).reshape(B, H * W, 3, self.num_heads, -1)
- # q, k, v with shape (B, H * W, nheads, C)
- q, k, v = torch.unbind(qkv, 2)
- # Q pooling (for downsample at stage changes)
- if self.q_pool:
- q = do_pool(q.reshape(B, H, W, -1), self.q_pool)
- H, W = q.shape[1:3] # downsampled shape
- q = q.reshape(B, H * W, self.num_heads, -1)
- # Torch's SDPA expects [B, nheads, H*W, C] so we transpose
- x = F.scaled_dot_product_attention(
- q.transpose(1, 2),
- k.transpose(1, 2),
- v.transpose(1, 2),
- )
- # Transpose back
- x = x.transpose(1, 2)
- x = x.reshape(B, H, W, -1)
- x = self.proj(x)
- return x
- class MultiScaleBlock(nn.Module):
- """
- A multiscale attention block with window partitioning and query pooling for efficient vision transformers.
- This class implements a multiscale attention mechanism with optional window partitioning and downsampling,
- designed for use in vision transformer architectures.
- Attributes:
- dim (int): Input dimension of the block.
- dim_out (int): Output dimension of the block.
- norm1 (nn.Module): First normalization layer.
- window_size (int): Size of the window for partitioning.
- pool (nn.Module | None): Pooling layer for query downsampling.
- q_stride (Tuple[int, int] | None): Stride for query pooling.
- attn (MultiScaleAttention): Multi-scale attention module.
- drop_path (nn.Module): Drop path layer for regularization.
- norm2 (nn.Module): Second normalization layer.
- mlp (MLP): Multi-layer perceptron module.
- proj (nn.Linear | None): Projection layer for dimension mismatch.
- Methods:
- forward: Processes input tensor through the multiscale block.
- Examples:
- >>> block = MultiScaleBlock(dim=256, dim_out=512, num_heads=8, window_size=7)
- >>> x = torch.randn(1, 56, 56, 256)
- >>> output = block(x)
- >>> print(output.shape)
- torch.Size([1, 28, 28, 512])
- """
- def __init__(
- self,
- dim: int,
- dim_out: int,
- num_heads: int,
- mlp_ratio: float = 4.0,
- drop_path: float = 0.0,
- norm_layer: Union[nn.Module, str] = "LayerNorm",
- q_stride: Tuple[int, int] = None,
- act_layer: nn.Module = nn.GELU,
- window_size: int = 0,
- ):
- """Initializes a multiscale attention block with window partitioning and optional query pooling."""
- super().__init__()
- if isinstance(norm_layer, str):
- norm_layer = partial(getattr(nn, norm_layer), eps=1e-6)
- self.dim = dim
- self.dim_out = dim_out
- self.norm1 = norm_layer(dim)
- self.window_size = window_size
- self.pool, self.q_stride = None, q_stride
- if self.q_stride:
- self.pool = nn.MaxPool2d(kernel_size=q_stride, stride=q_stride, ceil_mode=False)
- self.attn = MultiScaleAttention(
- dim,
- dim_out,
- num_heads=num_heads,
- q_pool=self.pool,
- )
- self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
- self.norm2 = norm_layer(dim_out)
- self.mlp = MLP(
- dim_out,
- int(dim_out * mlp_ratio),
- dim_out,
- num_layers=2,
- act=act_layer,
- )
- if dim != dim_out:
- self.proj = nn.Linear(dim, dim_out)
- def forward(self, x: torch.Tensor) -> torch.Tensor:
- """Processes input through multiscale attention and MLP, with optional windowing and downsampling."""
- shortcut = x # B, H, W, C
- x = self.norm1(x)
- # Skip connection
- if self.dim != self.dim_out:
- shortcut = do_pool(self.proj(x), self.pool)
- # Window partition
- window_size = self.window_size
- if window_size > 0:
- H, W = x.shape[1], x.shape[2]
- x, pad_hw = window_partition(x, window_size)
- # Window Attention + Q Pooling (if stage change)
- x = self.attn(x)
- if self.q_stride:
- # Shapes have changed due to Q pooling
- window_size = self.window_size // self.q_stride[0]
- H, W = shortcut.shape[1:3]
- pad_h = (window_size - H % window_size) % window_size
- pad_w = (window_size - W % window_size) % window_size
- pad_hw = (H + pad_h, W + pad_w)
- # Reverse window partition
- if self.window_size > 0:
- x = window_unpartition(x, window_size, pad_hw, (H, W))
- x = shortcut + self.drop_path(x)
- # MLP
- x = x + self.drop_path(self.mlp(self.norm2(x)))
- return x
- class PositionEmbeddingSine(nn.Module):
- """
- A module for generating sinusoidal positional embeddings for 2D inputs like images.
- This class implements sinusoidal position encoding for 2D spatial positions, which can be used in
- transformer-based models for computer vision tasks.
- Attributes:
- num_pos_feats (int): Number of positional features (half of the embedding dimension).
- temperature (int): Temperature parameter for the sinusoidal functions.
- normalize (bool): Whether to normalize the positional embeddings.
- scale (float): Scaling factor for the embeddings when normalize is True.
- cache (Dict): Cache for storing precomputed embeddings.
- Methods:
- _encode_xy: Encodes 2D positions using sine and cosine functions.
- encode_boxes: Encodes box coordinates and dimensions into positional embeddings.
- encode_points: Encodes 2D point coordinates with sinusoidal positional embeddings.
- forward: Generates sinusoidal position embeddings for 2D inputs.
- Examples:
- >>> pos_emb = PositionEmbeddingSine(num_pos_feats=128)
- >>> x = torch.randn(1, 3, 224, 224)
- >>> embeddings = pos_emb(x)
- >>> print(embeddings.shape)
- torch.Size([1, 256, 224, 224])
- """
- def __init__(
- self,
- num_pos_feats,
- temperature: int = 10000,
- normalize: bool = True,
- scale: Optional[float] = None,
- ):
- """Initializes sinusoidal position embeddings for 2D image inputs."""
- super().__init__()
- assert num_pos_feats % 2 == 0, "Expecting even model width"
- self.num_pos_feats = num_pos_feats // 2
- self.temperature = temperature
- self.normalize = normalize
- if scale is not None and not normalize:
- raise ValueError("normalize should be True if scale is passed")
- if scale is None:
- scale = 2 * math.pi
- self.scale = scale
- self.cache = {}
- def _encode_xy(self, x, y):
- """Encodes 2D positions using sine/cosine functions for transformer positional embeddings."""
- assert len(x) == len(y) and x.ndim == y.ndim == 1
- x_embed = x * self.scale
- y_embed = y * self.scale
- dim_t = torch.arange(self.num_pos_feats, dtype=torch.float32, device=x.device)
- dim_t = self.temperature ** (2 * (dim_t // 2) / self.num_pos_feats)
- pos_x = x_embed[:, None] / dim_t
- pos_y = y_embed[:, None] / dim_t
- pos_x = torch.stack((pos_x[:, 0::2].sin(), pos_x[:, 1::2].cos()), dim=2).flatten(1)
- pos_y = torch.stack((pos_y[:, 0::2].sin(), pos_y[:, 1::2].cos()), dim=2).flatten(1)
- return pos_x, pos_y
- @torch.no_grad()
- def encode_boxes(self, x, y, w, h):
- """Encodes box coordinates and dimensions into positional embeddings for detection."""
- pos_x, pos_y = self._encode_xy(x, y)
- return torch.cat((pos_y, pos_x, h[:, None], w[:, None]), dim=1)
- encode = encode_boxes # Backwards compatibility
- @torch.no_grad()
- def encode_points(self, x, y, labels):
- """Encodes 2D points with sinusoidal embeddings and appends labels."""
- (bx, nx), (by, ny), (bl, nl) = x.shape, y.shape, labels.shape
- assert bx == by and nx == ny and bx == bl and nx == nl
- pos_x, pos_y = self._encode_xy(x.flatten(), y.flatten())
- pos_x, pos_y = pos_x.reshape(bx, nx, -1), pos_y.reshape(by, ny, -1)
- return torch.cat((pos_y, pos_x, labels[:, :, None]), dim=2)
- @torch.no_grad()
- def forward(self, x: torch.Tensor):
- """Generates sinusoidal position embeddings for 2D inputs like images."""
- cache_key = (x.shape[-2], x.shape[-1])
- if cache_key in self.cache:
- return self.cache[cache_key][None].repeat(x.shape[0], 1, 1, 1)
- y_embed = (
- torch.arange(1, x.shape[-2] + 1, dtype=torch.float32, device=x.device)
- .view(1, -1, 1)
- .repeat(x.shape[0], 1, x.shape[-1])
- )
- x_embed = (
- torch.arange(1, x.shape[-1] + 1, dtype=torch.float32, device=x.device)
- .view(1, 1, -1)
- .repeat(x.shape[0], x.shape[-2], 1)
- )
- if self.normalize:
- eps = 1e-6
- y_embed = y_embed / (y_embed[:, -1:, :] + eps) * self.scale
- x_embed = x_embed / (x_embed[:, :, -1:] + eps) * self.scale
- dim_t = torch.arange(self.num_pos_feats, dtype=torch.float32, device=x.device)
- dim_t = self.temperature ** (2 * (dim_t // 2) / self.num_pos_feats)
- pos_x = x_embed[:, :, :, None] / dim_t
- pos_y = y_embed[:, :, :, None] / dim_t
- pos_x = torch.stack((pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()), dim=4).flatten(3)
- pos_y = torch.stack((pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()), dim=4).flatten(3)
- pos = torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2)
- self.cache[cache_key] = pos[0]
- return pos
- class PositionEmbeddingRandom(nn.Module):
- """
- Positional encoding using random spatial frequencies.
- This class generates positional embeddings for input coordinates using random spatial frequencies. It is
- particularly useful for transformer-based models that require position information.
- Attributes:
- positional_encoding_gaussian_matrix (torch.Tensor): A buffer containing random values for encoding.
- Methods:
- _pe_encoding: Positionally encodes points that are normalized to [0,1].
- forward: Generates positional encoding for a grid of the specified size.
- forward_with_coords: Positionally encodes points that are not normalized to [0,1].
- Examples:
- >>> pe = PositionEmbeddingRandom(num_pos_feats=64)
- >>> size = (32, 32)
- >>> encoding = pe(size)
- >>> print(encoding.shape)
- torch.Size([128, 32, 32])
- """
- def __init__(self, num_pos_feats: int = 64, scale: Optional[float] = None) -> None:
- """Initializes random spatial frequency position embedding for transformers."""
- super().__init__()
- if scale is None or scale <= 0.0:
- scale = 1.0
- self.register_buffer("positional_encoding_gaussian_matrix", scale * torch.randn((2, num_pos_feats)))
- # Set non-deterministic for forward() error 'cumsum_cuda_kernel does not have a deterministic implementation'
- torch.use_deterministic_algorithms(False)
- torch.backends.cudnn.deterministic = False
- def _pe_encoding(self, coords: torch.Tensor) -> torch.Tensor:
- """Encodes normalized [0,1] coordinates using random spatial frequencies."""
- # Assuming coords are in [0, 1]^2 square and have d_1 x ... x d_n x 2 shape
- coords = 2 * coords - 1
- coords = coords @ self.positional_encoding_gaussian_matrix
- coords = 2 * np.pi * coords
- # Outputs d_1 x ... x d_n x C shape
- return torch.cat([torch.sin(coords), torch.cos(coords)], dim=-1)
- def forward(self, size: Tuple[int, int]) -> torch.Tensor:
- """Generates positional encoding for a grid using random spatial frequencies."""
- h, w = size
- device: Any = self.positional_encoding_gaussian_matrix.device
- grid = torch.ones((h, w), device=device, dtype=torch.float32)
- y_embed = grid.cumsum(dim=0) - 0.5
- x_embed = grid.cumsum(dim=1) - 0.5
- y_embed = y_embed / h
- x_embed = x_embed / w
- pe = self._pe_encoding(torch.stack([x_embed, y_embed], dim=-1))
- return pe.permute(2, 0, 1) # C x H x W
- def forward_with_coords(self, coords_input: torch.Tensor, image_size: Tuple[int, int]) -> torch.Tensor:
- """Positionally encodes input coordinates, normalizing them to [0,1] based on the given image size."""
- coords = coords_input.clone()
- coords[:, :, 0] = coords[:, :, 0] / image_size[1]
- coords[:, :, 1] = coords[:, :, 1] / image_size[0]
- return self._pe_encoding(coords.to(torch.float)) # B x N x C
- class Block(nn.Module):
- """
- Transformer block with support for window attention and residual propagation.
- This class implements a transformer block that can use either global or windowed self-attention,
- followed by a feed-forward network. It supports relative positional embeddings and is designed
- for use in vision transformer architectures.
- Attributes:
- norm1 (nn.Module): First normalization layer.
- attn (REAttention): Self-attention layer with optional relative positional encoding.
- norm2 (nn.Module): Second normalization layer.
- mlp (MLPBlock): Multi-layer perceptron block.
- window_size (int): Size of attention window. If 0, global attention is used.
- Methods:
- forward: Processes input through the transformer block.
- Examples:
- >>> import torch
- >>> block = Block(dim=256, num_heads=8, window_size=7)
- >>> x = torch.randn(1, 56, 56, 256)
- >>> output = block(x)
- >>> print(output.shape)
- torch.Size([1, 56, 56, 256])
- """
- def __init__(
- self,
- dim: int,
- num_heads: int,
- mlp_ratio: float = 4.0,
- qkv_bias: bool = True,
- norm_layer: Type[nn.Module] = nn.LayerNorm,
- act_layer: Type[nn.Module] = nn.GELU,
- use_rel_pos: bool = False,
- rel_pos_zero_init: bool = True,
- window_size: int = 0,
- input_size: Optional[Tuple[int, int]] = None,
- ) -> None:
- """
- Initializes a transformer block with optional window attention and relative positional embeddings.
- This constructor sets up a transformer block that can use either global or windowed self-attention,
- followed by a feed-forward network. It supports relative positional embeddings and is designed
- for use in vision transformer architectures.
- Args:
- dim (int): Number of input channels.
- num_heads (int): Number of attention heads in the self-attention layer.
- mlp_ratio (float): Ratio of mlp hidden dimension to embedding dimension.
- qkv_bias (bool): If True, adds a learnable bias to query, key, value projections.
- norm_layer (Type[nn.Module]): Type of normalization layer to use.
- act_layer (Type[nn.Module]): Type of activation function to use in the MLP block.
- use_rel_pos (bool): If True, uses relative positional embeddings in attention.
- rel_pos_zero_init (bool): If True, initializes relative positional parameters to zero.
- window_size (int): Size of attention window. If 0, uses global attention.
- input_size (Optional[Tuple[int, int]]): Input resolution for calculating relative positional parameter size.
- Examples:
- >>> block = Block(dim=256, num_heads=8, window_size=7)
- >>> x = torch.randn(1, 56, 56, 256)
- >>> output = block(x)
- >>> print(output.shape)
- torch.Size([1, 56, 56, 256])
- """
- super().__init__()
- self.norm1 = norm_layer(dim)
- self.attn = REAttention(
- dim,
- num_heads=num_heads,
- qkv_bias=qkv_bias,
- use_rel_pos=use_rel_pos,
- rel_pos_zero_init=rel_pos_zero_init,
- input_size=input_size if window_size == 0 else (window_size, window_size),
- )
- self.norm2 = norm_layer(dim)
- self.mlp = MLPBlock(embedding_dim=dim, mlp_dim=int(dim * mlp_ratio), act=act_layer)
- self.window_size = window_size
- def forward(self, x: torch.Tensor) -> torch.Tensor:
- """Processes input through transformer block with optional windowed self-attention and residual connection."""
- shortcut = x
- x = self.norm1(x)
- # Window partition
- if self.window_size > 0:
- H, W = x.shape[1], x.shape[2]
- x, pad_hw = window_partition(x, self.window_size)
- x = self.attn(x)
- # Reverse window partition
- if self.window_size > 0:
- x = window_unpartition(x, self.window_size, pad_hw, (H, W))
- x = shortcut + x
- return x + self.mlp(self.norm2(x))
- class REAttention(nn.Module):
- """
- Rotary Embedding Attention module for efficient self-attention in transformer architectures.
- This class implements a multi-head attention mechanism with rotary positional embeddings, designed
- for use in vision transformer models. It supports optional query pooling and window partitioning
- for efficient processing of large inputs.
- Attributes:
- compute_cis (Callable): Function to compute axial complex numbers for rotary encoding.
- freqs_cis (Tensor): Precomputed frequency tensor for rotary encoding.
- rope_k_repeat (bool): Flag to repeat query RoPE to match key length for cross-attention to memories.
- q_proj (nn.Linear): Linear projection for query.
- k_proj (nn.Linear): Linear projection for key.
- v_proj (nn.Linear): Linear projection for value.
- out_proj (nn.Linear): Output projection.
- num_heads (int): Number of attention heads.
- internal_dim (int): Internal dimension for attention computation.
- Methods:
- forward: Applies rotary position encoding and computes attention between query, key, and value tensors.
- Examples:
- >>> rope_attn = REAttention(embedding_dim=256, num_heads=8, rope_theta=10000.0, feat_sizes=(32, 32))
- >>> q = torch.randn(1, 1024, 256)
- >>> k = torch.randn(1, 1024, 256)
- >>> v = torch.randn(1, 1024, 256)
- >>> output = rope_attn(q, k, v)
- >>> print(output.shape)
- torch.Size([1, 1024, 256])
- """
- def __init__(
- self,
- dim: int,
- num_heads: int = 8,
- qkv_bias: bool = True,
- use_rel_pos: bool = False,
- rel_pos_zero_init: bool = True,
- input_size: Optional[Tuple[int, int]] = None,
- ) -> None:
- """
- Initializes a Relative Position Attention module for transformer-based architectures.
- This module implements multi-head attention with optional relative positional encodings, designed
- specifically for vision tasks in transformer models.
- Args:
- dim (int): Number of input channels.
- num_heads (int): Number of attention heads. Default is 8.
- qkv_bias (bool): If True, adds a learnable bias to query, key, value projections. Default is True.
- use_rel_pos (bool): If True, uses relative positional encodings. Default is False.
- rel_pos_zero_init (bool): If True, initializes relative positional parameters to zero. Default is True.
- input_size (Tuple[int, int] | None): Input resolution for calculating relative positional parameter size.
- Required if use_rel_pos is True. Default is None.
- Examples:
- >>> attention = REAttention(dim=256, num_heads=8, input_size=(32, 32))
- >>> x = torch.randn(1, 32, 32, 256)
- >>> output = attention(x)
- >>> print(output.shape)
- torch.Size([1, 32, 32, 256])
- """
- super().__init__()
- self.num_heads = num_heads
- head_dim = dim // num_heads
- self.scale = head_dim**-0.5
- self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
- self.proj = nn.Linear(dim, dim)
- self.use_rel_pos = use_rel_pos
- if self.use_rel_pos:
- assert input_size is not None, "Input size must be provided if using relative positional encoding."
- # Initialize relative positional embeddings
- self.rel_pos_h = nn.Parameter(torch.zeros(2 * input_size[0] - 1, head_dim))
- self.rel_pos_w = nn.Parameter(torch.zeros(2 * input_size[1] - 1, head_dim))
- def forward(self, x: torch.Tensor) -> torch.Tensor:
- """Applies multi-head attention with optional relative positional encoding to input tensor."""
- B, H, W, _ = x.shape
- # qkv with shape (3, B, nHead, H * W, C)
- qkv = self.qkv(x).reshape(B, H * W, 3, self.num_heads, -1).permute(2, 0, 3, 1, 4)
- # q, k, v with shape (B * nHead, H * W, C)
- q, k, v = qkv.reshape(3, B * self.num_heads, H * W, -1).unbind(0)
- attn = (q * self.scale) @ k.transpose(-2, -1)
- if self.use_rel_pos:
- attn = add_decomposed_rel_pos(attn, q, self.rel_pos_h, self.rel_pos_w, (H, W), (H, W))
- attn = attn.softmax(dim=-1)
- x = (attn @ v).view(B, self.num_heads, H, W, -1).permute(0, 2, 3, 1, 4).reshape(B, H, W, -1)
- return self.proj(x)
- class PatchEmbed(nn.Module):
- """
- Image to Patch Embedding module for vision transformer architectures.
- This module converts an input image into a sequence of patch embeddings using a convolutional layer.
- It is commonly used as the first layer in vision transformer architectures to transform image data
- into a suitable format for subsequent transformer blocks.
- Attributes:
- proj (nn.Conv2d): Convolutional layer for projecting image patches to embeddings.
- Methods:
- forward: Applies patch embedding to the input tensor.
- Examples:
- >>> patch_embed = PatchEmbed(kernel_size=(16, 16), stride=(16, 16), in_chans=3, embed_dim=768)
- >>> x = torch.randn(1, 3, 224, 224)
- >>> output = patch_embed(x)
- >>> print(output.shape)
- torch.Size([1, 768, 14, 14])
- """
- def __init__(
- self,
- kernel_size: Tuple[int, int] = (16, 16),
- stride: Tuple[int, int] = (16, 16),
- padding: Tuple[int, int] = (0, 0),
- in_chans: int = 3,
- embed_dim: int = 768,
- ) -> None:
- """
- Initializes the PatchEmbed module for converting image patches to embeddings.
- This module is typically used as the first layer in vision transformer architectures to transform
- image data into a suitable format for subsequent transformer blocks.
- Args:
- kernel_size (Tuple[int, int]): Size of the convolutional kernel for patch extraction.
- stride (Tuple[int, int]): Stride of the convolutional operation.
- padding (Tuple[int, int]): Padding applied to the input before convolution.
- in_chans (int): Number of input image channels.
- embed_dim (int): Dimensionality of the output patch embeddings.
- Examples:
- >>> patch_embed = PatchEmbed(kernel_size=(16, 16), stride=(16, 16), in_chans=3, embed_dim=768)
- >>> x = torch.randn(1, 3, 224, 224)
- >>> output = patch_embed(x)
- >>> print(output.shape)
- torch.Size([1, 768, 14, 14])
- """
- super().__init__()
- self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=kernel_size, stride=stride, padding=padding)
- def forward(self, x: torch.Tensor) -> torch.Tensor:
- """Computes patch embedding by applying convolution and transposing resulting tensor."""
- return self.proj(x).permute(0, 2, 3, 1) # B C H W -> B H W C
|