Spaces:
Sleeping
Sleeping
| # Copyright 2024 EPFL and Apple Inc. | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| from typing import Dict, List, Optional, Tuple, Union | |
| import torch | |
| import torch.nn as nn | |
| from einops import rearrange, repeat | |
| from .fm_utils import build_1d_sincos_posemb, build_2d_sincos_posemb, pair | |
| class SequenceEncoderEmbedding(nn.Module): | |
| """Embedding module for encoding sequence inputs, like captions or a sequence of objects. | |
| Args: | |
| vocab_size: Vocabulary size | |
| max_length: Maximum number of tokens in the sequence | |
| dim_tokens: Dimension of output tokens. Can be set using init method. | |
| sincos_pos_emb: Set to True (default) to use fixed 1D sin-cos positional embeddings | |
| max_sincos_pos_emb: Maximum allowed length for sin-cos positional embeddings | |
| padding_idx: Padding index for word embedding | |
| """ | |
| def __init__(self, | |
| vocab_size: int, | |
| max_length: int, | |
| dim_tokens: Optional[int] = None, | |
| sincos_pos_emb: bool = True, | |
| max_sincos_pos_emb: int = 4675, | |
| padding_idx: int = 0, | |
| ): | |
| super().__init__() | |
| self.vocab_size = vocab_size | |
| self.max_length = max_length | |
| self.dim_tokens = dim_tokens | |
| self.sincos_pos_emb = sincos_pos_emb | |
| self.padding_idx = padding_idx | |
| self.max_sincos_pos_emb = max_sincos_pos_emb | |
| if self.dim_tokens is not None: | |
| self.init(dim_tokens=dim_tokens) | |
| def init(self, dim_tokens: int = 768, init_std=0.02): | |
| """ | |
| Initialize parts of embedding module that are dependent on dimension of tokens. | |
| Should be called when setting up FourM. | |
| Args: | |
| dim_tokens: Dimension of tokens | |
| init_std: Standard deviation of init | |
| """ | |
| self.dim_tokens = dim_tokens | |
| # Task embedding identifying from which task a given token comes from | |
| # Fixed-size positional embeddings. Can be interpolated to different input sizes | |
| if self.sincos_pos_emb: | |
| if self.max_length > self.max_sincos_pos_emb: | |
| raise ValueError(f"Max length ({self.max_length}) is greater than the number of posembs ({self.max_sincos_pos_emb}") | |
| pos_emb = build_1d_sincos_posemb(max_len=self.max_sincos_pos_emb, embed_dim=self.dim_tokens)[:self.max_length] | |
| self.register_buffer("pos_emb", pos_emb) # self.pos_emb is now a buffer for FSDP | |
| else: | |
| self.pos_emb = nn.Parameter(torch.zeros(1, self.max_length, self.dim_tokens)) | |
| nn.init.normal_(self.pos_emb, std=init_std) | |
| self.mod_emb = nn.Parameter(torch.zeros(1, 1, self.dim_tokens)) | |
| nn.init.normal_(self.mod_emb, std=init_std) | |
| # Token embedding | |
| self.token_emb = nn.Embedding(num_embeddings=self.vocab_size, embedding_dim=self.dim_tokens, | |
| padding_idx=self.padding_idx) | |
| def no_weight_decay(self): | |
| return set() | |
| def forward(self, d : Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]: | |
| """ | |
| Forward pass through embedding module, transforming sequence of ids to sequence of embeddings. | |
| Creates corresponding modality and positional embeddings and adds them to the dict. | |
| Args: | |
| d (Dict[str, torch.Tensor]): Modality dict with at least the following keys: | |
| - 'tensor' (torch.Tensor): Input token sequence for each batch. Shape (B, L) where B is the batch size and L is the sequence length. | |
| - 'input_mask' (torch.Tensor): Mask for valid tokens in the input sequence (set to 0 for valid tokens and 1 otherwise). Shape (B, L). | |
| Returns: | |
| Dict[str, torch.Tensor]: Modality dict with added keys: | |
| - 'x' (torch.Tensor): Embedded token sequence. Shape (B, L, D) where D is the embedding dimension. | |
| - 'emb' (torch.Tensor): Sum of positional and modality embeddings for the input sequence. Shape (B, L, D). | |
| """ | |
| ids = d['tensor'] | |
| B = ids.shape[0] | |
| assert self.dim_tokens is not None, 'Need to call init(dim_tokens) function first' | |
| # Map to embedding | |
| x = self.token_emb(ids) | |
| expanded_pos_emb = repeat(self.pos_emb, "() n d -> b n d", b=B) | |
| # Input pos encoding | |
| input_mask = d['input_mask'] | |
| input_pos_id = (~input_mask).int().cumsum(dim=1) - 1 | |
| input_pos_id[input_mask] = 0 | |
| input_pos_emb = torch.gather(expanded_pos_emb, dim=1, index=repeat(input_pos_id, "b n -> b n d", d=expanded_pos_emb.shape[2])) | |
| input_pos_emb[input_mask] = 0 | |
| x_emb = input_pos_emb + self.mod_emb | |
| d['x'] = x | |
| d['emb'] = x_emb | |
| return d | |
| class LongSequenceEncoderEmbedding(nn.Module): | |
| """Embedding module for encoding sequence inputs, like captions or a sequence of objects. | |
| Args: | |
| vocab_size: Vocabulary size | |
| max_length: Maximum number of tokens in the sequence | |
| dim_tokens: Dimension of output tokens. Can be set using init method. | |
| sincos_pos_emb: Set to True (default) to use fixed 1D sin-cos positional embeddings | |
| max_sincos_pos_emb: Maximum allowed length for sin-cos positional embeddings | |
| padding_idx: Padding index for word embedding | |
| """ | |
| def __init__(self, | |
| vocab_size: int, | |
| dim_tokens: Optional[int] = None, | |
| sincos_pos_emb: bool = True, | |
| max_spatial_positions: int = 60, | |
| padding_idx: int = 0, | |
| ): | |
| super().__init__() | |
| self.vocab_size = vocab_size | |
| self.dim_tokens = dim_tokens | |
| self.sincos_pos_emb = sincos_pos_emb | |
| self.padding_idx = padding_idx | |
| self.max_spatial_positions = max_spatial_positions | |
| self.max_temporal_positions = 17 # 17 frame video | |
| self.max_length = self.max_spatial_positions * self.max_temporal_positions | |
| if self.dim_tokens is not None: | |
| self.init(dim_tokens=dim_tokens) | |
| def init(self, dim_tokens: int = 768, init_std=0.02): | |
| """ | |
| Initialize parts of embedding module that are dependent on dimension of tokens. | |
| Should be called when setting up FourM. | |
| Args: | |
| dim_tokens: Dimension of tokens | |
| init_std: Standard deviation of init | |
| """ | |
| self.dim_tokens = dim_tokens | |
| # Task embedding identifying from which task a given token comes from | |
| # Fixed-size positional embeddings. Can be interpolated to different input sizes | |
| if self.sincos_pos_emb: | |
| # Use sin-cos for both spatial and temporal | |
| spatial_pos_emb = build_1d_sincos_posemb( | |
| max_len=self.max_spatial_positions, | |
| embed_dim=self.dim_tokens | |
| ) | |
| temporal_pos_emb = build_1d_sincos_posemb( | |
| max_len=self.max_temporal_positions, | |
| embed_dim=self.dim_tokens | |
| ) | |
| self.register_buffer("spatial_pos_emb", spatial_pos_emb) | |
| self.register_buffer("temporal_pos_emb", temporal_pos_emb) | |
| else: | |
| # Learnable spatial and temporal embeddings | |
| self.spatial_pos_emb = nn.Parameter( | |
| torch.zeros(1, self.max_spatial_positions, self.dim_tokens) | |
| ) | |
| self.temporal_pos_emb = nn.Parameter( | |
| torch.zeros(1, self.max_temporal_positions, self.dim_tokens) | |
| ) | |
| nn.init.normal_(self.spatial_pos_emb, std=init_std) | |
| nn.init.normal_(self.temporal_pos_emb, std=init_std) | |
| self.mod_emb = nn.Parameter(torch.zeros(1, 1, self.dim_tokens)) | |
| nn.init.normal_(self.mod_emb, std=init_std) | |
| # Token embedding | |
| self.token_emb = nn.Embedding(num_embeddings=self.vocab_size, embedding_dim=self.dim_tokens, | |
| padding_idx=self.padding_idx) | |
| def no_weight_decay(self): | |
| return set() | |
| def forward(self, d: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]: | |
| """ | |
| Forward pass through embedding module, transforming sequence of ids to sequence of embeddings. | |
| Creates corresponding modality and positional embeddings and adds them to the dict. | |
| Args: | |
| d (Dict[str, torch.Tensor]): Modality dict with at least the following keys: | |
| - 'tensor' (torch.Tensor): Input token sequence for each batch. Shape (B, L) where B is the batch size and L is the sequence length. | |
| - 'input_mask' (torch.Tensor): Mask for valid tokens in the input sequence (set to 0 for valid tokens and 1 otherwise). Shape (B, L). | |
| Returns: | |
| Dict[str, torch.Tensor]: Modality dict with added keys: | |
| - 'x' (torch.Tensor): Embedded token sequence. Shape (B, L, D) where D is the embedding dimension. | |
| - 'emb' (torch.Tensor): Sum of positional and modality embeddings for the input sequence. Shape (B, L, D). | |
| """ | |
| ids = d['tensor'] | |
| frame_ids = d["tensor_frame_ids"] # (B, N) | |
| input_mask = d["input_mask"] # (B, N) | |
| B = ids.shape[0] | |
| assert self.dim_tokens is not None, 'Need to call init(dim_tokens) function first' | |
| # 2. Expand learnable embeddings | |
| expanded_spatial_pos_emb = repeat(self.spatial_pos_emb, "() n d -> b n d", b=B) | |
| expanded_temporal_pos_emb = repeat(self.temporal_pos_emb, "() t d -> b t d", b=B) | |
| # Map to embedding | |
| x = self.token_emb(ids) | |
| # Input pos encoding | |
| input_pos_id = (~input_mask).int().cumsum(dim=1) - 1 | |
| # Detect frame boundaries (where frame_id changes) | |
| frame_change = torch.cat([ | |
| torch.ones(B, 1, dtype=torch.bool, device=frame_ids.device), # First position is always a boundary | |
| frame_ids[:, 1:] != frame_ids[:, :-1] | |
| ], dim=1) | |
| # Get cumsum at frame boundaries to subtract | |
| frame_offsets = torch.where(frame_change, input_pos_id, torch.zeros_like(input_pos_id)) | |
| frame_offsets = torch.cummax(frame_offsets, dim=1)[0] | |
| # Reset position ids by subtracting the offset at each frame start | |
| input_pos_id = input_pos_id - frame_offsets | |
| # Mask out padding positions | |
| input_pos_id[input_mask] = 0 | |
| # Now, need to change update it using frame per id | |
| input_pos_emb = torch.gather(expanded_spatial_pos_emb, dim=1, index=repeat(input_pos_id, "b n -> b n d", d=expanded_spatial_pos_emb.shape[2])) | |
| input_pos_emb[input_mask] = 0 | |
| # now lets do similar for temporal embeddings | |
| input_pos_emb_temporal = torch.gather(expanded_temporal_pos_emb, dim=1, index=repeat(frame_ids, "b n -> b n d", d=expanded_temporal_pos_emb.shape[2])) | |
| input_pos_emb_temporal[input_mask] = 0 | |
| x_emb = input_pos_emb + self.mod_emb + input_pos_emb_temporal | |
| d['x'] = x | |
| d['emb'] = x_emb | |
| return d | |
| class ImageTokenEncoderEmbedding(nn.Module): | |
| """Embedding module for tokenized spatial inputs. | |
| Args: | |
| vocab_size: Vocabulary size | |
| patch_size: Int or tuple of the patch size over the full image size. | |
| dim_tokens: Dimension of output tokens. Can be set using init method. | |
| sincos_pos_emb: Set to True (default) to use fixed 2D sin-cos positional embeddings | |
| image_size: Default image size. Used to initialize size of positional embeddings. | |
| """ | |
| def __init__(self, | |
| vocab_size: int, | |
| patch_size: Union[int, Tuple[int,int]] = 16, | |
| dim_tokens: Optional[int] = None, | |
| sincos_pos_emb: bool = True, | |
| image_size: Union[int, Tuple[int]] = 224, | |
| **kwargs): | |
| super().__init__() | |
| self.vocab_size = vocab_size | |
| self.patch_size = pair(patch_size) | |
| self.dim_tokens = dim_tokens | |
| self.sincos_pos_emb = sincos_pos_emb | |
| self.image_size = pair(image_size) | |
| self.num_patches = (self.image_size[0] // patch_size) * (self.image_size[1] // patch_size) | |
| if self.dim_tokens is not None: | |
| self.init(dim_tokens=dim_tokens) | |
| def init(self, dim_tokens: int = 768, init_std=0.02): | |
| """ | |
| Initialize parts of module that are dependent on dimension of tokens. | |
| Should be called when setting up FourM. | |
| Args: | |
| dim_tokens: Dimension of tokens | |
| init_std: Standard deviation of init | |
| """ | |
| self.dim_tokens = dim_tokens | |
| # Task embedding identifying from which task a given token comes from | |
| # Fixed-size positional embeddings. Can be interpolated to different input sizes | |
| h_posemb = self.image_size[0] // self.patch_size[0] | |
| w_posemb = self.image_size[1] // self.patch_size[1] | |
| if self.sincos_pos_emb: | |
| pos_emb = build_2d_sincos_posemb(h=h_posemb, w=w_posemb, embed_dim=self.dim_tokens) | |
| self.register_buffer("pos_emb", pos_emb) # self.pos_emb is now a buffer for FSDP | |
| else: | |
| self.pos_emb = nn.Parameter(torch.zeros(1, (h_posemb * w_posemb), self.dim_tokens)) | |
| nn.init.normal_(self.pos_emb, std=init_std) | |
| self.mod_emb = nn.Parameter(torch.zeros(1, 1, self.dim_tokens)) | |
| nn.init.normal_(self.mod_emb, std=init_std) | |
| # Token embedding | |
| self.token_emb = nn.Embedding(num_embeddings=self.vocab_size, embedding_dim=self.dim_tokens) | |
| def no_weight_decay(self): | |
| return set() | |
| def forward(self, d: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]: | |
| """ | |
| Forward pass through embedding module, transforming image tokens to a sequence of embeddings. | |
| Creates corresponding modality and positional embeddings and adds them to the dict. | |
| Args: | |
| d (Dict[str, torch.Tensor]): Modality dict with at least the following key: | |
| - 'tensor' (torch.Tensor): Input image tokens for each batch. Shape (B, H, W) where B is the batch size, and H, W are height and width of the tokenized image. - 'input_mask' (torch.Tensor): Mask for valid tokens in the input sequence (set to 0 for valid tokens and 1 otherwise). Shape (B, L). | |
| Returns: | |
| Dict[str, torch.Tensor]: Modality dictionary with added keys: | |
| - 'x' (torch.Tensor): Embedded token sequence. Shape (B, H*W, D). | |
| - 'emb' (torch.Tensor): Sum of positional and modality embeddings for the input sequence. Shape (B, H*W, D). | |
| """ | |
| ids = d['tensor'] | |
| B = ids.shape[0] | |
| ids = ids.reshape(B, -1) | |
| # Map to embedding | |
| x = self.token_emb(ids) | |
| # Create positional embedding + modality embedding | |
| x_emb = repeat(self.pos_emb + self.mod_emb, '() n d -> b n d', b=B) | |
| d['x'] = x | |
| d['emb'] = x_emb | |
| return d | |
| class ClassConditionalEncoderEmbedding(nn.Module): | |
| """Embedding module for tokenized spatio-spatial inputs. | |
| Args: | |
| vocab_size: Vocabulary size | |
| patch_size: Int or tuple of the patch size over the full image size. | |
| dim_tokens: Dimension of output tokens. Can be set using init method. | |
| sincos_pos_emb: Set to True (default) to use fixed 2D sin-cos positional embeddings | |
| image_size: Default image size. Used to initialize size of positional embeddings. | |
| """ | |
| def __init__(self, | |
| vocab_size: int, | |
| dim_tokens: Optional[int] = None, | |
| **kwargs): | |
| super().__init__() | |
| self.vocab_size = vocab_size | |
| self.dim_tokens = dim_tokens | |
| if self.dim_tokens is not None: | |
| self.init(dim_tokens=dim_tokens) | |
| def init(self, dim_tokens: int = 768, init_std=0.02): | |
| """ | |
| Initialize parts of module that are dependent on dimension of tokens. | |
| Should be called when setting up FourM. | |
| Args: | |
| dim_tokens: Dimension of tokens | |
| init_std: Standard deviation of init | |
| """ | |
| self.dim_tokens = dim_tokens | |
| # Task embedding identifying from which task a given token comes from | |
| # Fixed-size positional embeddings. Can be interpolated to different input sizes | |
| self.mod_emb = nn.Parameter(torch.zeros(1, 1, self.dim_tokens)) | |
| nn.init.normal_(self.mod_emb, std=init_std) | |
| # Token embedding | |
| self.token_emb = nn.Embedding(num_embeddings=self.vocab_size, embedding_dim=self.dim_tokens) | |
| def no_weight_decay(self): | |
| return set() | |
| def forward(self, d: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]: | |
| """ | |
| Forward pass through embedding module, transforming image tokens to a sequence of embeddings. | |
| Creates corresponding modality and positional embeddings and adds them to the dict. | |
| Args: | |
| d (Dict[str, torch.Tensor]): Modality dict with at least the following key: | |
| - 'tensor' (torch.Tensor): Input image tokens for each batch. Shape (B, H, W) where B is the batch size, and H, W are height and width of the tokenized image. - 'input_mask' (torch.Tensor): Mask for valid tokens in the input sequence (set to 0 for valid tokens and 1 otherwise). Shape (B, L). | |
| Returns: | |
| Dict[str, torch.Tensor]: Modality dictionary with added keys: | |
| - 'x' (torch.Tensor): Embedded token sequence. Shape (B, H*W, D). | |
| - 'emb' (torch.Tensor): Sum of positional and modality embeddings for the input sequence. Shape (B, H*W, D). | |
| """ | |
| ids = d['tensor'] # this will be a single token id | |
| B = ids.shape[0] | |
| # ids = ids.reshape(B, -1) | |
| # Map to embedding | |
| x = self.token_emb(ids) | |
| # Now rearrange them fully | |
| x_emb = repeat(self.mod_emb, '() n d -> b n d', b=B) | |
| d['x'] = x | |
| d['emb'] = x_emb | |
| return d | |
| class VideoTokenEncoderEmbedding(nn.Module): | |
| """Embedding module for tokenized spatio-spatial inputs. | |
| Args: | |
| vocab_size: Vocabulary size | |
| patch_size: Int or tuple of the patch size over the full image size. | |
| dim_tokens: Dimension of output tokens. Can be set using init method. | |
| sincos_pos_emb: Set to True (default) to use fixed 2D sin-cos positional embeddings | |
| image_size: Default image size. Used to initialize size of positional embeddings. | |
| """ | |
| def __init__(self, | |
| vocab_size: int, | |
| patch_size: Union[int, Tuple[int, int]] = 8, # here the spatial compression is 8 times, not 16 times | |
| dim_tokens: Optional[int] = None, | |
| sincos_pos_emb=True, | |
| image_size: Union[int, Tuple[int]] = 128, | |
| num_frames=17, | |
| temporal_t=5, | |
| modality_name=None, | |
| **kwargs): | |
| super().__init__() | |
| self.vocab_size = vocab_size | |
| self.patch_size = pair(patch_size) | |
| self.dim_tokens = dim_tokens | |
| self.sincos_pos_emb = sincos_pos_emb | |
| self.image_size = pair(image_size) | |
| self.num_patches = (self.image_size[0] // patch_size) * (self.image_size[1] // patch_size) | |
| self.num_frames = num_frames | |
| self.temporal_t = temporal_t | |
| self.modality_name = modality_name | |
| if self.dim_tokens is not None: | |
| self.init(dim_tokens=dim_tokens) | |
| def init(self, dim_tokens: int = 768, init_std=0.02): | |
| """ | |
| Initialize parts of module that are dependent on dimension of tokens. | |
| Should be called when setting up FourM. | |
| Args: | |
| dim_tokens: Dimension of tokens | |
| init_std: Standard deviation of init | |
| """ | |
| self.dim_tokens = dim_tokens | |
| # Task embedding identifying from which task a given token comes from | |
| # Fixed-size positional embeddings. Can be interpolated to different input sizes | |
| self.sincos_pos_emb = False | |
| if self.sincos_pos_emb: | |
| h_posemb = self.image_size[0] // self.patch_size[0] | |
| w_posemb = self.image_size[1] // self.patch_size[1] | |
| spatial_emb = build_2d_sincos_posemb(h=h_posemb, w=w_posemb, embed_dim=self.dim_tokens) | |
| temporal_emb = build_1d_sincos_posemb(self.temporal_t, embed_dim=self.dim_tokens) # hardcoded t=5 for now | |
| spatial_expanded = spatial_emb.repeat(1, self.temporal_t, 1) # (1, T*H*W, embed_dim) | |
| temporal_expanded = temporal_emb.repeat_interleave(h_posemb * w_posemb, dim=1) # (1, T*H*W, embed_dim) | |
| final_embeddings = spatial_expanded + temporal_expanded | |
| self.register_buffer("pos_emb", final_embeddings) # self.pos_emb is now a buffer for FSDP | |
| else: | |
| # self.pos_emb = nn.Parameter(torch.zeros(1, (h_posemb * w_posemb), self.dim_tokens)) | |
| # nn.init.normal_(self.pos_emb, std=init_std) | |
| self.h_posemb = self.image_size[0] // self.patch_size[0] | |
| self.w_posemb = self.image_size[1] // self.patch_size[1] | |
| self.temb = nn.Parameter(torch.zeros(1, self.temporal_t, dim_tokens)) | |
| self.semb = nn.Parameter(torch.zeros(1, self.h_posemb * self.w_posemb, dim_tokens)) | |
| nn.init.trunc_normal_(self.temb, std=0.02) | |
| nn.init.trunc_normal_(self.semb, std=0.02) | |
| self.mod_emb = nn.Parameter(torch.zeros(1, 1, self.dim_tokens)) | |
| nn.init.normal_(self.mod_emb, std=init_std) | |
| # Token embedding | |
| self.token_emb = nn.Embedding(num_embeddings=self.vocab_size, embedding_dim=self.dim_tokens) | |
| def no_weight_decay(self): | |
| return set() | |
| def forward(self, d: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]: | |
| """ | |
| Forward pass through embedding module, transforming image tokens to a sequence of embeddings. | |
| Creates corresponding modality and positional embeddings and adds them to the dict. | |
| Args: | |
| d (Dict[str, torch.Tensor]): Modality dict with at least the following key: | |
| - 'tensor' (torch.Tensor): Input image tokens for each batch. Shape (B, H, W) where B is the batch size, and H, W are height and width of the tokenized image. - 'input_mask' (torch.Tensor): Mask for valid tokens in the input sequence (set to 0 for valid tokens and 1 otherwise). Shape (B, L). | |
| Returns: | |
| Dict[str, torch.Tensor]: Modality dictionary with added keys: | |
| - 'x' (torch.Tensor): Embedded token sequence. Shape (B, H*W, D). | |
| - 'emb' (torch.Tensor): Sum of positional and modality embeddings for the input sequence. Shape (B, H*W, D). | |
| """ | |
| ids = d['tensor'] # B, (T/4)*(HW/8*8) --> B, 5*256 | |
| B = ids.shape[0] | |
| # ids = ids.reshape(B, -1) | |
| # Map to embedding | |
| x = self.token_emb(ids) | |
| if self.sincos_pos_emb: | |
| x_emb = self.pos_emb | |
| else: | |
| spatial_expanded = self.semb.repeat(1, self.temporal_t, 1) # (1, T*H*W, D) | |
| temporal_expanded = self.temb.repeat_interleave(self.h_posemb * self.w_posemb, dim=1) | |
| x_emb = spatial_expanded + temporal_expanded | |
| # Now rearrange them fully | |
| x_emb = repeat(x_emb + self.mod_emb, '() n d -> b n d', b=B) | |
| d['x'] = x | |
| d['emb'] = x_emb | |
| return d | |
| class PatchEmbed(nn.Module): | |
| """ Image to Patch Embedding | |
| """ | |
| def __init__(self, | |
| img_size=224, | |
| patch_size=16, | |
| in_chans=3, | |
| embed_dim=768, | |
| num_frames=16, | |
| tubelet_size=2): | |
| super().__init__() | |
| img_size = (img_size, img_size) | |
| patch_size = (patch_size, patch_size) | |
| num_spatial_patches = (img_size[0] // patch_size[0]) * ( | |
| img_size[1] // patch_size[1]) | |
| num_patches = num_spatial_patches * (num_frames // tubelet_size) | |
| self.img_size = img_size | |
| self.tubelet_size = tubelet_size | |
| self.patch_size = patch_size | |
| self.num_patches = num_patches | |
| self.proj = nn.Conv3d( | |
| in_channels=in_chans, | |
| out_channels=embed_dim, | |
| kernel_size=(self.tubelet_size, patch_size[0], patch_size[1]), | |
| stride=(self.tubelet_size, patch_size[0], patch_size[1])) | |
| def forward(self, x, **kwargs): | |
| B, C, T, H, W = x.shape | |
| assert H == self.img_size[0] and W == self.img_size[ | |
| 1], f"Input image size ({H}*{W}) doesn't match model ({self.img_size[0]}*{self.img_size[1]})." | |
| # b, c, l -> b, l, c | |
| x = self.proj(x).flatten(2).transpose(1, 2) # channels on the end... | |
| return x | |
| class VideoEncoderEmbedding(nn.Module): | |
| """Embedding module for spatial inputs, like images or feature maps. | |
| Creates tokens from patches over the image. | |
| This adapter / embedding differs from the one of MultiMAE by taking as input a dict and | |
| separating positional embeddings and modality embeddings from the input projection | |
| Input projection is 'x', posemb + modemb is 'emb' | |
| Args: | |
| num_channels: Number of input channels of the image/feature map | |
| patch_size: Int or tuple of the patch size over the full image size. | |
| dim_tokens: Dimension of output tokens. Can be set using init method. | |
| sincos_pos_emb: Set to True (default) to use fixed 2D sin-cos positional embeddings | |
| image_size: Default image size. Used to initialize size of positional embeddings. | |
| """ | |
| def __init__(self, | |
| num_channels: int, | |
| patch_size: Union[int, Tuple[int, int]], | |
| dim_tokens: Optional[int] = None, | |
| sincos_pos_emb=True, | |
| image_size: Union[int, Tuple[int]] = 224, | |
| num_frames=16, | |
| tube_size=2, | |
| ): | |
| super().__init__() | |
| self.num_channels = num_channels | |
| self.patch_size = pair(patch_size) | |
| self.dim_tokens = dim_tokens | |
| self.sincos_pos_emb = sincos_pos_emb | |
| self.image_size = pair(image_size) | |
| self.num_patches = (self.image_size[0] // patch_size) * (self.image_size[1] // patch_size) | |
| self.num_frames = num_frames | |
| self.tube_size = tube_size | |
| if self.dim_tokens is not None: | |
| self.init(dim_tokens=dim_tokens) | |
| def init(self, dim_tokens: int = 768, init_std=0.02): | |
| """ | |
| Initialize parts of encoder that are dependent on dimension of tokens. | |
| Should be called when setting up FourM. | |
| Args: | |
| dim_tokens: Dimension of tokens | |
| init_std: Standard deviation of init | |
| """ | |
| self.dim_tokens = dim_tokens | |
| # Task embedding identifying from which task a given token comes from | |
| # Fixed-size positional embeddings. Can be interpolated to different input sizes | |
| self.sincos_pos_emb = False | |
| if self.sincos_pos_emb: | |
| h_posemb = self.image_size[0] // self.patch_size[0] | |
| w_posemb = self.image_size[1] // self.patch_size[1] | |
| temporal_t = 5 | |
| spatial_emb = build_2d_sincos_posemb(h=h_posemb, w=w_posemb, embed_dim=self.dim_tokens) | |
| temporal_emb = build_1d_sincos_posemb(temporal_t, embed_dim=self.dim_tokens) # hardcoded t=5 for now | |
| spatial_expanded = spatial_emb.repeat(1, temporal_t, 1) # (1, T*H*W, embed_dim) | |
| temporal_expanded = temporal_emb.repeat_interleave(h_posemb * w_posemb, dim=1) # (1, T*H*W, embed_dim) | |
| final_embeddings = spatial_expanded + temporal_expanded | |
| self.register_buffer("pos_emb", final_embeddings) # self.pos_emb is now a buffer for FSDP | |
| else: | |
| # self.pos_emb = nn.Parameter(torch.zeros(1, (h_posemb * w_posemb), self.dim_tokens)) | |
| # nn.init.normal_(self.pos_emb, std=init_std) | |
| self.temporal_t = self.num_frames // self.tube_size | |
| self.h_posemb = self.image_size[0] // self.patch_size[0] | |
| self.w_posemb = self.image_size[1] // self.patch_size[1] | |
| self.temb = nn.Parameter(torch.zeros(1, self.temporal_t, dim_tokens)) | |
| self.semb = nn.Parameter(torch.zeros(1, self.h_posemb * self.w_posemb, dim_tokens)) | |
| nn.init.trunc_normal_(self.temb, std=0.02) | |
| nn.init.trunc_normal_(self.semb, std=0.02) | |
| self.mod_emb = nn.Parameter(torch.zeros(1, 1, self.dim_tokens)) | |
| nn.init.normal_(self.mod_emb, std=init_std) | |
| # Video -> tokens projection | |
| # No bias term here, so modality embedding fully comes from self.mod_emb | |
| # self.proj = nn.Linear(self.num_channels * self.patch_size[0] * self.patch_size[1], self.dim_tokens, bias=False) | |
| self.proj = PatchEmbed( | |
| img_size=self.image_size[0], | |
| patch_size=self.patch_size[0], | |
| in_chans=self.num_channels, | |
| embed_dim=self.dim_tokens, | |
| num_frames=self.num_frames, | |
| tubelet_size=self.tube_size) | |
| def no_weight_decay(self): | |
| return set() | |
| def forward(self, d: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]: | |
| """ | |
| Forward pass through embedding module, transforming image to sequence of tokens. | |
| Creates corresponding modality and positional embeddings and adds them to the dict. | |
| Args: | |
| d (Dict[str, torch.Tensor]): Modality dict with at least the following key: | |
| - 'tensor' (torch.Tensor): Input image for each batch. Shape (B, C, H, W) where B is the batch size, C is the number of channels, and H, W are height and width of the image. | |
| Returns: | |
| Dict[str, torch.Tensor]: Modality dict with added keys: | |
| - 'x' (torch.Tensor): Embedded token sequence. Shape (B, (H / PH) * (W / PW), D), where PH and PW are the patch sizes | |
| - 'emb' (torch.Tensor): Sum of positional and modality embeddings for the input sequence. Shape (B, (H / PH) * (W / PW), D) | |
| """ | |
| x = d['tensor'] | |
| B, C, T, H, W = x.shape | |
| assert self.dim_tokens is not None, 'Need to call init(dim_tokens) function first' | |
| assert (H % self.patch_size[0] == 0) and (W % self.patch_size[ | |
| 1] == 0), f'Image sizes {H}x{W} must be divisible by patch sizes {self.patch_size[0]}x{self.patch_size[1]}' | |
| # Create patches [B, T, C, H, W] -> [B, T, HW, C] | |
| x_patch = self.proj(x) | |
| # Here add both positional and temporal embeddings to the model please... | |
| # Create positional embedding + modality embedding | |
| if self.sincos_pos_emb: | |
| x_emb = self.pos_emb | |
| else: | |
| spatial_expanded = self.semb.repeat(1, self.temporal_t, 1) # (1, T*H*W, D) | |
| temporal_expanded = self.temb.repeat_interleave(self.h_posemb * self.w_posemb, dim=1) | |
| x_emb = spatial_expanded + temporal_expanded | |
| x_emb = repeat(x_emb + self.mod_emb, '() n d -> b n d', b=B) | |
| d['x'] = x_patch | |
| d['emb'] = x_emb | |
| return d | |
| class ImageEncoderEmbedding(nn.Module): | |
| """Embedding module for spatial inputs, like images or feature maps. | |
| Creates tokens from patches over the image. | |
| This adapter / embedding differs from the one of MultiMAE by taking as input a dict and | |
| separating positional embeddings and modality embeddings from the input projection | |
| Input projection is 'x', posemb + modemb is 'emb' | |
| Args: | |
| num_channels: Number of input channels of the image/feature map | |
| patch_size: Int or tuple of the patch size over the full image size. | |
| dim_tokens: Dimension of output tokens. Can be set using init method. | |
| sincos_pos_emb: Set to True (default) to use fixed 2D sin-cos positional embeddings | |
| image_size: Default image size. Used to initialize size of positional embeddings. | |
| """ | |
| def __init__(self, | |
| num_channels: int, | |
| patch_size: Union[int, Tuple[int, int]], | |
| dim_tokens: Optional[int] = None, | |
| sincos_pos_emb: bool = True, | |
| image_size: Union[int, Tuple[int]] = 224): | |
| super().__init__() | |
| self.num_channels = num_channels | |
| self.patch_size = pair(patch_size) | |
| self.dim_tokens = dim_tokens | |
| self.sincos_pos_emb = sincos_pos_emb | |
| self.image_size = pair(image_size) | |
| self.num_patches = (self.image_size[0] // patch_size) * (self.image_size[1] // patch_size) | |
| if self.dim_tokens is not None: | |
| self.init(dim_tokens=dim_tokens) | |
| def init(self, dim_tokens: int = 768, init_std=0.02): | |
| """ | |
| Initialize parts of encoder that are dependent on dimension of tokens. | |
| Should be called when setting up FourM. | |
| Args: | |
| dim_tokens: Dimension of tokens | |
| init_std: Standard deviation of init | |
| """ | |
| self.dim_tokens = dim_tokens | |
| # Task embedding identifying from which task a given token comes from | |
| # Fixed-size positional embeddings. Can be interpolated to different input sizes | |
| h_posemb = self.image_size[0] // self.patch_size[0] | |
| w_posemb = self.image_size[1] // self.patch_size[1] | |
| if self.sincos_pos_emb: | |
| pos_emb = build_2d_sincos_posemb(h=h_posemb, w=w_posemb, embed_dim=self.dim_tokens) | |
| self.register_buffer("pos_emb", pos_emb) # self.pos_emb is now a buffer for FSDP | |
| else: | |
| self.pos_emb = nn.Parameter(torch.zeros(1, (h_posemb * w_posemb), self.dim_tokens)) | |
| nn.init.normal_(self.pos_emb, std=init_std) | |
| self.mod_emb = nn.Parameter(torch.zeros(1, 1, self.dim_tokens)) | |
| nn.init.normal_(self.mod_emb, std=init_std) | |
| # Image -> tokens projection | |
| # No bias term here, so modality embedding fully comes from self.mod_emb | |
| self.proj = nn.Linear(self.num_channels * self.patch_size[0] * self.patch_size[1], self.dim_tokens, bias=False) | |
| def no_weight_decay(self): | |
| return set() | |
| def forward(self, d: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]: | |
| """ | |
| Forward pass through embedding module, transforming image to sequence of tokens. | |
| Creates corresponding modality and positional embeddings and adds them to the dict. | |
| Args: | |
| d (Dict[str, torch.Tensor]): Modality dict with at least the following key: | |
| - 'tensor' (torch.Tensor): Input image for each batch. Shape (B, C, H, W) where B is the batch size, C is the number of channels, and H, W are height and width of the image. | |
| Returns: | |
| Dict[str, torch.Tensor]: Modality dict with added keys: | |
| - 'x' (torch.Tensor): Embedded token sequence. Shape (B, (H / PH) * (W / PW), D), where PH and PW are the patch sizes | |
| - 'emb' (torch.Tensor): Sum of positional and modality embeddings for the input sequence. Shape (B, (H / PH) * (W / PW), D) | |
| """ | |
| x = d['tensor'] | |
| B, C, H, W = x.shape | |
| assert self.dim_tokens is not None, 'Need to call init(dim_tokens) function first' | |
| assert (H % self.patch_size[0] == 0) and (W % self.patch_size[ | |
| 1] == 0), f'Image sizes {H}x{W} must be divisible by patch sizes {self.patch_size[0]}x{self.patch_size[1]}' | |
| # Create patches [B, C, H, W] -> [B, (H*W), C] | |
| x_patch = self.proj( | |
| rearrange(x, 'b d (nh ph) (nw pw) -> b (nh nw) (ph pw d)', ph=self.patch_size[0], pw=self.patch_size[1])) | |
| # Create positional embedding + modality embedding | |
| x_emb = repeat(self.pos_emb + self.mod_emb, '() n d -> b n d', b=B) | |
| d['x'] = x_patch | |
| d['emb'] = x_emb | |
| return d | |
| class SequenceEmbEncoderEmbedding(nn.Module): | |
| """Adapter for sequence emb inputs, like T5-XXL, CLIP text embeddings. | |
| Args: | |
| max_length: Maximum number of tokens in the sequence | |
| dim_tokens: Dimension of output tokens. Can be set using init method. | |
| sincos_pos_emb: Set to True (default) to use fixed 1D sin-cos positional embeddings | |
| padding_idx: Padding index for word embedding | |
| orig_emb_dim: Dimension of original embeddings | |
| bottleneck_dim: Dimension of bottleneck layer | |
| use_bottleneck: Set to True to use bottleneck layer | |
| """ | |
| def __init__(self, | |
| max_length: int, | |
| dim_tokens: Optional[int] = None, | |
| sincos_pos_emb: bool = True, | |
| max_sincos_pos_emb: int = 512, | |
| padding_idx: int = 0, | |
| orig_emb_dim: int = 3584, | |
| bottleneck_dim: int = 64, | |
| use_bottleneck: bool = False, | |
| ): | |
| super().__init__() | |
| self.max_length = max_length | |
| self.dim_tokens = dim_tokens | |
| self.sincos_pos_emb = sincos_pos_emb | |
| self.padding_idx = padding_idx | |
| self.max_sincos_pos_emb = max_sincos_pos_emb | |
| self.orig_emb_dim = orig_emb_dim | |
| self.use_bottleneck = use_bottleneck | |
| if self.use_bottleneck: | |
| self.bottleneck_dim = bottleneck_dim | |
| if self.dim_tokens is not None: | |
| self.init(dim_tokens=dim_tokens) | |
| def init(self, dim_tokens: int = 768, init_std=0.02): | |
| """ | |
| Initialize parts of embedding module that are dependent on dimension of tokens. | |
| Should be called when setting up FourM. | |
| Args: | |
| dim_tokens: Dimension of tokens | |
| init_std: Standard deviation of init | |
| """ | |
| self.dim_tokens = dim_tokens | |
| # Task embedding identifying from which task a given token comes from | |
| # Fixed-size positional embeddings. Can be interpolated to different input sizes | |
| if self.sincos_pos_emb: | |
| if self.max_length > self.max_sincos_pos_emb: | |
| raise ValueError( | |
| f"Max length ({self.max_length}) is greater than the number of posembs ({self.max_sincos_pos_emb}") | |
| pos_emb = build_1d_sincos_posemb(max_len=self.max_sincos_pos_emb, embed_dim=self.dim_tokens)[ | |
| :self.max_length] | |
| self.register_buffer("pos_emb", pos_emb) # self.pos_emb is now a buffer for FSDP | |
| else: | |
| self.pos_emb = nn.Parameter(torch.zeros(1, self.max_length, self.dim_tokens)) | |
| nn.init.normal_(self.pos_emb, std=init_std) | |
| self.mod_emb = nn.Parameter(torch.zeros(1, 1, self.dim_tokens)) | |
| nn.init.normal_(self.mod_emb, std=init_std) | |
| # Token embedding projection | |
| if self.use_bottleneck: | |
| self.emb_proj = nn.Sequential( | |
| nn.Linear(self.orig_emb_dim, self.bottleneck_dim), | |
| nn.Linear(self.bottleneck_dim, self.dim_tokens), | |
| ) | |
| else: | |
| self.emb_proj = nn.Linear(self.orig_emb_dim, self.dim_tokens) | |
| def no_weight_decay(self): | |
| return set() | |
| def forward(self, d): | |
| """ | |
| Forward pass through embedding module, projecting original embeddings to the Transformer dimension. | |
| Creates corresponding modality and positional embeddings and adds them to the dict. | |
| Args: | |
| d (Dict[str, torch.Tensor]): Modality dict with at least the following keys: | |
| - 'tensor' (torch.Tensor): Input token sequence for each batch. Shape (B, L, E) where B is the batch size and L is the sequence length, and E is the dimension of the original embeddings. | |
| - 'input_mask' (torch.Tensor): Mask for valid tokens in the input sequence (set to 0 for valid tokens and 1 otherwise). Shape (B, L). | |
| Returns: | |
| Dict[str, torch.Tensor]: Modality dict with added keys: | |
| - 'x' (torch.Tensor): Embedded token sequence. Shape (B, L, D) where D is the Transformer embedding dimension. | |
| - 'emb' (torch.Tensor): Sum of positional and modality embeddings for the input sequence. Shape (B, L, D). | |
| """ | |
| orig_emb = d['tensor'] | |
| B = orig_emb.shape[0] | |
| assert self.dim_tokens is not None, 'Need to call init(dim_tokens) function first' | |
| # Map to embedding | |
| x = self.emb_proj(orig_emb) | |
| expanded_pos_emb = repeat(self.pos_emb, "() n d -> b n d", b=B) | |
| # Input pos encoding | |
| input_mask = d['input_mask'] | |
| input_pos_id = (~input_mask).int().cumsum(dim=1) - 1 | |
| input_pos_id[input_mask] = 0 | |
| input_pos_emb = torch.gather(expanded_pos_emb, dim=1, | |
| index=repeat(input_pos_id, "b n -> b n d", d=expanded_pos_emb.shape[2])) | |
| input_pos_emb[input_mask] = 0 | |
| x_emb = input_pos_emb + self.mod_emb | |
| d['x'] = x | |
| d['emb'] = x_emb | |
| return d | |