zuna / transformer.py
mhough's picture
Self-contained HF-compatible ZUNA (vendored arch, byte-identical weights)
c618186 verified
Raw
History Blame Contribute Delete
48.1 kB
from dataclasses import dataclass, field
from math import inf
from typing import Optional, Tuple, Union, List
from copy import deepcopy
import torch
from torch import nn
from torch.nn import Parameter
from torch.nn import functional as F
from torch.nn.attention.flex_attention import (
create_block_mask,
BlockMask,
_mask_mod_signature,
noop_mask,
)
torch._dynamo.config.capture_scalar_outputs = True
from .lingua_transformer import (
RMSNorm,
InitStdFactor,
RotaryEmbedding,
TransformerBlock,
)
from .xattn import DecoderBlock, FourierConditioner, DecoderArgs, AdaRMSNorm
from .conv_stem import CausalConv2DStem
from .bottlenecks import mmd_imq
from vector_quantize_pytorch import SimVQ, FSQ
import functools
# def create_causal_mask(seqlen, attn_impl, sliding_window):
# if attn_impl == "sdpa":
# return "causal"
# elif attn_impl == "flex_attention":
# return create_block_mask(causal_mask, None, None, seqlen, seqlen)
# else:
# raise NotImplementedError(
# f"Attention {attn_impl} with {sliding_window} sliding window not implemented"
# )
def create_document_mask(lengths: torch.Tensor,
kv_lengths: Optional[torch.Tensor] = None, # for cross-attn
base_mask_mod: Optional[_mask_mod_signature] = None):
"""
Create a document mask. Grabbing code from lingua.transformer
"""
def generate_doc_mask_mod(
mask_mod: _mask_mod_signature,
lengths: torch.Tensor,
kv_lengths: Optional[torch.Tensor] = None, # for cross-attn
) -> _mask_mod_signature:
"""Generates mask mods that apply to inputs to flex attention in the sequence stacked
format.
Args:
mask_mod: The mask mod to apply to the documents
lengths: Lengths of each document
Note:
What is the sequence stacked format? When assembling batches of inputs, we
take multiple sequences and stack them together to form 1 large sequence. We then
use masking to ensure that the attention scores are only applied to tokens within
the same document.
Example:
- Square mask
doc_mask lengths
a a b b b c c 2 3 2
a 1 0 0 0 0 0 0
a 1 1 0 0 0 0 0
b 0 0 1 0 0 0 0
b 0 0 1 1 0 0 0
b 0 0 1 1 1 0 0
c 0 0 0 0 0 1 0
c 0 0 0 0 0 1 1
"""
def lengths_to_start_ids(lengths):
doc_start = lengths.cumsum(0)
doc_start = doc_start.roll(1)
doc_start[0] = 0
return doc_start
def lengths_to_local_ids(lengths):
assert lengths.ndim == 1
nb_seqs = lengths.size(0)
total_seqlen = lengths.sum()
# This gives the document id of each token
doc_id = torch.repeat_interleave(lengths)
# Compute document start for each document
doc_start = lengths_to_start_ids(lengths)
# Compute document start for each token
doc_start = doc_start[doc_id]
# Compute the position of each token within each document
tok_id = torch.arange(total_seqlen, device=lengths.device) - doc_start
return doc_id, tok_id
kv_lengths = kv_lengths if kv_lengths is not None else lengths
q_document_id, q_token_id = lengths_to_local_ids(lengths)
kv_document_id, kv_token_id = lengths_to_local_ids(kv_lengths)
q_max_idx = lengths.sum() - 1
kv_max_idx = kv_lengths.sum() - 1
def doc_mask_mod(b, h, q_idx, kv_idx):
q_idx_cap = torch.minimum(q_max_idx, q_idx)
kv_idx_cap = torch.minimum(kv_max_idx, kv_idx)
valid_idx = (q_idx <= q_max_idx) & (kv_idx <= kv_max_idx)
same_doc = q_document_id[q_idx_cap] == kv_document_id[kv_idx_cap]
q_logical = q_token_id[q_idx_cap]
kv_logical = kv_token_id[kv_idx_cap]
inner_mask = mask_mod(b, h, q_logical, kv_logical)
return same_doc & inner_mask & valid_idx
return doc_mask_mod
if base_mask_mod is None:
base_mask_mod = noop_mask
if torch.cuda.is_available():
doc_mask_mod = generate_doc_mask_mod(base_mask_mod, lengths)
return create_block_mask(doc_mask_mod, None, None, lengths.sum().item(), lengths.sum().item())
else:
# create_block_mask runs on CPU; ensure closure tensors are on CPU too
doc_mask_mod = generate_doc_mask_mod(base_mask_mod, lengths.cpu())
return create_block_mask(doc_mask_mod, None, None, lengths.sum().item(), lengths.sum().item(),
device='cpu', _compile=False)
def attention_flops_per_token(n_layers, seq_len, dim, causal):
# Formula from https://github.com/Dao-AILab/flash-attention/blob/main/benchmarks/benchmark_flash_attention.py#L27-L30
return 3.5 * (4 * n_layers * seq_len * dim // (2 if causal else 1))
def get_num_flop_per_token(
num_non_embed_params: int, n_layers: int, dim: int, seq_len: int
) -> int:
return 6 * num_non_embed_params + attention_flops_per_token(
n_layers, seq_len, dim, True
)
def causal_mask(b, h, q_idx, kv_idx):
return q_idx >= kv_idx
def extract_non_registers(h: torch.Tensor, num_groups: int, original_seqlen: int = None, downsample_factor: int = None) -> torch.Tensor:
"""
Extract non-register tokens from the output tensor.
Args:
h: Output tensor from transformer layers [B, interleaved_seqlen, D].
num_groups: Number of groups used in interleaving.
original_seqlen: The sequence length of the input *before* padding
and interleaving. Used to trim non-register tokens.
Returns:
non_registers: [B, original_seqlen, D]
"""
bsz, seq_len, dim = h.shape
# seq_len should be num_groups*(df+1)
h = h.reshape(bsz, num_groups, downsample_factor + 1, dim)
# Extract non-register tokens (indices 1 to df)
non_registers = h[:, :, 1:, :]
# Flatten back to sequence dimension
padded_seqlen = num_groups * downsample_factor
non_registers = non_registers.reshape(bsz, padded_seqlen, dim)
# Trim back to the original sequence length, removing padding effects
non_registers = non_registers[:, :original_seqlen, :] # [B, original_seqlen, D]
return non_registers.contiguous()
@torch.compile()
def huber_loss(target, logits, huber_c):
return huber_c * (torch.sqrt((target - logits) ** 2 + huber_c**2) - huber_c)
@torch.compile()
def cosine_similarity_loss(input, target):
return (1 - F.cosine_similarity(input, target, dim=-1).mean())
@torch.compile()
def huber_cosine_weighted(input, target, huber_c = 0.1):
# Compute the Huber loss
h_loss = huber_loss(input, target, huber_c).mean() * 0.5
# Compute the cosine similarity
cosine_sim = cosine_similarity_loss(input, target)
# Combine the two losses
combined_loss = h_loss + cosine_sim
return combined_loss
@dataclass
class DecoderTransformerArgs(DecoderArgs):
seed: int = 42
weight_tying: bool = False
sliding_window: int = 128
xattn_sliding_window: int = 32
input_dim: int = 64
decoder_encoder_dropout: float = 0.1
decoder_timestep_dropout: float = 0.1
encoder_sliding_window: int = 128
encoder_input_dim: int = input_dim
encoder_output_dim: int = input_dim*2
encoder_latent_downsample_factor: int = 2
encoder_hidden_dim: Optional[int] = None
adaptive_loss_weighting: bool = False
num_fine_time_pts: int = 128
dont_noise_chan_xyz: bool = False
stft_global_sigma: Union[str, float] = 1.0
dropout_type: str = "zero" # {"zero", "rand", "learnable"}
bottleneck_type: str = "mmd"
distill_output_dim: int = 0
codebook_size: int = 1024
levels: List[int] = field(default_factory=list)
init_base_std: float = 0.02
learnable_bias: bool = False
huber_c: Optional[float] = None
decoder_repa_index: float = float('inf')
encoder_repa_index: float = float('inf')
repa_dim: int = 1024
repa_loss_fn: str = "cosine"
compression_free_conv_stem: bool = False
max_seqlen: int = 1024
class BaseTransformerDecoder(nn.Module):
def __init__(self, args: DecoderTransformerArgs):
super().__init__()
self.dim = args.dim
self.init_base_std = args.init_base_std
self.init_std_factor = InitStdFactor(args.init_std_factor)
self.max_seqlen = args.max_seqlen
self.rope_embeddings = RotaryEmbedding(
theta=args.rope_theta,
head_dim=args.head_dim or args.dim // args.n_heads,
max_seqlen=args.max_seqlen,
rope_dim=args.rope_dim,
)
self.layers = nn.ModuleList()
for _ in range(args.n_layers):
self.layers.append(DecoderBlock(args))
self.repa_index = args.decoder_repa_index
if self.repa_index != inf:
# self.repa_proj = nn.Linear(args.dim, args.repa_dim)
self.repa_proj = nn.Sequential(
nn.Linear(args.dim, args.repa_dim),
nn.SiLU(),
nn.Linear(args.repa_dim, args.repa_dim),
nn.SiLU(),
nn.Linear(args.repa_dim, args.repa_dim),
)
self.repa_norm = AdaRMSNorm(args.t_dim, args.dim, eps=args.norm_eps)
self.repa_loss_fn = cosine_similarity_loss if args.repa_loss_fn == "cosine" else huber_cosine_weighted
def forward(
self,
h,
x_attended,
t,
tok_idx: Optional[torch.Tensor] = None,
cross_tok_idx: Optional[torch.Tensor] = None,
mask: Optional[Union[BlockMask, str]] = None,
cross_attn_mask: Optional[Union[BlockMask, str]] = None,
attn_impl: str = "sdpa",
repa_target: Optional[torch.Tensor] = None,
do_idx: Optional[torch.Tensor] = None,
):
freq_cis = self.rope_embeddings(seqlen=self.max_seqlen, tok_idx=tok_idx)
repa_loss = None
for i, layer in enumerate(self.layers): # all these layers are type 'xattn.DecoderBlock'
h = layer(h,
x_attended,
t,
freq_cis,
tok_idx=tok_idx,
cross_tok_idx=cross_tok_idx,
self_attn_mask=mask,
cross_attn_mask=cross_attn_mask,
attn_impl=attn_impl,
do_idx=do_idx,
)
if self.training and self.repa_index != inf and i == self.repa_index:
repa_loss = self.repa_loss_fn(self.repa_proj(self.repa_norm(h, t)).float(), repa_target,)
return h, repa_loss
def reset_parameters(self):
# Either use fixed base std or sqrt model dim
self.rope_embeddings.reset_parameters()
def init_weights(self):
self.reset_parameters()
for depth, layer in enumerate(self.layers):
factor = {
InitStdFactor.CURRENT_DEPTH: (2 * (depth + 1)) ** 0.5,
InitStdFactor.GLOBAL_DEPTH: (2 * (len(self.layers) + 1)) ** 0.5,
InitStdFactor.DIM_RATIO: self.dim / 4096,
InitStdFactor.DISABLED: 1.0,
}[self.init_std_factor]
layer.init_weights(self.init_base_std, factor)
# Add these lines for repa_proj initialization
if self.repa_index != float('inf'):
init_std = self.init_base_std or (self.dim ** (-0.5))
self.repa_norm.reset_parameters() # Ensure repa_norm is also reset
#now repa_proj is nn.Sequential, let's do it in a loop
for i, layer in enumerate(self.repa_proj):
if isinstance(layer, nn.Linear):
nn.init.trunc_normal_(
layer.weight,
mean=0.0,
std=init_std,
a=-3 * init_std,
b=3 * init_std,
)
# nn.init.zeros_(layer.weight)
if layer.bias is not None:
nn.init.zeros_(layer.bias)
class BaseTransformer(nn.Module):
def __init__(self, args: DecoderTransformerArgs):
super().__init__()
self.dim = args.dim
self.init_base_std = args.init_base_std
self.init_std_factor = InitStdFactor(args.init_std_factor)
self.max_seqlen = args.max_seqlen
self.rope_embeddings = RotaryEmbedding(
theta=args.rope_theta,
head_dim=args.head_dim or args.dim // args.n_heads,
max_seqlen=args.max_seqlen,
rope_dim=args.rope_dim,
)
self.layers = nn.ModuleList()
for _ in range(args.n_layers):
self.layers.append(TransformerBlock(args))
self.repa_index = args.encoder_repa_index
if self.repa_index != inf:
self.repa_proj = nn.Linear(args.dim, args.repa_dim)
self.repa_norm = RMSNorm(args.dim, eps=args.norm_eps)
self.repa_loss_fn = cosine_similarity_loss if args.repa_loss_fn == "cosine" else huber_cosine_weighted
def forward(
self,
h,
tok_idx: Optional[torch.Tensor] = None,
mask: Optional[Union[BlockMask, str]] = None,
attn_impl: str = "sdpa",
repa_target: Optional[torch.Tensor] = None,
do_idx: Optional[torch.Tensor] = None,
**kwargs
):
freq_cis = self.rope_embeddings(seqlen=self.max_seqlen,
tok_idx=tok_idx
)
repa_loss = None
for i, layer in enumerate(self.layers): # all these layers are type 'TransformerBlock'
h = layer(h,
freq_cis,
tok_idx=tok_idx,
mask=mask,
attn_impl=attn_impl,
do_idx=do_idx,
)
if self.training and self.repa_index != inf and i == self.repa_index:
repa_loss = cosine_similarity_loss(self.repa_proj(self.repa_norm(extract_non_registers(h, **kwargs))).float(), repa_target,)
return h, repa_loss
def reset_parameters(self):
# Either use fixed base std or sqrt model dim
self.rope_embeddings.reset_parameters()
def init_weights(self):
self.reset_parameters()
for depth, layer in enumerate(self.layers):
factor = {
InitStdFactor.CURRENT_DEPTH: (2 * (depth + 1)) ** 0.5,
InitStdFactor.GLOBAL_DEPTH: (2 * (len(self.layers) + 1)) ** 0.5,
InitStdFactor.DIM_RATIO: self.dim / 4096,
InitStdFactor.DISABLED: 1.0,
}[self.init_std_factor]
layer.init_weights(self.init_base_std, factor)
# Add these lines for repa_proj initialization
if self.repa_index != float('inf'):
init_std = self.init_base_std or (self.dim ** (-0.5))
nn.init.trunc_normal_(
self.repa_proj.weight,
mean=0.0,
std=init_std,
a=-3 * init_std,
b=3 * init_std,
)
if self.repa_proj.bias is not None:
nn.init.zeros_(self.repa_proj.bias)
self.repa_norm.reset_parameters()
class DecoderTransformer(BaseTransformerDecoder):
def __init__(self, args: DecoderTransformerArgs):
super().__init__(args)
self.weight_tying = False
self.sliding_window = args.sliding_window
self.xattn_sliding_window = args.xattn_sliding_window
if args.huber_c is not None:
self.huber_c = args.huber_c
else:
self.huber_c = None
self.tok_embeddings = nn.Linear(args.input_dim, args.dim,)
self.t_embedder = FourierConditioner(args.t_dim, std=args.init_base_std)
self.encoder_proj = nn.Linear(args.encoder_output_dim, args.dim)
self.norm = AdaRMSNorm(args.t_dim, args.dim, eps=args.norm_eps)
self.output = nn.Linear(
args.dim,
args.input_dim,
bias=False,
)
self.init_base_std = args.init_base_std
self.use_compression_free_conv_stem = False
if args.compression_free_conv_stem:
self.use_compression_free_conv_stem = True
self.compression_free_conv_stem_input = CausalConv2DStem(
input_features = args.input_dim,
hidden_channels = 32,
activation = nn.SELU,
compress_channels=False,
)
self.compression_free_conv_stem_output = CausalConv2DStem(
input_features = args.input_dim,
hidden_channels = 32,
activation = nn.SELU,
compress_channels=False,
)
self.adaptive_loss_weighting = args.adaptive_loss_weighting
def forward(
self,
tokens: torch.Tensor,
cross_attended: torch.Tensor,
timeD: torch.Tensor,
seq_lens: torch.Tensor, # for document masking packed sequences in self-attention
cross_seq_lens: torch.Tensor, # for document masking packed sequences in cross-attention
target: Optional[torch.Tensor] = None,
tok_idx: Optional[torch.Tensor] = None,
cross_tok_idx: Optional[torch.Tensor] = None,
mask: Optional[Union[BlockMask, torch.Tensor, str]] = None,
cross_attn_mask: Optional[Union[BlockMask, str]] = None,
attn_impl: str = "flex_attention",
time_masks: Optional[torch.Tensor] = None,
channel_loss_weighting: Optional[torch.Tensor] = None, # [1, 1, input_dim*2]
repa_target: Optional[torch.Tensor] = None,
freq_masks: Optional[torch.Tensor] = None,
do_idx: Optional[torch.Tensor] = None,
print_layerwise_activation_stats: bool = False,
):
tokens = tokens.squeeze(1)
bsz, seqlen, dim = tokens.shape
_, cross_seqlen, _ = cross_attended.shape
# Masking out channels that were set to all-zeros
if self.training and freq_masks is not None:
with torch.no_grad():
tokens *= freq_masks
if self.use_compression_free_conv_stem:
tokens = self.compression_free_conv_stem_input(tokens)
h = self.tok_embeddings(tokens)
t = self.t_embedder(timeD)
cross_attended = self.encoder_proj(cross_attended)
# COMBINE SLIDING WINDOW MASK AND DOCUMENT MASK
SLIDING_WINDOW = self.sliding_window
def selfattn_sliding_window_func(b, h, q_idx, kv_idx):
# Self-attention case
return (q_idx - kv_idx).abs() <= SLIDING_WINDOW
mask_mod_slide = selfattn_sliding_window_func
mask = create_document_mask(lengths=seq_lens, base_mask_mod=mask_mod_slide)
#
SLIDING_WINDOW = self.xattn_sliding_window
def crossattn_sliding_window_func(b, h, q_idx, kv_idx):
# Cross-attention case
center_k_idx = (q_idx * cross_seqlen) // seqlen
return (kv_idx - center_k_idx).abs() <= SLIDING_WINDOW
mask_mod_slide = crossattn_sliding_window_func
cross_attn_mask = create_document_mask(lengths=seq_lens, kv_lengths=cross_seq_lens, base_mask_mod=mask_mod_slide)
visualize_attention_masks = False
if visualize_attention_masks:
from .utils import visualize_attention_mask
torch._dynamo.config.disable = True
visualize_attention_mask(mask, title_suffix="decoder_self_attn")
visualize_attention_mask(cross_attn_mask, title_suffix="decoder_cross_attn")
torch._dynamo.config.disable = False
if tok_idx is not None:
if tok_idx.ndim==3 and tok_idx.shape[0]==1:
tok_idx = tok_idx.squeeze().squeeze() # make it the right size for RoPE.
if cross_tok_idx is not None:
if cross_tok_idx.ndim==3 and cross_tok_idx.shape[0]==1:
cross_tok_idx = cross_tok_idx.squeeze().squeeze() # make it the right size for RoPE.
h, repa_loss = super().forward(h,
cross_attended,
t=t,
tok_idx=tok_idx,
cross_tok_idx=cross_tok_idx,
mask=mask,
cross_attn_mask=cross_attn_mask,
attn_impl=attn_impl,
repa_target=repa_target,
do_idx=do_idx,
)
h_normed = self.norm(h, t)
# if print_layerwise_activation_stats and do_idx is not None:
# print(f"\nDecoder output norm: (drop-out) mean={h[:, do_idx, :].mean().item():.6f}, std={h[:, do_idx, :].std().item():.6f}", end=" --> ")
# print(f"mean={h_normed[:, do_idx, :].mean().item():.6f}, std={h_normed[:, do_idx, :].std().item():.6f}")
# print(f"Decoder output norm: (non-drop) mean={h[:, ~do_idx, :].mean().item():.6f}, std={h[:, ~do_idx, :].std().item():.6f}", end=" --> ")
# print(f"mean={h_normed[:, ~do_idx, :].mean().item():.6f}, std={h_normed[:, ~do_idx, :].std().item():.6f}")
logits = self.output(h_normed)
if self.use_compression_free_conv_stem:
logits = self.compression_free_conv_stem_output(logits)
losses = self.compute_losses(target, logits, time_masks, freq_masks, channel_loss_weighting)
if repa_target is not None:
losses["decoder_repa_loss"] = repa_loss#.mean()
return logits, losses
@torch.compile()
def compute_losses(self, target, logits, time_masks, freq_masks, channel_loss_weighting):
losses = {}
if target is not None:
if self.huber_c is None:
batchwise_loss = F.mse_loss(target.float(), logits.float(), reduction="none") # shape = [B, T, C]
else:
batchwise_loss = huber_loss(target.float(), logits.float(), self.huber_c)
# Do Adaptive Loss Weighting - to boost loss from channels with small signals so we can better learn small signals.
if self.adaptive_loss_weighting:
ALW = batchwise_loss.detach().abs().mean(dim=2).unsqueeze(2) # shape = [B,C,1]
batchwise_loss = batchwise_loss/(ALW + 1e-5)
if channel_loss_weighting is not None:
batchwise_loss = batchwise_loss * channel_loss_weighting
if freq_masks is not None:
batchwise_loss = (batchwise_loss * freq_masks).sum(dim=1) / (freq_masks.sum(dim=1) + 1e-6) # shape = [B,C,1]
else:
batchwise_loss = batchwise_loss.mean(dim=-1)
if time_masks is not None:
batchwise_loss = (batchwise_loss * time_masks).sum(dim=-1) / time_masks.sum(dim=-1)
losses["decoder_rf_loss"] = batchwise_loss.mean()
return losses
def reset_parameters(self, init_std=None):
# Either use fixed base std or sqrt model dim
super().reset_parameters()
if init_std is None:
init_std = self.init_base_std or (self.dim ** (-0.5))
self.norm.reset_parameters()
self.t_embedder.reset_parameters(init_std)
nn.init.trunc_normal_(
self.tok_embeddings.weight,
mean=0.0,
std=init_std,
a=-3 * init_std,
b=3 * init_std,
)
nn.init.trunc_normal_(
self.encoder_proj.weight,
mean=0.0,
std=init_std,
a=-3 * init_std,
b=3 * init_std,
)
if self.encoder_proj.bias is not None:
nn.init.zeros_(self.encoder_proj.bias)
nn.init.trunc_normal_(
self.output.weight,
mean=0.0,
std=init_std,
a=-3 * init_std,
b=3 * init_std,
)
if self.use_compression_free_conv_stem:
self.compression_free_conv_stem_input.reset_parameters(init_std)
self.compression_free_conv_stem_output.reset_parameters(init_std)
def init_weights(self):
super().init_weights()
self.reset_parameters()
class EncoderTransformer(BaseTransformer):
def __init__(self, args: DecoderTransformerArgs):
args = deepcopy(args)
args.dim = args.dim if args.encoder_hidden_dim is None else args.encoder_hidden_dim
super().__init__(args)
self.weight_tying = False
self.sliding_window = args.encoder_sliding_window
self.bottleneck_type = args.bottleneck_type
self.downsample_factor = args.encoder_latent_downsample_factor
self.distill = args.distill_output_dim != 0
self.tok_embeddings = nn.Linear(args.encoder_input_dim, args.dim)
self.norm = RMSNorm(args.dim, eps=args.norm_eps)
self.registers = torch.nn.Parameter(torch.zeros(1, args.encoder_input_dim))
self.dropout_type = args.dropout_type
if self.dropout_type=="learnable":
self.dropout_vec = torch.nn.Parameter(args.stft_global_sigma*torch.rand(1, args.encoder_input_dim, dtype=torch.float32)) # rand init for learnable dropout vector
else:
self.dropout_vec = None # If None, it will just use zeros for dropped out chans (rather than learnable vector).
self.init_base_std = args.init_base_std
self.output = nn.Linear(args.dim, args.encoder_output_dim, bias=False)
if args.distill_output_dim != 0:
self.distill_output = nn.Linear(
args.dim,
args.distill_output_dim,
bias=True,
)
self.distill_norm = RMSNorm(args.dim, eps=args.norm_eps)
if "sim" in args.bottleneck_type:
self.quantizer = SimVQ(
dim = args.encoder_output_dim,
codebook_size = args.codebook_size,
rotation_trick = True # use rotation trick from Fifty et al.
)
elif "fsq" in args.bottleneck_type:
self.quantizer = FSQ(
levels = args.levels
)
self.use_compression_free_conv_stem = False
if args.compression_free_conv_stem:
self.use_compression_free_conv_stem = True
self.compression_free_conv_stem_input = CausalConv2DStem(
input_features = args.input_dim,
hidden_channels = 32,
activation = nn.SELU,
compress_channels=False,
)
def _interleave_registers(self, x: torch.Tensor):
"""
1) Pad `x` along the sequence dimension so it’s divisible by `self.downsample_factor`.
2) Reshape into groups of length `df`.
3) Insert a copy of `self.registers` in front of each group.
4) Flatten back out.
Returns:
interleaved: [B, num_groups*(df+1), D]
num_groups: int
"""
bsz, seqlen, dim = x.shape
df = self.downsample_factor
# Number of groups
num_groups = (seqlen + df - 1) // df
new_seqlen = num_groups * df
# Pad if needed
if new_seqlen > seqlen:
pad_len = new_seqlen - seqlen
x = torch.cat([x, x.new_zeros(bsz, pad_len, dim)], dim=1)
# Reshape to [B, num_groups, df, D]
x = x.reshape(bsz, num_groups, df, dim)
# Expand the single register => [B, num_groups, 1, D]
regs = self.registers.expand(bsz, num_groups, -1).unsqueeze(2)
# Cat the register in front of each group => [B, num_groups, df+1, D]
x = torch.cat([regs, x], dim=2)
# Flatten => [B, num_groups*(df+1), D]
x = x.reshape(bsz, -1, dim).contiguous()
return x, num_groups
def _extract_registers_and_non_registers(
self,
h: torch.Tensor,
num_groups: int,
original_seqlen: int = None, # Added: original sequence length before padding
return_non_registers: bool = False # Added: flag to return other tokens
) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]: # Updated return type hint
"""
Args:
h: Output tensor from transformer layers [B, interleaved_seqlen, D].
num_groups: Number of groups used in interleaving.
original_seqlen: The sequence length of the input *before* padding
and interleaving. Used to trim non-register tokens.
return_non_registers: If True, return both register and non-register
tokens. Otherwise, return only register tokens.
Returns:
If return_non_registers is False:
registers: [B, num_groups, D]
If return_non_registers is True:
registers: [B, num_groups, D]
non_registers: [B, original_seqlen, D]
"""
bsz, seq_len, dim = h.shape
df = self.downsample_factor
# seq_len should be num_groups*(df+1)
h = h.reshape(bsz, num_groups, df + 1, dim)
# The register is the first token in each group
registers = h[:, :, 0, :] # [B, num_groups, D]
if not return_non_registers:
return registers.contiguous(), None
else:
# Extract non-register tokens (indices 1 to df)
non_registers = h[:, :, 1:, :] # [B, num_groups, df, D]
# Flatten back to sequence dimension
padded_seqlen = num_groups * df
non_registers = non_registers.reshape(bsz, padded_seqlen, dim)
# Trim back to the original sequence length, removing padding effects
non_registers = non_registers[:, :original_seqlen, :] # [B, original_seqlen, D]
return registers.contiguous(), non_registers.contiguous()
def forward(
self,
token_values: torch.Tensor,
seq_lens: torch.Tensor,
distill_target: Optional[torch.Tensor] = None,
tok_idx: Optional[torch.Tensor] = None,
mask: Optional[Union[BlockMask, torch.Tensor, str]] = None,
attn_impl: str = "flex_attention",
repa_target: Optional[torch.Tensor] = None,
do_idx: Optional[torch.Tensor] = None,
print_layerwise_activation_stats: bool = False,
):
_, orig_seqlen, _ = token_values.shape
if self.use_compression_free_conv_stem:
token_values = self.compression_free_conv_stem_input(token_values)
token_values, num_groups = self._interleave_registers(token_values)
bsz, seqlen, _ = token_values.shape
if do_idx is not None: # (CW)
do_idx_pre_reg = do_idx # indices of dropped-out channels without registers interleaved
do_idx = (token_values.sum(axis=2)==0).squeeze(0) # recompute do_idx after interleaving registers
# Now if using Learable Dropout, replace dropped-out channels with a learned but fixed parameter vector.
if self.dropout_vec is not None:
token_values[:,do_idx,:] = self.dropout_vec
h = self.tok_embeddings(token_values)
# (CW) - COMBINE SLIDING WINDOW MASK AND DOCUMENT MASK
SLIDING_WINDOW = self.sliding_window
def sliding_window_func(b, h, q_idx, kv_idx):
# Self-attention case
return (q_idx - kv_idx).abs() <= SLIDING_WINDOW
mask_mod_slide = sliding_window_func
mask = create_document_mask(seq_lens*2, base_mask_mod=mask_mod_slide) # Hardcoding for CR=1 with interleave_registers thing.
visualize_attention_masks = False
if visualize_attention_masks:
from .utils import visualize_attention_mask
torch._dynamo.config.disable = True
visualize_attention_mask(mask, title_suffix="encoder")
torch._dynamo.config.disable = False
if tok_idx is not None:
tok_idx = tok_idx.repeat_interleave(repeats=2,dim=1)
tok_idx = tok_idx.squeeze().squeeze() # make it the right size for RoPE.
# if print_layerwise_activation_stats and do_idx is not None: # (CW)
# print(f"{do_idx.sum()=} and {(~do_idx).sum()=}")
# print(f"{token_values.shape=}")
h, repa_loss = super().forward(h, # BaseTransformer.forward
tok_idx=tok_idx,
mask=mask,
attn_impl=attn_impl,
repa_target=repa_target,
num_groups=num_groups,
original_seqlen=orig_seqlen,
downsample_factor=self.downsample_factor,
do_idx=do_idx,
)
h, non_regs = self._extract_registers_and_non_registers(h, num_groups, original_seqlen=orig_seqlen, return_non_registers=distill_target is not None)
# if print_layerwise_activation_stats and do_idx is not None: # (CW)
# h_normed = self.norm(h) # (CW)
# print(f"\nEncoder output norm (drop-out): mean={h[:, do_idx_pre_reg, :].mean().item():.6f}, std={h[:, do_idx_pre_reg, :].std().item():.6f}", end=" --> ") # (CW)
# print(f"mean={h_normed[:, do_idx_pre_reg, :].mean().item():.6f}, std={h_normed[:, do_idx_pre_reg, :].std().item():.6f}") # (CW)
# print(f"Encoder output norm (non-drop): mean={h[:, ~do_idx_pre_reg, :].mean().item():.6f}, std={h[:, ~do_idx_pre_reg, :].std().item():.6f}", end=" --> ") # (CW)
# print(f"mean={h_normed[:, ~do_idx_pre_reg, :].mean().item():.6f}, std={h_normed[:, ~do_idx_pre_reg, :].std().item():.6f}") # (CW)
# logits = self.output(h_normed) # (CW)
logits = self.output(self.norm(h))
logits, losses = self.bottleneck(logits)
if distill_target is not None:
losses['encoder_distill'] = (1 - F.cosine_similarity(self.distill_output(self.distill_norm(non_regs)), distill_target, dim=-1).mean()) * 0.1
if repa_target is not None:
losses["encoder_repa_loss"] = repa_loss#.mean()
return logits, losses
def bottleneck(self, h,):
losses = {}
latent = h
b, l, d = h.shape
if "kl" in self.bottleneck_type:
mean, logvar = h.chunk(2, dim=-1)
logvar = logvar.clamp(min=-3)
std = torch.exp(0.5 * logvar)
latent = mean + std * torch.randn_like(mean)
kl_div_loss = -0.5 * (1 + logvar - mean.pow(2) - logvar.exp())
losses["kl"] = kl_div_loss.mean()
if "mmd" in self.bottleneck_type and self.training:
losses["mmd"] = mmd_imq(latent.view(b*l, d).float(), torch.randn((b*l,d), dtype=torch.float32, device=latent.device,), 10.0)
if "sim" in self.bottleneck_type:
latent, codes, simvq_loss = self.quantizer(h)
losses["simvq_commit_loss"] = simvq_loss
if "fsq" in self.bottleneck_type:
latent, codes = self.quantizer(h)
return latent, losses
def reset_parameters(self, init_std=None):
# Either use fixed base std or sqrt model dim
super().reset_parameters()
if init_std is None:
init_std = self.init_base_std or (self.dim ** (-0.5))
self.norm.reset_parameters()
nn.init.trunc_normal_(
self.tok_embeddings.weight,
mean=0.0,
std=init_std,
a=-3 * init_std,
b=3 * init_std,
)
nn.init.trunc_normal_(
self.output.weight,
mean=0.0,
std=init_std,
a=-3 * init_std,
b=3 * init_std,
)
if self.distill:
self.distill_norm.reset_parameters()
nn.init.trunc_normal_(
self.distill_output.weight,
mean=0.0,
std=init_std,
a=-3 * init_std,
b=3 * init_std,
)
nn.init.zeros_(self.distill_output.bias)
nn.init.trunc_normal_(
self.registers,
mean=0.0,
std=init_std,
a=-3 * init_std,
b=3 * init_std,
)
if self.use_compression_free_conv_stem:
self.compression_free_conv_stem_input.reset_parameters(init_std)
def init_weights(self):
super().init_weights()
self.reset_parameters()
class EncoderDecoder(nn.Module):
def __init__(self, args: DecoderTransformerArgs):
super().__init__()
self.encoder = EncoderTransformer(args)
self.decoder = DecoderTransformer(args)
self.input_output_dim = args.input_dim
self.encoder_dropout = args.decoder_encoder_dropout
self.decoder_timestep_dropout = args.decoder_timestep_dropout
self.global_sigma = args.stft_global_sigma
self.num_fine_time_pts = args.num_fine_time_pts
self.dont_noise_chan_xyz = args.dont_noise_chan_xyz
self.rope_dim = args.rope_dim
self.tok_idx_type = args.tok_idx_type
def forward(
self,
encoder_input: torch.Tensor,
decoder_input: torch.Tensor,
t: torch.Tensor,
chan_pos: torch.Tensor,
chan_pos_discrete: torch.Tensor,
chan_id: torch.Tensor,
t_coarse: torch.Tensor,
seq_lens: torch.Tensor,
target: Optional[torch.Tensor] = None,
distill_target: Optional[torch.Tensor] = None,
time_masks: Optional[torch.Tensor] = None,
channel_loss_weighting: Optional[torch.Tensor] = None, # [1, 1, input_dim*2]
encoder_repa_target: Optional[torch.Tensor] = None,
decoder_repa_target: Optional[torch.Tensor] = None,
freq_masks: Optional[torch.Tensor] = None, # 0s where to not compute loss, 1s where to compute loss [B, 1, C]
):
if encoder_input.ndim==2:
encoder_input = encoder_input.unsqueeze(0)
target = target.unsqueeze(0) # doing to get rid of broadcast warning from DecoderTransformer.compute_losses
chan_pos = chan_pos.unsqueeze(0)
chan_pos_discrete = chan_pos_discrete.unsqueeze(0)
chan_id = chan_id.unsqueeze(0)
t_coarse = t_coarse.unsqueeze(0)
## Options for tok_idx. Choose 1.
if self.tok_idx_type is None:
tok_idx = None # this will just use args.model.max_seqlen to construct 1D-RoPE (but requires max_seqlen way too long).
elif self.tok_idx_type == "t_coarse" and self.rope_dim==1:
tok_idx = t_coarse # this ignores channel and just uses coarse time in 1D-RoPE
elif self.tok_idx_type == "chan_id" and self.rope_dim==1:
tok_idx = chan_id # this uses channel id in 1D-RoPE # this is same as hstack(arange(seq_lens)) below when seq_len = num_chans, ie chop_signals_only
elif self.tok_idx_type == "stack_arange_seqlen" and self.rope_dim==1:
tok_idx = torch.hstack([torch.arange(sl) for sl in seq_lens]).unsqueeze(0).unsqueeze(-1) # This has a different tok_id value for each element in sequence (chan or tc).
elif self.tok_idx_type == "{x,y,z,tc}" and self.rope_dim==4:
tok_idx = torch.cat((chan_pos_discrete,t_coarse), dim=2)
else:
raise ValueError(f"Dont understand {self.tok_idx_type=} and {self.rope_dim}")
do_idx = (encoder_input.sum(axis=2)==0).squeeze(0) # indices of dropped-out channels (CW)
# do_idx = None # [Set do_idx to None to disable printing of activation stats comparing channel drop-out]
enc_out, enc_losses = self.encoder(encoder_input,
distill_target=distill_target, # (CW) - None
repa_target=encoder_repa_target, # (CW) - None
mask=None,
seq_lens=seq_lens, # (CW) - for document masking
tok_idx=tok_idx, # (CW) - pass in coarse time index for 1D RoPE
do_idx=do_idx, # indices of dropped-out channels (CW)
)
dec_out, dec_losses = self.decoder(tokens=decoder_input,
cross_attended=enc_out,
timeD=t,
target=target,
time_masks=time_masks, # (CW) - None
channel_loss_weighting=channel_loss_weighting, # (CW) - None
repa_target=decoder_repa_target, # (CW) - None
freq_masks = freq_masks, # (CW) - masks out bad (all-zero) channels [B, 1, C]
mask=None,
cross_attn_mask=None,
seq_lens=seq_lens, # (CW) - for document masking in self-attention
cross_seq_lens=seq_lens, # (CW) - for document masking in cross-attention (with CR=1)
tok_idx=tok_idx, # (CW) - pass in coarse time index for 1D RoPE
cross_tok_idx=tok_idx, # (CW) - pass in coarse time index for 1D RoPE (with CR=1)
do_idx=do_idx, #.squeeze(0) if do_idx is not None else None, # indices of dropped-out channels (CW)
)
return dec_out, enc_losses, dec_losses
@torch.no_grad()
def sample(self, encoder_input: torch.Tensor, seq_lens: torch.Tensor, tok_idx: torch.Tensor, sample_steps: int = 50, cfg: float = 1.0):
device = encoder_input.device
dtype = torch.bfloat16 # if device.type == "cuda" else torch.float16 # torch.float32
# CPU Autocast only supports dtypes of torch.bfloat16, torch.float16 currently.
with torch.autocast(device.type, dtype=dtype):
do_idx = (encoder_input.sum(axis=2)==0).squeeze(0) # indices of dropped-out channels (CW)
# do_idx = None # [Set do_idx to None to disable printing of activation stats comparing channel drop-out]
enc_out, _ = self.encoder(
token_values=encoder_input,
seq_lens=seq_lens,
tok_idx=tok_idx,
do_idx=do_idx,
)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
bsz, seqlen, dim = enc_out.shape
dt_time = torch.tensor([1.0 / sample_steps] * bsz, device=enc_out.device).view(-1)
z = self.global_sigma*torch.randn_like(encoder_input).to(enc_out.device) # init to rand
# z = torch.zeros_like(encoder_input).to(enc_out.device) # init to zeros
# Do not noise channel {x,y,z}-position in eeg_signal
if self.dont_noise_chan_xyz:
if dim==131 or dim==35:
z[:,:,:3] = encoder_input[:,:,:3]
else:
pass
# print("NOTE: EEG channel {x,y,z}-position was never concatenated into signal.")
# import IPython; print('\n\nDebug:'); IPython.embed(); import time; time.sleep(0.3)
dt = dt_time.unsqueeze(-1).unsqueeze(-1)
outputs = []
for i in range(sample_steps, 0, -1):
t = dt_time * i
t_model = t.unsqueeze(1).unsqueeze(1)
vc, _ = self.decoder(tokens=z.unsqueeze(1),
cross_attended=enc_out,
timeD=t_model,
seq_lens=seq_lens, # for document masking in self-attention
cross_seq_lens=seq_lens, # for document masking in cross-attention (with CR=1)
tok_idx=tok_idx,
cross_tok_idx=tok_idx,
)
if cfg != 1.0:
vc_uncond, _ = self.decoder(tokens=z.unsqueeze(1),
cross_attended=torch.zeros_like(enc_out),
timeD=t_model,
seq_lens=seq_lens, # for document masking in self-attention
cross_seq_lens=seq_lens, # for document masking in cross-attention (with CR=1)
tok_idx=tok_idx,
cross_tok_idx=tok_idx,
)
vc = vc_uncond + cfg * (vc - vc_uncond) # starts at unconditioned, moves toward conditioned as cfg increases
z = z - dt * vc
# Do not noise channel {x,y,z}-position in eeg_signal
if self.dont_noise_chan_xyz:
if dim==131 or dim==35:
z[:,:,:3] = encoder_input[:,:,:3]
else:
# print("NOTE: EEG channel {x,y,z}-position was never concatenated into signal.")
# import IPython; print('\n\nDebug:'); IPython.embed(); import time; time.sleep(0.3)
pass
outputs.append(z)
return z, outputs
def reset_parameters(self):
self.encoder.reset_parameters()
self.decoder.reset_parameters()
def init_weights(self):
self.encoder.init_weights()
self.decoder.init_weights()
# Optional policy for activation checkpointing. With None, we stick to the default (defined distributed.py: default_no_recompute_ops)
def get_no_recompute_ops():
return None
# Optional and only used for fully shard options (fsdp) is choose. Highly recommanded for large models
def build_fsdp_grouping_plan(model_args: DecoderTransformerArgs) -> List[Tuple[str, bool]]:
group_plan: List[Tuple[str, bool]] = []
# # 1. Encoder Input
group_plan.append(("encoder.output", False)) # <-- Changed to True
group_plan.append(("decoder.output", False)) # Final output for main loss
if model_args.decoder_repa_index != inf:
group_plan.append(("decoder.repa_proj", False))
if model_args.encoder_repa_index != inf:
group_plan.append(("encoder.repa_proj", False))
# 2. Encoder Transformer Blocks
for i in range(model_args.n_layers):
group_plan.append((f"encoder.layers.{i}", False))
# 3. Decoder Transformer Blocks
for i in range(model_args.n_layers):
group_plan.append((f"decoder.layers.{i}", False))
# 4. Add Decoder and Encoder themselves
group_plan.append(("encoder", False))
group_plan.append(("decoder", False))
return group_plan